/* * Make a hard link to a file. * The VFS layer should prevent this being called unless both * vnodes are ours. */ static int sfs_link(struct vnode *dir, const char *name, struct vnode *file) { struct sfs_vnode *sv = dir->vn_data; struct sfs_vnode *f = file->vn_data; int result; KASSERT(file->vn_fs == dir->vn_fs); vfs_biglock_acquire(); /* Just create a link */ result = sfs_dir_link(sv, name, f->sv_ino, NULL); if (result) { vfs_biglock_release(); return result; } /* and update the link count, marking the inode dirty */ f->sv_i.sfi_linkcount++; f->sv_dirty = true; vfs_biglock_release(); return 0; }
/* Setup . and .. in root directory if this is the first mount */ int sfs_setup_root(struct sfs_fs *sfs) { struct fs *fs = &sfs->sfs_absfs; struct vnode *root_vv; struct sfs_vnode *root_sv; int result; root_vv = sfs_getroot(fs); root_sv = root_vv->vn_data; if(sfs_dir_nentries(root_sv) != 0){ VOP_DECREF(root_vv); return 0; } result = sfs_dir_link(root_sv, ".", SFS_ROOT_LOCATION, NULL); if (result) { VOP_DECREF(root_vv); return result; } root_sv->sv_i.sfi_linkcount++; root_sv->sv_dirty = 1; result = sfs_dir_link(root_sv, "..", SFS_ROOT_LOCATION, NULL); if (result) { sfs_dir_unlink(root_sv, 0); /* Hope this doesn't fail?? */ root_sv->sv_i.sfi_linkcount--; root_sv->sv_dirty = 1; VOP_DECREF(root_vv); return result; } root_sv->sv_i.sfi_linkcount++; root_sv->sv_dirty = 1; /* Decrement reference that sfs_getroot incremented */ VOP_DECREF(root_vv); return 0; }
/* * Create a file. If EXCL is set, insist that the filename not already * exist; otherwise, if it already exists, just open it. */ static int sfs_creat(struct vnode *v, const char *name, int excl, struct vnode **ret) { struct sfs_fs *sfs = v->vn_fs->fs_data; struct sfs_vnode *sv = v->vn_data; struct sfs_vnode *newguy; u_int32_t ino; int result; /* Look up the name */ result = sfs_dir_findname(sv, name, &ino, NULL, NULL); if (result!=0 && result!=ENOENT) { return result; } /* If it exists and we didn't want it to, fail */ if (result==0 && excl) { return EEXIST; } if (result==0) { /* We got a file; load its vnode and return */ result = sfs_loadvnode(sfs, ino, SFS_TYPE_INVAL, &newguy); if (result) { return result; } *ret = &newguy->sv_v; return 0; } /* Didn't exist - create it */ result = sfs_makeobj(sfs, SFS_TYPE_FILE, &newguy); if (result) { return result; } /* Link it into the directory */ result = sfs_dir_link(sv, name, newguy->sv_ino, NULL); if (result) { VOP_DECREF(&newguy->sv_v); return result; } /* Update the linkcount of the new file */ newguy->sv_i.sfi_linkcount++; /* and consequently mark it dirty. */ newguy->sv_dirty = 1; *ret = &newguy->sv_v; return 0; }
/* * Make a hard link to a file. * The VFS layer should prevent this being called unless both * vnodes are ours. * * Locking: locks both vnodes, parent first. Since we aren't allowed * to hardlink directories, the target can't be an ancestor of the * directory we're working in. * * Requires up to 4 buffers. */ static int sfs_link(struct vnode *dir, const char *name, struct vnode *file) { struct sfs_vnode *sv = dir->vn_data; struct sfs_vnode *f = file->vn_data; struct sfs_dinode *inodeptr; int result; KASSERT(file->vn_fs == dir->vn_fs); /* Hard links to directories aren't allowed. */ if (f->sv_type == SFS_TYPE_DIR) { return EINVAL; } KASSERT(file != dir); reserve_buffers(SFS_BLOCKSIZE); /* directory must be locked first */ lock_acquire(sv->sv_lock); lock_acquire(f->sv_lock); result = sfs_dinode_load(f); if (result) { lock_release(f->sv_lock); lock_release(sv->sv_lock); unreserve_buffers(SFS_BLOCKSIZE); return result; } /* Create the link */ result = sfs_dir_link(sv, name, f->sv_ino, NULL); if (result) { sfs_dinode_unload(f); lock_release(f->sv_lock); lock_release(sv->sv_lock); unreserve_buffers(SFS_BLOCKSIZE); return result; } /* and update the link count, marking the inode dirty */ inodeptr = sfs_dinode_map(f); inodeptr->sfi_linkcount++; sfs_dinode_mark_dirty(f); sfs_dinode_unload(f); lock_release(f->sv_lock); lock_release(sv->sv_lock); unreserve_buffers(SFS_BLOCKSIZE); return 0; }
/* * Make a hard link to a file. * The VFS layer should prevent this being called unless both * vnodes are ours. */ static int sfs_link(struct vnode *dir, const char *name, struct vnode *file) { struct sfs_vnode *sv = dir->vn_data; struct sfs_vnode *f = file->vn_data; int result; assert(file->vn_fs == dir->vn_fs); /* Just create a link */ result = sfs_dir_link(sv, name, f->sv_ino, NULL); if (result) { return result; } /* and update the link count, marking the inode dirty */ f->sv_i.sfi_linkcount++; f->sv_dirty = 1; return 0; }
static int sfs_mkdir(struct vnode *vv, const char *name){ struct sfs_fs *sfs = vv->vn_fs->fs_data; struct sfs_vnode *sv = vv->vn_data; struct sfs_vnode *newguy; u_int32_t ino; int result, result2, slot1, slot2; /* Look up the name */ result = sfs_dir_findname(sv, name, &ino, NULL, NULL); if (result!=0 && result!=ENOENT) { return result; } if (result==0) { /* Directory or file of same name already exists */ return EINVAL; } /* Didn't exist - create it */ result = sfs_makeobj(sfs, SFS_TYPE_DIR, &newguy); if (result) { return result; } /* Link it into the directory */ result = sfs_dir_link(sv, name, newguy->sv_ino, &slot1); if (result) { VOP_DECREF(&newguy->sv_v); return result; } /* Increment linkcount of the new directory and mark it dirty */ newguy->sv_i.sfi_linkcount++; newguy->sv_dirty = 1; /* Link . and .. into our directory */ result = sfs_dir_link(newguy, ".", newguy->sv_ino, &slot2); if (result) { result2 = sfs_dir_unlink(sv, slot1); if(result2){ VOP_DECREF(&newguy->sv_v); return result2; } newguy->sv_i.sfi_linkcount--; newguy->sv_dirty = 1; VOP_DECREF(&newguy->sv_v); return result; } newguy->sv_i.sfi_linkcount++; newguy->sv_dirty = 1; result = sfs_dir_link(newguy, "..", sv->sv_ino, NULL); if (result) { result2 = sfs_dir_unlink(newguy, slot2); if(result2){ VOP_DECREF(&newguy->sv_v); return result2; } newguy->sv_i.sfi_linkcount--; newguy->sv_dirty = 1; result2 = sfs_dir_unlink(sv, slot1); if(result2){ VOP_DECREF(&newguy->sv_v); return result2; } newguy->sv_i.sfi_linkcount--; newguy->sv_dirty = 1; VOP_DECREF(&newguy->sv_v); return result; } sv->sv_i.sfi_linkcount++; sv->sv_dirty = 1; VOP_DECREF(&newguy->sv_v); return 0; }
/* * Rename a file. */ static int sfs_rename(struct vnode *d1, const char *n1, struct vnode *d2, const char *n2) { struct sfs_vnode *sv1 = d1->vn_data; struct sfs_vnode *sv2 = d2->vn_data; struct sfs_vnode *g1; int slot1, slot2; int result, result2; /* Look up the old name of the file and get its inode and slot number*/ result = sfs_lookonce(sv1, n1, &g1, &slot1); if (result) { return result; } /* * Link it to the 'new' directory under the new name. * * We could theoretically just overwrite the original * directory entry, except that we need to check to make sure * the new name doesn't already exist; might as well use the * existing link routine. */ result = sfs_dir_link(sv2, n2, g1->sv_ino, &slot2); if (result) { goto puke; } /* Increment the link count, and mark inode dirty */ g1->sv_i.sfi_linkcount++; g1->sv_dirty = 1; /* Unlink the old slot */ result = sfs_dir_unlink(sv1, slot1); if (result) { goto puke_harder; } /* * Decrement the link count again, and mark the inode dirty again, * in case it's been synced behind our back. */ assert(g1->sv_i.sfi_linkcount>0); g1->sv_i.sfi_linkcount--; g1->sv_dirty = 1; /* Let go of the reference to g1 */ VOP_DECREF(&g1->sv_v); return 0; puke_harder: /* * Error recovery: try to undo what we already did */ result2 = sfs_dir_unlink(sv2, slot2); if (result2) { kprintf("sfs: rename: %s\n", strerror(result)); kprintf("sfs: rename: while cleaning up: %s\n", strerror(result2)); panic("sfs: rename: Cannot recover\n"); } g1->sv_i.sfi_linkcount--; puke: /* Let go of the reference to g1 */ VOP_DECREF(&g1->sv_v); return result; }
/* * Delete a directory. * * Locking: Acquires vnode lock for parent dir and then vnode lock for * victim dir. Releases both. * * Requires 4 buffers. */ static int sfs_rmdir(struct vnode *v, const char *name) { struct sfs_vnode *sv = v->vn_data; struct sfs_fs *sfs = sv->sv_absvn.vn_fs->fs_data; struct sfs_vnode *victim; struct sfs_dinode *dir_inodeptr; struct sfs_dinode *victim_inodeptr; int result, result2; int slot; /* Cannot remove the . or .. entries from a directory! */ if (!strcmp(name, ".") || !strcmp(name, "..")) { return EINVAL; } lock_acquire(sv->sv_lock); reserve_buffers(SFS_BLOCKSIZE); result = sfs_dinode_load(sv); if (result) { goto die_loadsv; } dir_inodeptr = sfs_dinode_map(sv); if (dir_inodeptr->sfi_linkcount == 0) { result = ENOENT; goto die_linkcount; } result = sfs_lookonce(sv, name, &victim, &slot); if (result) { goto die_linkcount; } lock_acquire(victim->sv_lock); result = sfs_dinode_load(victim); if (result) { goto die_loadvictim; } victim_inodeptr = sfs_dinode_map(victim); if (victim->sv_ino == SFS_ROOTDIR_INO) { result = EPERM; goto die_total; } /* Only allowed on directories */ if (victim_inodeptr->sfi_type != SFS_TYPE_DIR) { result = ENOTDIR; goto die_total; } result = sfs_dir_checkempty(victim); if (result) { goto die_total; } result = sfs_dir_unlink(sv, slot); if (result) { goto die_total; } KASSERT(dir_inodeptr->sfi_linkcount > 1); KASSERT(victim_inodeptr->sfi_linkcount==2); dir_inodeptr->sfi_linkcount--; sfs_dinode_mark_dirty(sv); victim_inodeptr->sfi_linkcount -= 2; sfs_dinode_mark_dirty(victim); result = sfs_itrunc(victim, 0); if (result) { victim_inodeptr->sfi_linkcount += 2; dir_inodeptr->sfi_linkcount++; result2 = sfs_dir_link(sv, name, victim->sv_ino, NULL); if (result2) { /* XXX: would be better if this case didn't exist */ panic("sfs: %s: rmdir: %s; while recovering: %s\n", sfs->sfs_sb.sb_volname, strerror(result), strerror(result2)); } goto die_total; } die_total: sfs_dinode_unload(victim); die_loadvictim: lock_release(victim->sv_lock); VOP_DECREF(&victim->sv_absvn); die_linkcount: sfs_dinode_unload(sv); die_loadsv: unreserve_buffers(SFS_BLOCKSIZE); lock_release(sv->sv_lock); return result; }
static int sfs_mkdir(struct vnode *v, const char *name, mode_t mode) { struct sfs_fs *sfs = v->vn_fs->fs_data; struct sfs_vnode *sv = v->vn_data; int result; uint32_t ino; struct sfs_dinode *dir_inodeptr; struct sfs_dinode *new_inodeptr; struct sfs_vnode *newguy; (void)mode; lock_acquire(sv->sv_lock); reserve_buffers(SFS_BLOCKSIZE); result = sfs_dinode_load(sv); if (result) { goto die_early; } dir_inodeptr = sfs_dinode_map(sv); if (dir_inodeptr->sfi_linkcount == 0) { result = ENOENT; goto die_simple; } /* Look up the name */ result = sfs_dir_findname(sv, name, &ino, NULL, NULL); if (result!=0 && result!=ENOENT) { goto die_simple; } /* If it exists, fail */ if (result==0) { result = EEXIST; goto die_simple; } /* * If we're trying to create . or .. and we get this far * (meaning the entry in question is missing), the fs is * corrupted. Let's not make it worse. Best at this point to * bail out and run fsck. */ if (!strcmp(name, ".") || !strcmp(name, "..")) { panic("sfs: %s: No %s entry in dir %u; please fsck\n", sfs->sfs_sb.sb_volname, name, sv->sv_ino); } result = sfs_makeobj(sfs, SFS_TYPE_DIR, &newguy); if (result) { goto die_simple; } new_inodeptr = sfs_dinode_map(newguy); result = sfs_dir_link(newguy, ".", newguy->sv_ino, NULL); if (result) { goto die_uncreate; } result = sfs_dir_link(newguy, "..", sv->sv_ino, NULL); if (result) { goto die_uncreate; } result = sfs_dir_link(sv, name, newguy->sv_ino, NULL); if (result) { goto die_uncreate; } /* * Increment link counts (Note: not until after the names are * added - that way if one fails, the link count will be zero, * and reclaim will dispose of the new directory. * * Note also that the name in the parent directory gets added * last, so there's no case in which we have to go back and * remove it. */ new_inodeptr->sfi_linkcount += 2; dir_inodeptr->sfi_linkcount++; sfs_dinode_mark_dirty(newguy); sfs_dinode_mark_dirty(sv); sfs_dinode_unload(newguy); sfs_dinode_unload(sv); lock_release(newguy->sv_lock); lock_release(sv->sv_lock); VOP_DECREF(&newguy->sv_absvn); unreserve_buffers(SFS_BLOCKSIZE); KASSERT(result==0); return result; die_uncreate: sfs_dinode_unload(newguy); lock_release(newguy->sv_lock); VOP_DECREF(&newguy->sv_absvn); die_simple: sfs_dinode_unload(sv); die_early: unreserve_buffers(SFS_BLOCKSIZE); lock_release(sv->sv_lock); return result; }
/* * Create a file. If EXCL is set, insist that the filename not already * exist; otherwise, if it already exists, just open it. * * Locking: Gets/releases the vnode lock for v. Does not lock the new vnode, * as nobody else can get to it except by searching the directory it's in, * which is locked. * * Requires up to 4 buffers as VOP_DECREF invocations may take 3. */ static int sfs_creat(struct vnode *v, const char *name, bool excl, mode_t mode, struct vnode **ret) { struct sfs_fs *sfs = v->vn_fs->fs_data; struct sfs_vnode *sv = v->vn_data; struct sfs_vnode *newguy; struct sfs_dinode *sv_dino; struct sfs_dinode *new_dino; uint32_t ino; int result; lock_acquire(sv->sv_lock); reserve_buffers(SFS_BLOCKSIZE); result = sfs_dinode_load(sv); if (result) { unreserve_buffers(SFS_BLOCKSIZE); lock_release(sv->sv_lock); return result; } sv_dino = sfs_dinode_map(sv); if (sv_dino->sfi_linkcount == 0) { sfs_dinode_unload(sv); unreserve_buffers(SFS_BLOCKSIZE); lock_release(sv->sv_lock); return ENOENT; } sfs_dinode_unload(sv); /* Look up the name */ result = sfs_dir_findname(sv, name, &ino, NULL, NULL); if (result!=0 && result!=ENOENT) { unreserve_buffers(SFS_BLOCKSIZE); lock_release(sv->sv_lock); return result; } /* If it exists and we didn't want it to, fail */ if (result==0 && excl) { unreserve_buffers(SFS_BLOCKSIZE); lock_release(sv->sv_lock); return EEXIST; } if (result==0) { /* We got something; load its vnode and return */ result = sfs_loadvnode(sfs, ino, SFS_TYPE_INVAL, &newguy); if (result) { unreserve_buffers(SFS_BLOCKSIZE); lock_release(sv->sv_lock); return result; } *ret = &newguy->sv_absvn; unreserve_buffers(SFS_BLOCKSIZE); lock_release(sv->sv_lock); return 0; } /* Didn't exist - create it */ result = sfs_makeobj(sfs, SFS_TYPE_FILE, &newguy); if (result) { unreserve_buffers(SFS_BLOCKSIZE); lock_release(sv->sv_lock); return result; } /* sfs_makeobj loads the inode for us */ new_dino = sfs_dinode_map(newguy); /* We don't currently support file permissions; ignore MODE */ (void)mode; /* Link it into the directory */ result = sfs_dir_link(sv, name, newguy->sv_ino, NULL); if (result) { sfs_dinode_unload(newguy); lock_release(newguy->sv_lock); VOP_DECREF(&newguy->sv_absvn); lock_release(sv->sv_lock); unreserve_buffers(SFS_BLOCKSIZE); return result; } /* Update the linkcount of the new file */ new_dino->sfi_linkcount++; /* and consequently mark it dirty. */ sfs_dinode_mark_dirty(newguy); *ret = &newguy->sv_absvn; sfs_dinode_unload(newguy); unreserve_buffers(SFS_BLOCKSIZE); lock_release(newguy->sv_lock); lock_release(sv->sv_lock); return 0; }
/* * Create a file. If EXCL is set, insist that the filename not already * exist; otherwise, if it already exists, just open it. */ static int sfs_creat(struct vnode *v, const char *name, bool excl, mode_t mode, struct vnode **ret) { struct sfs_fs *sfs = v->vn_fs->fs_data; struct sfs_vnode *sv = v->vn_data; struct sfs_vnode *newguy; uint32_t ino; int result; vfs_biglock_acquire(); /* Look up the name */ result = sfs_dir_findname(sv, name, &ino, NULL, NULL); if (result!=0 && result!=ENOENT) { vfs_biglock_release(); return result; } /* If it exists and we didn't want it to, fail */ if (result==0 && excl) { vfs_biglock_release(); return EEXIST; } if (result==0) { /* We got a file; load its vnode and return */ result = sfs_loadvnode(sfs, ino, SFS_TYPE_INVAL, &newguy); if (result) { vfs_biglock_release(); return result; } *ret = &newguy->sv_v; vfs_biglock_release(); return 0; } /* Didn't exist - create it */ result = sfs_makeobj(sfs, SFS_TYPE_FILE, &newguy); if (result) { vfs_biglock_release(); return result; } /* We don't currently support file permissions; ignore MODE */ (void)mode; /* Link it into the directory */ result = sfs_dir_link(sv, name, newguy->sv_ino, NULL); if (result) { VOP_DECREF(&newguy->sv_v); vfs_biglock_release(); return result; } /* Update the linkcount of the new file */ newguy->sv_i.sfi_linkcount++; /* and consequently mark it dirty. */ newguy->sv_dirty = true; *ret = &newguy->sv_v; vfs_biglock_release(); return 0; }