/* * Delete a file. */ static int sfs_remove(struct vnode *dir, const char *name) { struct sfs_vnode *sv = dir->vn_data; struct sfs_vnode *victim; int slot; int result; vfs_biglock_acquire(); /* Look for the file and fetch a vnode for it. */ result = sfs_lookonce(sv, name, &victim, &slot); if (result) { vfs_biglock_release(); return result; } /* Erase its directory entry. */ result = sfs_dir_unlink(sv, slot); if (result==0) { /* If we succeeded, decrement the link count. */ KASSERT(victim->sv_i.sfi_linkcount > 0); victim->sv_i.sfi_linkcount--; victim->sv_dirty = true; } /* Discard the reference that sfs_lookonce got us */ VOP_DECREF(&victim->sv_v); vfs_biglock_release(); return result; }
/* * Unmount code. * * VFS calls FS_SYNC on the filesystem prior to unmounting it. */ static int sfs_unmount(struct fs *fs) { struct sfs_fs *sfs = fs->fs_data; vfs_biglock_acquire(); /* Do we have any files open? If so, can't unmount. */ if (vnodearray_num(sfs->sfs_vnodes) > 0) { vfs_biglock_release(); return EBUSY; } /* We should have just had sfs_sync called. */ KASSERT(sfs->sfs_superdirty == false); KASSERT(sfs->sfs_freemapdirty == false); /* Once we start nuking stuff we can't fail. */ vnodearray_destroy(sfs->sfs_vnodes); bitmap_destroy(sfs->sfs_freemap); /* The vfs layer takes care of the device for us */ (void)sfs->sfs_device; /* Destroy the fs object */ kfree(sfs); /* nothing else to do */ vfs_biglock_release(); 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; 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; }
/* * lookparent returns the last path component as a string and the * directory it's in as a vnode. * * Since we don't support subdirectories, this is very easy - * return the root dir and copy the path. */ static int sfs_lookparent(struct vnode *v, char *path, struct vnode **ret, char *buf, size_t buflen) { struct sfs_vnode *sv = v->vn_data; vfs_biglock_acquire(); if (sv->sv_i.sfi_type != SFS_TYPE_DIR) { vfs_biglock_release(); return ENOTDIR; } if (strlen(path)+1 > buflen) { vfs_biglock_release(); return ENAMETOOLONG; } strcpy(buf, path); VOP_INCREF(&sv->sv_v); *ret = &sv->sv_v; vfs_biglock_release(); return 0; }
/* * Lookup gets a vnode for a pathname. * * Since we don't support subdirectories, it's easy - just look up the * name. */ static int sfs_lookup(struct vnode *v, char *path, struct vnode **ret) { struct sfs_vnode *sv = v->vn_data; struct sfs_vnode *final; int result; vfs_biglock_acquire(); if (sv->sv_i.sfi_type != SFS_TYPE_DIR) { vfs_biglock_release(); return ENOTDIR; } result = sfs_lookonce(sv, path, &final, NULL); if (result) { vfs_biglock_release(); return result; } *ret = &final->sv_v; vfs_biglock_release(); return 0; }
/* * VOP_RECLAIM * * Reclaim should make an effort to returning errors other than EBUSY. */ static int emufs_reclaim(struct vnode *v) { struct emufs_vnode *ev = v->vn_data; struct emufs_fs *ef = v->vn_fs->fs_data; unsigned ix, i, num; int result; /* * Need both of these locks, e_lock to protect the device * and vfs_biglock to protect the fs-related material. */ vfs_biglock_acquire(); lock_acquire(ef->ef_emu->e_lock); if (ev->ev_v.vn_refcount != 1) { lock_release(ef->ef_emu->e_lock); vfs_biglock_release(); return EBUSY; } /* emu_close retries on I/O error */ result = emu_close(ev->ev_emu, ev->ev_handle); if (result) { lock_release(ef->ef_emu->e_lock); vfs_biglock_release(); return result; } num = vnodearray_num(ef->ef_vnodes); ix = num; for (i=0; i<num; i++) { struct vnode *vx; vx = vnodearray_get(ef->ef_vnodes, i); if (vx == v) { ix = i; break; } } if (ix == num) { panic("emu%d: reclaim vnode %u not in vnode pool\n", ef->ef_emu->e_unit, ev->ev_handle); } vnodearray_remove(ef->ef_vnodes, ix); VOP_CLEANUP(&ev->ev_v); lock_release(ef->ef_emu->e_lock); vfs_biglock_release(); kfree(ev); return 0; }
/* * Called for fsync(), and also on filesystem unmount, global sync(), * and some other cases. */ static int sfs_fsync(struct vnode *v) { struct sfs_vnode *sv = v->vn_data; int result; vfs_biglock_acquire(); result = sfs_sync_inode(sv); vfs_biglock_release(); return result; }
/* * Routine to retrieve the volume name. Filesystems can be referred * to by their volume name followed by a colon as well as the name * of the device they're mounted on. */ static const char * sfs_getvolname(struct fs *fs) { struct sfs_fs *sfs = fs->fs_data; const char *ret; vfs_biglock_acquire(); ret = sfs->sfs_super.sp_volname; vfs_biglock_release(); return ret; }
/* * Called for write(). sfs_io() does the work. */ static int sfs_write(struct vnode *v, struct uio *uio) { struct sfs_vnode *sv = v->vn_data; int result; KASSERT(uio->uio_rw==UIO_WRITE); vfs_biglock_acquire(); result = sfs_io(sv, uio); vfs_biglock_release(); return result; }
/* * Read a single filename from a directory into a uio. * */ int sfs_getdirentry(struct vnode *v, struct uio *uio) { struct sfs_vnode *sv = v->vn_data; struct sfs_dir tsd; int nentries = sfs_dir_nentries(sv); int result; vfs_biglock_acquire(); while (uio->uio_offset <= nentries) { // Read directory entry result = sfs_readdir(sv, &tsd, uio->uio_offset); if (result) { vfs_biglock_release(); return result; } if (tsd.sfd_ino != SFS_NOINO) { break; } uio->uio_offset ++; } // If we still don't have a result, there must be nothing left if (tsd.sfd_ino == SFS_NOINO) { vfs_biglock_release(); return ENOMEM; } /* Ensure null termination, just in case */ tsd.sfd_name[sizeof(tsd.sfd_name)-1] = 0; // Copy results to uio result = uiomove(tsd.sfd_name, strlen(tsd.sfd_name), uio); if (result) { vfs_biglock_release(); return result; } uio->uio_offset ++; vfs_biglock_release(); return 0; }
/* * Get vnode for the root of the filesystem. * The root vnode is always found in block 1 (SFS_ROOT_LOCATION). */ struct vnode * sfs_getroot(struct fs *fs) { struct sfs_fs *sfs = fs->fs_data; struct sfs_vnode *sv; int result; vfs_biglock_acquire(); result = sfs_loadvnode(sfs, SFS_ROOT_LOCATION, SFS_TYPE_INVAL, &sv); if (result) { panic("sfs: getroot: Cannot load root vnode\n"); } vfs_biglock_release(); return &sv->sv_v; }
/* * Unmount a filesystem/device by name. * First calls FSOP_SYNC on the filesystem; then calls FSOP_UNMOUNT. */ int vfs_unmount(const char *devname) { struct knowndev *kd; int result; vfs_biglock_acquire(); result = findmount(devname, &kd); if (result) { goto fail; } if (kd->kd_fs == NULL) { result = EINVAL; goto fail; } KASSERT(kd->kd_rawname != NULL); KASSERT(kd->kd_device != NULL); /* sync the fs */ result = FSOP_SYNC(kd->kd_fs); if (result) { goto fail; } result = FSOP_UNMOUNT(kd->kd_fs); if (result) { goto fail; } kprintf("vfs: Unmounted %s:\n", kd->kd_name); /* now drop the filesystem */ kd->kd_fs = NULL; KASSERT(result==0); fail: vfs_biglock_release(); return result; }
/* * Global sync function - call FSOP_SYNC on all devices. */ int vfs_sync(void) { struct knowndev *dev; unsigned i, num; vfs_biglock_acquire(); num = knowndevarray_num(knowndevs); for (i=0; i<num; i++) { dev = knowndevarray_get(knowndevs, i); if (dev->kd_fs != NULL) { /*result =*/ FSOP_SYNC(dev->kd_fs); } } vfs_biglock_release(); return 0; }
/* * Mount a filesystem. Once we've found the device, call MOUNTFUNC to * set up the filesystem and hand back a struct fs. * * The DATA argument is passed through unchanged to MOUNTFUNC. */ int vfs_mount(const char *devname, void *data, int (*mountfunc)(void *data, struct device *, struct fs **ret)) { const char *volname; struct knowndev *kd; struct fs *fs; int result; vfs_biglock_acquire(); result = findmount(devname, &kd); if (result) { vfs_biglock_release(); return result; } if (kd->kd_fs != NULL) { vfs_biglock_release(); return EBUSY; } KASSERT(kd->kd_rawname != NULL); KASSERT(kd->kd_device != NULL); result = mountfunc(data, kd->kd_device, &fs); if (result) { vfs_biglock_release(); return result; } KASSERT(fs != NULL); kd->kd_fs = fs; volname = FSOP_GETVOLNAME(fs); kprintf("vfs: Mounted %s: on %s\n", volname ? volname : kd->kd_name, kd->kd_name); vfs_biglock_release(); return 0; }
int sfs_getdirentry(struct vnode *v_node, struct uio *user_io) { struct sfs_vnode *s_node = v_node->vn_data; struct sfs_dir sdir; int offset = user_io->uio_offset; vfs_biglock_acquire(); //acquire lock int nentries = sfs_dir_nentries(s_node); if (offset >= nentries) { vfs_biglock_release(); //release lock return ENOENT; //Error no entry } int result; while(1) { result = sfs_readdir(s_node, &sdir, user_io->uio_offset); if (result) { vfs_biglock_release(); //release lock return result; //return this result } if (sdir.sfd_ino == SFS_NOINO) { //if this sdir is the inode # for free dir entry offset++; if(offset >= nentries) { vfs_biglock_release(); //release lock return ENOENT; //error no entry } user_io->uio_offset++; continue; } break; } result = uiomove(sdir.sfd_name, strlen(sdir.sfd_name), user_io); //get the result user_io->uio_offset = offset + 1; //update uio offset vfs_biglock_release(); //release lock return 0; //and return }
/* * Return the type of the file (types as per kern/stat.h) */ static int sfs_gettype(struct vnode *v, uint32_t *ret) { struct sfs_vnode *sv = v->vn_data; vfs_biglock_acquire(); switch (sv->sv_i.sfi_type) { case SFS_TYPE_FILE: *ret = S_IFREG; vfs_biglock_release(); return 0; case SFS_TYPE_DIR: *ret = S_IFDIR; vfs_biglock_release(); return 0; } panic("sfs: gettype: Invalid inode type (inode %u, type %u)\n", sv->sv_ino, sv->sv_i.sfi_type); return EINVAL; }
/* * Add a new device to the VFS layer's device table. * * If "mountable" is set, the device will be treated as one that expects * to have a filesystem mounted on it, and a raw device will be created * for direct access. */ static int vfs_doadd(const char *dname, int mountable, struct device *dev, struct fs *fs) { char *name=NULL, *rawname=NULL; struct knowndev *kd=NULL; struct vnode *vnode=NULL; const char *volname=NULL; unsigned index; int result; vfs_biglock_acquire(); name = kstrdup(dname); if (name==NULL) { goto nomem; } if (mountable) { rawname = mkrawname(name); if (rawname==NULL) { goto nomem; } } vnode = dev_create_vnode(dev); if (vnode==NULL) { goto nomem; } kd = kmalloc(sizeof(struct knowndev)); if (kd==NULL) { goto nomem; } kd->kd_name = name; kd->kd_rawname = rawname; kd->kd_device = dev; kd->kd_vnode = vnode; kd->kd_fs = fs; if (fs!=NULL) { volname = FSOP_GETVOLNAME(fs); } if (badnames(name, rawname, volname)) { vfs_biglock_release(); return EEXIST; } result = knowndevarray_add(knowndevs, kd, &index); if (result == 0 && dev != NULL) { /* use index+1 as the device number, so 0 is reserved */ dev->d_devnumber = index+1; } vfs_biglock_release(); return result; nomem: if (name) { kfree(name); } if (rawname) { kfree(rawname); } if (vnode) { kfree(vnode); } if (kd) { kfree(kd); } vfs_biglock_release(); return ENOMEM; }
static int sfs_domount(void *options, struct device *dev, struct fs **ret) { int result; struct sfs_fs *sfs; vfs_biglock_acquire(); /* We don't pass any options through mount */ (void)options; /* * Make sure our on-disk structures aren't messed up */ KASSERT(sizeof(struct sfs_super)==SFS_BLOCKSIZE); KASSERT(sizeof(struct sfs_inode)==SFS_BLOCKSIZE); KASSERT(SFS_BLOCKSIZE % sizeof(struct sfs_dir) == 0); /* * We can't mount on devices with the wrong sector size. * * (Note: for all intents and purposes here, "sector" and * "block" are interchangeable terms. Technically a filesystem * block may be composed of several hardware sectors, but we * don't do that in sfs.) */ if (dev->d_blocksize != SFS_BLOCKSIZE) { vfs_biglock_release(); return ENXIO; } /* Allocate object */ sfs = kmalloc(sizeof(struct sfs_fs)); if (sfs==NULL) { vfs_biglock_release(); return ENOMEM; } /* Allocate array */ sfs->sfs_vnodes = vnodearray_create(); if (sfs->sfs_vnodes == NULL) { kfree(sfs); vfs_biglock_release(); return ENOMEM; } /* Set the device so we can use sfs_rblock() */ sfs->sfs_device = dev; /* Load superblock */ result = sfs_rblock(sfs, &sfs->sfs_super, SFS_SB_LOCATION); if (result) { vnodearray_destroy(sfs->sfs_vnodes); kfree(sfs); vfs_biglock_release(); return result; } /* Make some simple sanity checks */ if (sfs->sfs_super.sp_magic != SFS_MAGIC) { kprintf("sfs: Wrong magic number in superblock " "(0x%x, should be 0x%x)\n", sfs->sfs_super.sp_magic, SFS_MAGIC); vnodearray_destroy(sfs->sfs_vnodes); kfree(sfs); vfs_biglock_release(); return EINVAL; } if (sfs->sfs_super.sp_nblocks > dev->d_blocks) { kprintf("sfs: warning - fs has %u blocks, device has %u\n", sfs->sfs_super.sp_nblocks, dev->d_blocks); } /* Ensure null termination of the volume name */ sfs->sfs_super.sp_volname[sizeof(sfs->sfs_super.sp_volname)-1] = 0; /* Load free space bitmap */ sfs->sfs_freemap = bitmap_create(SFS_FS_BITMAPSIZE(sfs)); if (sfs->sfs_freemap == NULL) { vnodearray_destroy(sfs->sfs_vnodes); kfree(sfs); vfs_biglock_release(); return ENOMEM; } result = sfs_mapio(sfs, UIO_READ); if (result) { bitmap_destroy(sfs->sfs_freemap); vnodearray_destroy(sfs->sfs_vnodes); kfree(sfs); vfs_biglock_release(); return result; } /* Set up abstract fs calls */ sfs->sfs_absfs.fs_sync = sfs_sync; sfs->sfs_absfs.fs_getvolname = sfs_getvolname; sfs->sfs_absfs.fs_getroot = sfs_getroot; sfs->sfs_absfs.fs_unmount = sfs_unmount; sfs->sfs_absfs.fs_data = sfs; /* the other fields */ sfs->sfs_superdirty = false; sfs->sfs_freemapdirty = false; /* Hand back the abstract fs */ *ret = &sfs->sfs_absfs; vfs_biglock_release(); return 0; }
static int sfs_sync(struct fs *fs) { struct sfs_fs *sfs; unsigned i, num; int result; vfs_biglock_acquire(); /* * Get the sfs_fs from the generic abstract fs. * * Note that the abstract struct fs, which is all the VFS * layer knows about, is actually a member of struct sfs_fs. * The pointer in the struct fs points back to the top of the * struct sfs_fs - essentially the same object. This can be a * little confusing at first. * * The following diagram may help: * * struct sfs_fs <-------------\ * : | * : sfs_absfs (struct fs) | <------\ * : : | | * : : various members | | * : : | | * : : fs_data ----------/ | * : : ...|... * : . VFS . * : . layer . * : other members ....... * : * : * * This construct is repeated with vnodes and devices and other * similar things all over the place in OS/161, so taking the * time to straighten it out in your mind is worthwhile. */ sfs = fs->fs_data; /* Go over the array of loaded vnodes, syncing as we go. */ num = vnodearray_num(sfs->sfs_vnodes); for (i=0; i<num; i++) { struct vnode *v = vnodearray_get(sfs->sfs_vnodes, i); VOP_FSYNC(v); } /* If the free block map needs to be written, write it. */ if (sfs->sfs_freemapdirty) { result = sfs_mapio(sfs, UIO_WRITE); if (result) { vfs_biglock_release(); return result; } sfs->sfs_freemapdirty = false; } /* If the superblock needs to be written, write it. */ if (sfs->sfs_superdirty) { result = sfs_wblock(sfs, &sfs->sfs_super, SFS_SB_LOCATION); if (result) { vfs_biglock_release(); return result; } sfs->sfs_superdirty = false; } vfs_biglock_release(); return 0; }
/* * Called for ftruncate() and from sfs_reclaim. */ static int sfs_truncate(struct vnode *v, off_t len) { /* * I/O buffer for handling the indirect block. * * Note: in real life (and when you've done the fs assignment) * you would get space from the disk buffer cache for this, * not use a static area. */ static uint32_t idbuf[SFS_DBPERIDB]; struct sfs_vnode *sv = v->vn_data; struct sfs_fs *sfs = sv->sv_v.vn_fs->fs_data; /* Length in blocks (divide rounding up) */ uint32_t blocklen = DIVROUNDUP(len, SFS_BLOCKSIZE); uint32_t i, j, block; uint32_t idblock, baseblock, highblock; int result; int hasnonzero, iddirty; KASSERT(sizeof(idbuf)==SFS_BLOCKSIZE); vfs_biglock_acquire(); /* * Go through the direct blocks. Discard any that are * past the limit we're truncating to. */ for (i=0; i<SFS_NDIRECT; i++) { block = sv->sv_i.sfi_direct[i]; if (i >= blocklen && block != 0) { sfs_bfree(sfs, block); sv->sv_i.sfi_direct[i] = 0; sv->sv_dirty = true; } } /* Indirect block number */ idblock = sv->sv_i.sfi_indirect; /* The lowest block in the indirect block */ baseblock = SFS_NDIRECT; /* The highest block in the indirect block */ highblock = baseblock + SFS_DBPERIDB - 1; if (blocklen < highblock && idblock != 0) { /* We're past the proposed EOF; may need to free stuff */ /* Read the indirect block */ result = sfs_rblock(sfs, idbuf, idblock); if (result) { vfs_biglock_release(); return result; } hasnonzero = 0; iddirty = 0; for (j=0; j<SFS_DBPERIDB; j++) { /* Discard any blocks that are past the new EOF */ if (blocklen < baseblock+j && idbuf[j] != 0) { sfs_bfree(sfs, idbuf[j]); idbuf[j] = 0; iddirty = 1; } /* Remember if we see any nonzero blocks in here */ if (idbuf[j]!=0) { hasnonzero=1; } } if (!hasnonzero) { /* The whole indirect block is empty now; free it */ sfs_bfree(sfs, idblock); sv->sv_i.sfi_indirect = 0; sv->sv_dirty = true; } else if (iddirty) { /* The indirect block is dirty; write it back */ result = sfs_wblock(sfs, idbuf, idblock); if (result) { vfs_biglock_release(); return result; } } } /* Set the file size */ sv->sv_i.sfi_size = len; /* Mark the inode dirty */ sv->sv_dirty = true; vfs_biglock_release(); return 0; }
/* * Called when the vnode refcount (in-memory usage count) hits zero. * * This function should try to avoid returning errors other than EBUSY. */ static int sfs_reclaim(struct vnode *v) { struct sfs_vnode *sv = v->vn_data; struct sfs_fs *sfs = v->vn_fs->fs_data; unsigned ix, i, num; int result; vfs_biglock_acquire(); /* * Make sure someone else hasn't picked up the vnode since the * decision was made to reclaim it. (You must also synchronize * this with sfs_loadvnode.) */ if (v->vn_refcount != 1) { /* consume the reference VOP_DECREF gave us */ KASSERT(v->vn_refcount>1); v->vn_refcount--; vfs_biglock_release(); return EBUSY; } /* If there are no on-disk references to the file either, erase it. */ if (sv->sv_i.sfi_linkcount==0) { result = VOP_TRUNCATE(&sv->sv_v, 0); if (result) { vfs_biglock_release(); return result; } } /* Sync the inode to disk */ result = sfs_sync_inode(sv); if (result) { vfs_biglock_release(); return result; } /* If there are no on-disk references, discard the inode */ if (sv->sv_i.sfi_linkcount==0) { sfs_bfree(sfs, sv->sv_ino); } /* Remove the vnode structure from the table in the struct sfs_fs. */ num = vnodearray_num(sfs->sfs_vnodes); ix = num; for (i=0; i<num; i++) { struct vnode *v2 = vnodearray_get(sfs->sfs_vnodes, i); struct sfs_vnode *sv2 = v2->vn_data; if (sv2 == sv) { ix = i; break; } } if (ix == num) { panic("sfs: reclaim vnode %u not in vnode pool\n", sv->sv_ino); } vnodearray_remove(sfs->sfs_vnodes, ix); VOP_CLEANUP(&sv->sv_v); vfs_biglock_release(); /* Release the storage for the vnode structure itself. */ kfree(sv); /* Done */ 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; }
/* * Function to load a vnode into memory. */ static int emufs_loadvnode(struct emufs_fs *ef, uint32_t handle, int isdir, struct emufs_vnode **ret) { struct vnode *v; struct emufs_vnode *ev; unsigned i, num; int result; vfs_biglock_acquire(); lock_acquire(ef->ef_emu->e_lock); num = vnodearray_num(ef->ef_vnodes); for (i=0; i<num; i++) { v = vnodearray_get(ef->ef_vnodes, i); ev = v->vn_data; if (ev->ev_handle == handle) { /* Found */ VOP_INCREF(&ev->ev_v); lock_release(ef->ef_emu->e_lock); vfs_biglock_release(); *ret = ev; return 0; } } /* Didn't have one; create it */ ev = kmalloc(sizeof(struct emufs_vnode)); if (ev==NULL) { lock_release(ef->ef_emu->e_lock); return ENOMEM; } ev->ev_emu = ef->ef_emu; ev->ev_handle = handle; result = VOP_INIT(&ev->ev_v, isdir ? &emufs_dirops : &emufs_fileops, &ef->ef_fs, ev); if (result) { lock_release(ef->ef_emu->e_lock); vfs_biglock_release(); kfree(ev); return result; } result = vnodearray_add(ef->ef_vnodes, &ev->ev_v, NULL); if (result) { /* note: VOP_CLEANUP undoes VOP_INIT - it does not kfree */ VOP_CLEANUP(&ev->ev_v); lock_release(ef->ef_emu->e_lock); vfs_biglock_release(); kfree(ev); return result; } lock_release(ef->ef_emu->e_lock); vfs_biglock_release(); *ret = ev; return 0; }
/* * Rename a file. * * Since we don't support subdirectories, assumes that the two * directories passed are the same. */ static int sfs_rename(struct vnode *d1, const char *n1, struct vnode *d2, const char *n2) { struct sfs_vnode *sv = d1->vn_data; struct sfs_vnode *g1; int slot1, slot2; int result, result2; vfs_biglock_acquire(); KASSERT(d1==d2); KASSERT(sv->sv_ino == SFS_ROOT_LOCATION); /* Look up the old name of the file and get its inode and slot number*/ result = sfs_lookonce(sv, n1, &g1, &slot1); if (result) { vfs_biglock_release(); return result; } /* We don't support subdirectories */ KASSERT(g1->sv_i.sfi_type == SFS_TYPE_FILE); /* * Link it 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(sv, 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 = true; /* Unlink the old slot */ result = sfs_dir_unlink(sv, 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. */ KASSERT(g1->sv_i.sfi_linkcount>0); g1->sv_i.sfi_linkcount--; g1->sv_dirty = true; /* Let go of the reference to g1 */ VOP_DECREF(&g1->sv_v); vfs_biglock_release(); return 0; puke_harder: /* * Error recovery: try to undo what we already did */ result2 = sfs_dir_unlink(sv, 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); vfs_biglock_release(); return result; }
/* * Global unmount function. */ int vfs_unmountall(void) { struct knowndev *dev; unsigned i, num; int result; vfs_biglock_acquire(); num = knowndevarray_num(knowndevs); for (i=0; i<num; i++) { dev = knowndevarray_get(knowndevs, i); if (dev->kd_rawname == NULL) { /* not mountable/unmountable */ continue; } if (dev->kd_fs == NULL) { /* not mounted */ continue; } kprintf("vfs: Unmounting %s:\n", dev->kd_name); result = FSOP_SYNC(dev->kd_fs); if (result) { kprintf("vfs: Warning: sync failed for %s: %s, trying " "again\n", dev->kd_name, strerror(result)); result = FSOP_SYNC(dev->kd_fs); if (result) { kprintf("vfs: Warning: sync failed second time" " for %s: %s, giving up...\n", dev->kd_name, strerror(result)); /* * Do not attempt to complete the * unmount as it will likely explode. */ continue; } } result = FSOP_UNMOUNT(dev->kd_fs); if (result == EBUSY) { kprintf("vfs: Cannot unmount %s: (busy)\n", dev->kd_name); continue; } if (result) { kprintf("vfs: Warning: unmount failed for %s:" " %s, already synced, dropping...\n", dev->kd_name, strerror(result)); continue; } /* now drop the filesystem */ dev->kd_fs = NULL; } vfs_biglock_release(); return 0; }
/* * VOP_RECLAIM * * Reclaim should make an effort to returning errors other than EBUSY. */ static int emufs_reclaim(struct vnode *v) { struct emufs_vnode *ev = v->vn_data; struct emufs_fs *ef = v->vn_fs->fs_data; unsigned ix, i, num; int result; /* * Need all of these locks, e_lock to protect the device, * vfs_biglock to protect the fs-related material, and * vn_countlock for the reference count. */ vfs_biglock_acquire(); lock_acquire(ef->ef_emu->e_lock); spinlock_acquire(&ev->ev_v.vn_countlock); if (ev->ev_v.vn_refcount > 1) { /* consume the reference VOP_DECREF passed us */ ev->ev_v.vn_refcount--; spinlock_release(&ev->ev_v.vn_countlock); lock_release(ef->ef_emu->e_lock); vfs_biglock_release(); return EBUSY; } KASSERT(ev->ev_v.vn_refcount == 1); /* * Since we hold e_lock and are the last ref, nobody can increment * the refcount, so we can release vn_countlock. */ spinlock_release(&ev->ev_v.vn_countlock); /* emu_close retries on I/O error */ result = emu_close(ev->ev_emu, ev->ev_handle); if (result) { lock_release(ef->ef_emu->e_lock); vfs_biglock_release(); return result; } num = vnodearray_num(ef->ef_vnodes); ix = num; for (i=0; i<num; i++) { struct vnode *vx; vx = vnodearray_get(ef->ef_vnodes, i); if (vx == v) { ix = i; break; } } if (ix == num) { panic("emu%d: reclaim vnode %u not in vnode pool\n", ef->ef_emu->e_unit, ev->ev_handle); } vnodearray_remove(ef->ef_vnodes, ix); vnode_cleanup(&ev->ev_v); lock_release(ef->ef_emu->e_lock); vfs_biglock_release(); kfree(ev); return 0; }