335 lines
9.4 KiB
C
335 lines
9.4 KiB
C
/* 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;
|
|
}
|