vfs: finish new locking scheme

This commit is contained in:
Matheus
2024-08-27 21:02:12 -03:00
parent 8f6bc6c4c1
commit f55b51a06d
8 changed files with 168 additions and 128 deletions
+112 -35
View File
@@ -469,15 +469,21 @@ int vfs_link(vnode_t *destref, char *destpath, vnode_t *linkref, char *linkpath,
int vfs_unlink(vnode_t *ref, char *path) {
char *component = alloc(strlen(path) + 1);
if (component == NULL)
return ENOMEM;
vnode_t *parent = NULL;
int err = vfs_lookup(&parent, ref, path, component, VFS_LOOKUP_PARENT);
int err = vfs_lookup(&parent, ref, path, component, VFS_LOOKUP_NOLINK | VFS_LOOKUP_PARENT);
if (err)
goto cleanup;
bool isdotdot = strcmp(path, "..") == 0;
vnode_t *child = NULL;
err = VOP_LOOKUP(parent, component, &child, getcred());
if (err) {
VOP_UNLOCK(parent);
VOP_RELEASE(parent);
goto cleanup;
}
@@ -485,16 +491,22 @@ int vfs_unlink(vnode_t *ref, char *path) {
if (0) {
// locked by VOP_LOOKUP
VOP_UNLOCK(child);
// locked by vfs_lookup
VOP_UNLOCK(parent);
// locked by vfs_lookup, unlocked by VOP_LOOKUP if component is ".."
if (!isdotdot)
VOP_UNLOCK(parent);
VOP_RELEASE(child);
VOP_RELEASE(parent);
return err;
}
err = VOP_UNLINK(parent, child, component, getcred());
// locked by VOP_LOOKUP
VOP_UNLOCK(child);
// locked by vfs_lookup
VOP_UNLOCK(parent);
// locked by vfs_lookup, unlocked by VOP_LOOKUP if component is ".."
if (!isdotdot)
VOP_UNLOCK(parent);
VOP_RELEASE(child);
VOP_RELEASE(parent);
cleanup:
@@ -502,6 +514,95 @@ int vfs_unlink(vnode_t *ref, char *path) {
return err;
}
int vfs_rename(vnode_t *srcref, char *srcpath, vnode_t *dstref, char *dstpath, int flags) {
void *srccomp = alloc(strlen(srcpath) + 1);
if (srccomp == NULL)
return ENOMEM;
void *dstcomp = alloc(strlen(dstpath) + 1);
if (dstcomp == NULL) {
free(srccomp);
return ENOMEM;
}
vnode_t *srcdir = NULL;
vnode_t *dstdir = NULL;
int err = vfs_lookup(&srcdir, srcref, srcpath, srccomp, VFS_LOOKUP_NOLINK | VFS_LOOKUP_PARENT);
if (err)
goto cleanup_comp;
VOP_UNLOCK(srcdir); // locked by vfs_lookup, unlocked here to allow the other vfs_lookup to not deadlock
err = vfs_lookup(&dstdir, dstref, dstpath, dstcomp, VFS_LOOKUP_NOLINK | VFS_LOOKUP_PARENT);
if (err) {
VOP_RELEASE(srcdir);
goto cleanup_comp;
}
if (srcdir != dstdir)
VOP_LOCK(srcdir); // lock it again for the other ops if they arent the same
bool srcdotdot = strcmp(srccomp, "..") == 0;
bool dstdotdot = strcmp(dstcomp, "..") == 0;
vnode_t *src = NULL;
vnode_t *dst = NULL;
err = VOP_LOOKUP(srcdir, srccomp, &src, getcred());
if (err)
goto cleanup_locks;
// srcdir is unlocked by VOP_LOOKUP if srccomp is ".."
if (srcdotdot)
VOP_LOCK(srcdir);
VOP_UNLOCK(src); // locked by VOP_LOOKUP, unlocked here to allow the other VOP_LOOKUP to not deadlock
err = VOP_LOOKUP(dstdir, dstcomp, &dst, getcred());
if (err != 0 && err != ENOENT) {
VOP_RELEASE(src);
goto cleanup_locks;
}
// dstdir is unlocked by VOP_LOOKUP if dstcomp is ".."
if (dstdotdot)
VOP_LOCK(dstdir);
if (src != dst)
VOP_LOCK(src); // lock it again for the rename if they arent the same
// TODO auth check
if (0)
goto cleanup_child;
err = VOP_RENAME(srcdir, src, srccomp, dstdir, dst, dstcomp, flags);
cleanup_child:
if (src != dst)
VOP_UNLOCK(src);
if (dst)
VOP_UNLOCK(dst);
VOP_RELEASE(src);
if (dst)
VOP_RELEASE(dst);
cleanup_locks:
if (srcdir != dstdir)
VOP_UNLOCK(srcdir);
VOP_UNLOCK(dstdir); // locked by vfs_lookup
VOP_RELEASE(srcdir);
VOP_RELEASE(dstdir);
cleanup_comp:
free(srccomp);
free(dstcomp);
return err;
}
// returns the highest node in a mount point
static int highestnodeinmp(vnode_t *node, vnode_t **ret) {
int e = 0;
@@ -523,7 +624,6 @@ static vnode_t *lowestnodeinmp(vnode_t *node) {
// looks up a pathname
// result will always be returned locked.
// if flags & VFS_LOOKUP_INPUTLOCKED, the input is already locked and will be locked on return
// if flags & VFS_LOOKUP_PARENT, the parent of the resulting node will be put in
// the result pointer and lastcomp will contain the last component name
// if flags & VFS_LOOKUP_NOLINK, the last component will not be dereferenced if its a symbolic link
@@ -545,12 +645,6 @@ int vfs_lookup(vnode_t **result, vnode_t *start, char *path, char *lastcomp, int
if (e)
return e;
if ((flags & VFS_LOOKUP_INPUTLOCKED) == 0) {
arch_e9_puts("lock1\n");
VOP_LOCK(r);
arch_e9_puts("lock1ok\n");
}
VOP_HOLD(r);
*result = r;
return 0;
@@ -578,11 +672,7 @@ int vfs_lookup(vnode_t **result, vnode_t *start, char *path, char *lastcomp, int
vnode_t *next;
error = 0;
VOP_HOLD(current);
arch_e9_puts("lock2\n");
if ((flags & VFS_LOOKUP_INPUTLOCKED) == 0 || current != start)
VOP_LOCK(current);
arch_e9_puts("lock2ok\n");
VOP_LOCK(current);
for (int i = 0; i < pathlen; ++i) {
if (compbuffer[i] == '\0')
@@ -604,17 +694,14 @@ int vfs_lookup(vnode_t **result, vnode_t *start, char *path, char *lastcomp, int
islast = j == pathlen;
}
arch_e9_puts("component: ");
arch_e9_puts(component);
if (islast && (flags & VFS_LOOKUP_PARENT)) {
__assert(lastcomp);
strcpy(lastcomp, component);
break;
}
if (strcmp(component, "..") == 0) {
bool isdotdot = strcmp(component, "..") == 0;
if (isdotdot) {
// if the tree root, skip to next component
vnode_t *root = NULL;
__assert(highestnodeinmp(vfsroot, &root) == 0);
@@ -626,7 +713,6 @@ int vfs_lookup(vnode_t **result, vnode_t *start, char *path, char *lastcomp, int
// if the root of a mounted fs, go to
if (current->flags & V_FLAGS_ROOT) {
vnode_t *n = lowestnodeinmp(current);
arch_e9_puts("lock3\n");
if (n != current) {
VOP_HOLD(n);
VOP_UNLOCK(current);
@@ -634,7 +720,6 @@ int vfs_lookup(vnode_t **result, vnode_t *start, char *path, char *lastcomp, int
current = n;
VOP_LOCK(current);
}
arch_e9_puts("lock3ok\n");
}
}
@@ -643,13 +728,12 @@ int vfs_lookup(vnode_t **result, vnode_t *start, char *path, char *lastcomp, int
if (error)
break;
arch_e9_puts("lookup\n");
error = VOP_LOOKUP(current, component, &next, getcred());
arch_e9_puts("lookupok\n");
if (error)
break;
if ((current != start || (flags & VFS_LOOKUP_INPUTLOCKED) == 0) && current != next)
// if the current node is not the next one and VOP_LOOKUP didn't already by looking up "..", unlock it
if (current != next && isdotdot == false)
VOP_UNLOCK(current);
vnode_t *r = next;
@@ -663,13 +747,11 @@ int vfs_lookup(vnode_t **result, vnode_t *start, char *path, char *lastcomp, int
}
if (r != next) {
arch_e9_puts("lock4\n");
VOP_HOLD(r);
VOP_UNLOCK(next);
VOP_RELEASE(next);
next = r;
VOP_LOCK(next);
arch_e9_puts("lock4ok\n");
}
// dereference symlink
@@ -703,9 +785,7 @@ int vfs_lookup(vnode_t **result, vnode_t *start, char *path, char *lastcomp, int
}
// TODO possible deadlock when input vnode is locked and the vfs_lookup would lock it
arch_e9_puts("it1\n");
error = vfs_lookup(&derefresult, derefstart, linkderef, NULL, pass);
arch_e9_puts("it1ok\n");
if (*linkderef == '/')
VOP_RELEASE(derefstart);
@@ -726,10 +806,7 @@ int vfs_lookup(vnode_t **result, vnode_t *start, char *path, char *lastcomp, int
}
if (error) {
// current is locked here, unlock it if not the start or start wasnt locked
if (current != start || (flags & VFS_LOOKUP_INPUTLOCKED) == 0)
VOP_UNLOCK(current);
VOP_UNLOCK(current);
VOP_RELEASE(current);
} else {
// result is already locked here