/* 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; }