slkern/drives.c

335 lines
9.4 KiB
C
Raw Normal View History

/* NEW CODE implementing a simple system like "drive letters" but longer names,
* Copyright (C) 2024 Zak Fenton
* NO WARRANTY USE AT YOUR OWN RISK etc. under terms of UNLICENSE or MIT license
*/
#include "types.h"
#include "param.h"
#include "memlayout.h"
#include "sched.h"
#include "riscv.h"
#include "proc.h"
#include "drives.h"
#include "defs.h"
#include "fs.h"
#include "kprintf.h"
#ifndef NULL
#define NULL ((void*)0ULL)
#endif
char* strcat(char*, const char*);
void* memcpy(void*, void*, long);
struct drives* drives_alloc() {
int i;
if (sizeof(struct drives) >= PGSIZE) {
panic("drives structure too large to fit in a page");
}
struct drives* result = kalloc();
if (result == NULL) {
panic("failed to allocate drives structure");
}
initlock(&(result->lock), "drives");
for (i = 0; i < DRIVES_MAX; i++) {
result->entries[i].handler = DRIVES_HANDLER_NONE;
}
return result;
}
/* Returns the length of the drive name in the given string, allowing for
* strings with more path information to be used in lookup (rather than copying
* just the drive name to a new string!).
*/
static int drives_namelen(const char* n) {
int i = 0;
if (n == NULL) {
return -1;
}
while (*n != 0 && *n != ':') {
if (i >= DRIVES_NAMEMAX || *n == '/' || *n == '\\') {
return -1;
}
n++;
i++;
}
return i;
}
/* Like drives_namelen but checks that a drive name ending in ':' is actually
* present (returning -1 otherwise).
*/
static int drives_namelencheck(const char* n) {
int i = 0;
if (n == (void*)0ULL) {
return -1;
}
while (*n != 0) {
if (*n == ':') {
return i;
}
if (i >= DRIVES_NAMEMAX || *n == '/' || *n == '\\') {
return -1;
}
n++;
i++;
}
return -1;
}
/* Returns 1 if the name matches the given drive number or 0 otherwise. */
static int drives_namesmatch(struct drives* drives, int driveid, const char* name, int namelen) {
int i;
for (i = 0; i < namelen; i++) {
if (drives->entries[driveid].name[i] != name[i]) {
return 0; // Not a match, character differs
}
}
if (i == DRIVES_NAMEMAX || drives->entries[driveid].name[i] == 0) {
return 1; // Is a match, we checked until the end
} else {
return 0; // Not a match, drive name has more characters
}
}
/* Scans the drives structure for a matching drive. Can be run without locking,
* then the structure can be locked once a candidate is found.
*/
static int drives_find(struct drives* drives, int startingat, const char* name, int namelen) {
int i;
for (i = startingat; i < DRIVES_MAX; i++) {
if (drives_namesmatch(drives, i, name, namelen) && drives->entries[i].handler != DRIVES_HANDLER_NONE) {
return i;
}
}
return -1; // Not found.
}
int drives_setup(struct drives* drives, int handlertype, void* handlerdata, const char* name) {
acquire(&(drives->lock));
int nlen = drives_namelen(name);
int idx = drives_find(drives, 0, name, nlen);
if (idx >= 0) { // Already exists
release(&(drives->lock));
return -1;
}
for (idx = 0; idx < DRIVES_MAX; idx++) {
if (drives->entries[idx].handler == DRIVES_HANDLER_NONE) {
drives->entries[idx].nusers = 0;
drives->entries[idx].handler = handlertype;
drives->entries[idx].handlerdata = handlerdata;
int namei;
for (namei = 0; namei < nlen; namei++) {
drives->entries[idx].name[namei] = name[namei];
}
for (; namei < DRIVES_NAMEMAX; namei++) {
drives->entries[idx].name[namei] = 0;
}
release(&(drives->lock));
return idx;
}
}
// No free slots
release(&(drives->lock));
return -1;
}
int drives_open(struct drives* drives, const char* name, int* handlertype, void** handlerdata) {
acquire(&(drives->lock));
int nlen = drives_namelen(name);
int idx = drives_find(drives, 0, name, nlen);
if (idx < 0) {
release(&(drives->lock));
return idx;
}
drives->entries[idx].nusers++;
release(&(drives->lock));
return idx;
}
int drives_opensimple(struct drives* drives, int driven) {
if (drives == NULL || driven < 0 || driven > DRIVES_MAX) {
return -1; // Return -1 to indicate error
}
acquire(&(drives->lock));
drives->entries[driven].nusers++;
release(&(drives->lock));
return driven;
}
/* Safely decrements the user count of the given drive number, returning -1. */
int drives_close(struct drives* drives, int driven) {
if (drives == NULL || driven < 0 || driven > DRIVES_MAX) {
return -1; // Always returns -1 (whether driven is invalid or not)
}
acquire(&(drives->lock));
drives->entries[driven].nusers--;
release(&(drives->lock));
return -1; // Always returns -1
}
/* Safely increments the user count of the given drive number, returning the number. */
int drives_dup(struct drives* drives, int driven) {
if (driven < 0 || driven > DRIVES_MAX) {
return -1; // Returns -1 if invalid
}
acquire(&(drives->lock));
drives->entries[driven].nusers++;
release(&(drives->lock));
return driven;
}
fsinstance_t* drives_fsbegin(struct drives* drives, int hint, char* path) {
int lench = drives_namelencheck(path);
//printf("from path '%s' got lencheck %d\n", path, lench);
//int nskip = lench < 0 ? 0 : lench+1;
int dn = hint;
acquire(&(drives->lock));
if (lench >= 0) {
dn = drives_find(drives, 0, path, lench);
//printf("Found drive %d handler %d\n", dn, drives->entries[dn].handler);
}
if (dn < 0) {
release(&(drives->lock));
return 0ULL;
}
fsinstance_t* instance = drives->entries[dn].handlerdata;
release(&drives->lock);
fsinstance_begin(instance);
return instance;
}
void drives_fsend(struct drives* drives, fsinstance_t* instance) {
if (instance != NULL) {
fsinstance_end(instance);
// TODO: Add some more bookkeeping, i.e. begin/end should also imply an additional open/close of the relevant drive
}
}
void* drives_fsnode(struct drives* drives, int hint, char* path, int* fstypevar) {
int lench = drives_namelencheck(path);
//printf("from path '%s' got lencheck %d\n", path, lench);
int nskip = 0;
if (lench >= 0) {
if (path[lench+1] == '/') {
nskip = lench + 1;
} else {
// Replace the ':' with a '/' to ensure we're in the drive's root
path[lench] = '/';
nskip = lench;
}
}
int dn = hint;
acquire(&(drives->lock));
if (lench >= 0) {
dn = drives_find(drives, 0, path, lench);
//printf("Found drive %d handler %d\n", dn, drives->entries[dn].handler);
}
if (dn < 0) {
release(&(drives->lock));
return 0ULL;
}
if (fstypevar) {
*fstypevar = drives->entries[dn].handler;
}
if (drives->entries[dn].handler != DRIVES_HANDLER_FS) {
release(&(drives->lock));
return 0ULL;
}
fsinstance_t* instance = drives->entries[dn].handlerdata;
release(&(drives->lock));
void* result = fsinstance_lookup(instance, path+nskip);
return result;
}
void* drives_fsparent(struct drives* drives, int hint, char* path, int* fstypevar, char* namevar, int namelen) {
if (namelen < FSFORMAT_NAMESIZE_NEW) {
return NULL;
}
int lench = drives_namelencheck(path);
//printf("from path '%s' got lencheck %d\n", path, lench);
int nskip = 0;
if (lench >= 0) {
if (path[lench+1] == '/') {
nskip = lench + 1;
} else {
// Replace the ':' with a '/' to ensure we're in the drive's root
path[lench] = '/';
nskip = lench;
}
}
int dn = hint;
acquire(&(drives->lock));
if (lench >= 0) {
dn = drives_find(drives, 0, path, lench);
//printf("Found drive %d handler %d\n", dn, drives->entries[dn].handler);
}
if (dn < 0) {
release(&(drives->lock));
return NULL;
}
if (fstypevar) {
*fstypevar = drives->entries[dn].handler;
}
if (drives->entries[dn].handler != DRIVES_HANDLER_FS) {
release(&(drives->lock));
return NULL;
}
fsinstance_t* instance = drives->entries[dn].handlerdata;
release(&(drives->lock));
void* result = fsinstance_lookupparent(instance, path+nskip, namevar);
return result;
}
int drives_getinfo(struct drives* drives, int drivenumber, struct __syscdefs_driveinfo* structure) {
if (!structure) {
return -1;
}
// This will mark the drive as being opened but will also validate the
// drive number first so we should use it's return value as the new
// number as long as it's >= 0.
int idx = drives_opensimple(drives, drivenumber);
// The structure is cleared to zeroes before setting or returning,
// even/especially in the case of the drive not existing!
memset(structure, 0, sizeof(struct __syscdefs_driveinfo));
// If the value returned by drives_opensimple is <0 then the drive
// number doesn't exist or is out of range.
if (idx < 0) {
return -1;
}
if (drives->entries[idx].handler == DRIVES_HANDLER_FS) {
fsinstance_t* instance = drives->entries[idx].handlerdata;
memcpy(structure->name, drives->entries[idx].name, __SYSCDEFS_DRIVES_NAMEMAX);
structure->drivenumber = idx;
structure->blocksize = 4096; // TODO: Make this configurable!!!
structure->totalblocks = instance->superblock->totalblocks;
structure->freedatablocks = fsinstance_countfreeblocks(instance, instance->fslog_device);
if (instance->fsversion == 0) {
strcat(structure->fsname, "xv6-compatible (v0)");
} else if (instance->fsversion == 1) {
strcat(structure->fsname, "Herodotus FS v1");
} else {
strcat(structure->fsname, "Unrecognised version");
}
} else {
return -1;
}
drives_close(drives, idx);
return 0;
}