/* Reload disk inode information */ int v7fs_vnode_reload(struct mount *mp, struct vnode *vp) { struct v7fs_mount *v7fsmount = mp->mnt_data; struct v7fs_self *fs = v7fsmount->core; struct v7fs_node *v7fs_node; struct v7fs_inode *inode = &((struct v7fs_node *)vp->v_data)->inode; int target_ino = inode->inode_number; int error = 0; DPRINTF("#%d\n", target_ino); mutex_enter(&mntvnode_lock); for (v7fs_node = LIST_FIRST(&v7fsmount->v7fs_node_head); v7fs_node != NULL; v7fs_node = LIST_NEXT(v7fs_node, link)) { inode = &v7fs_node->inode; if (!v7fs_inode_allocated(inode)) { continue; } if (inode->inode_number == target_ino) { error = v7fs_inode_load(fs, &v7fs_node->inode, target_ino); DPRINTF("sync #%d error=%d\n", target_ino, error); break; } } mutex_exit(&mntvnode_lock); return error; }
static int allocate(struct v7fs_self *fs, struct v7fs_inode *parent_inode, fsnode *node, dev_t dev, struct v7fs_inode *inode) { int error; v7fs_ino_t ino; struct v7fs_fileattr attr; memset(inode, 0, sizeof(*inode)); attr_setup(node, &attr); attr.device = dev; if ((error = v7fs_file_allocate(fs, parent_inode, node->name, &attr, &ino))) { errno = error; warn("%s", node->name); return error; } node->inode->ino = ino; node->inode->flags |= FI_ALLOCATED; if ((error = v7fs_inode_load(fs, inode, ino))) { errno = error; warn("%s", node->name); return error; } return 0; }
int v7fs_rename(void *v) { struct vop_rename_args /* { struct vnode *a_fdvp; from parent-directory struct vnode *a_fvp; from file struct componentname *a_fcnp; struct vnode *a_tdvp; to parent-directory struct vnode *a_tvp; to file struct componentname *a_tcnp; } */ *a = v; struct vnode *fvp = a->a_fvp; struct vnode *tvp = a->a_tvp; struct vnode *fdvp = a->a_fdvp; struct vnode *tdvp = a->a_tdvp; struct v7fs_node *parent_from = fdvp->v_data; struct v7fs_node *parent_to = tdvp->v_data; struct v7fs_node *v7node = fvp->v_data; struct v7fs_self *fs = v7node->v7fsmount->core; const char *from_name = a->a_fcnp->cn_nameptr; const char *to_name = a->a_tcnp->cn_nameptr; int error; DPRINTF("%s->%s %p %p\n", from_name, to_name, fvp, tvp); if ((fvp->v_mount != tdvp->v_mount) || (tvp && (fvp->v_mount != tvp->v_mount))) { error = EXDEV; DPRINTF("cross-device link\n"); goto out; } // XXXsource file lock? error = v7fs_file_rename(fs, &parent_from->inode, from_name, &parent_to->inode, to_name); /* 'to file' inode may be changed. (hard-linked and it is cached.) t_vnops rename_reg_nodir */ if (error == 0 && tvp) { struct v7fs_inode *inode = &((struct v7fs_node *)tvp->v_data)->inode; error = v7fs_inode_load(fs, inode, inode->inode_number); uvm_vnp_setsize(tvp, v7fs_inode_filesize(inode)); } /* Sync dirent size change. */ uvm_vnp_setsize(tdvp, v7fs_inode_filesize(&parent_to->inode)); uvm_vnp_setsize(fdvp, v7fs_inode_filesize(&parent_from->inode)); out: if (tvp) vput(tvp); /* locked on entry */ if (tdvp == tvp) vrele(tdvp); else vput(tdvp); vrele(fdvp); vrele(fvp); return error; }
int v7fs_directory_add_entry(struct v7fs_self *fs, struct v7fs_inode *parent_dir, v7fs_ino_t ino, const char *srcname) { struct v7fs_inode inode; struct v7fs_dirent *dir; int error = 0; v7fs_daddr_t blk; void *buf; char filename[V7FS_NAME_MAX + 1]; /* Truncate filename. */ v7fs_dirent_filename(filename, srcname); DPRINTF("%s(%s) %d\n", filename, srcname, ino); /* Target inode */ if ((error = v7fs_inode_load(fs, &inode, ino))) return error; /* Expand datablock. */ if ((error = v7fs_datablock_expand(fs, parent_dir, sizeof(*dir)))) return error; /* Read last entry. */ if (!(blk = v7fs_datablock_last(fs, parent_dir, v7fs_inode_filesize(parent_dir)))) return EIO; /* Load dirent block. This vnode(parent dir) is locked by VFS layer. */ if (!(buf = scratch_read(fs, blk))) return EIO; size_t sz = v7fs_inode_filesize(parent_dir); sz = V7FS_RESIDUE_BSIZE(sz); /* last block payload. */ int n = sz / sizeof(*dir) - 1; /* Add dirent. */ dir = (struct v7fs_dirent *)buf; dir[n].inode_number = V7FS_VAL16(fs, ino); memcpy((char *)dir[n].name, filename, V7FS_NAME_MAX); /* Write back datablock */ if (!fs->io.write(fs->io.cookie, buf, blk)) error = EIO; scratch_free(fs, buf); if (v7fs_inode_isdir(&inode)) { parent_dir->nlink++; v7fs_inode_writeback(fs, parent_dir); } DPRINTF("done. (dirent size=%dbyte)\n", parent_dir->filesize); return error; }
int v7fs_directory_remove_entry(struct v7fs_self *fs, struct v7fs_inode *parent_dir, const char *name) { struct v7fs_inode inode; int error; struct v7fs_dirent lastdirent; v7fs_daddr_t lastblk; size_t sz, lastsz; v7fs_off_t pos; void *buf; /* Setup replaced entry. */ sz = parent_dir->filesize; lastblk = v7fs_datablock_last(fs, parent_dir, v7fs_inode_filesize(parent_dir)); lastsz = V7FS_RESIDUE_BSIZE(sz); pos = lastsz - sizeof(lastdirent); if (!(buf = scratch_read(fs, lastblk))) return EIO; lastdirent = *((struct v7fs_dirent *)((uint8_t *)buf + pos)); scratch_free(fs, buf); DPRINTF("last dirent=%d %s pos=%d\n", V7FS_VAL16(fs, lastdirent.inode_number), lastdirent.name, pos); struct v7fs_lookup_arg lookup_arg = { .name = name, .replace = &lastdirent/*disk endian */ }; /* Search entry that removed. replace it to last dirent. */ if ((error = v7fs_datablock_foreach(fs, parent_dir, remove_subr, &lookup_arg)) != V7FS_ITERATOR_BREAK) return ENOENT; /* Contract dirent entries. */ v7fs_datablock_contract(fs, parent_dir, sizeof(lastdirent)); DPRINTF("done. (dirent size=%dbyte)\n", parent_dir->filesize); /* Target inode */ if ((error = v7fs_inode_load(fs, &inode, lookup_arg.inode_number))) return error; if (v7fs_inode_isdir(&inode)) { parent_dir->nlink--; v7fs_inode_writeback(fs, parent_dir); } return 0; }
int readdir_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz) { struct v7fs_readdir_arg *p = (struct v7fs_readdir_arg *)ctx; struct v7fs_dirent *dir; struct dirent *dp = p->dp; struct v7fs_inode inode; char filename[V7FS_NAME_MAX + 1]; int i, n; int error = 0; void *buf; if (!(buf = scratch_read(fs, blk))) return EIO; dir = (struct v7fs_dirent *)buf; n = sz / sizeof(*dir); for (i = 0; (i < n) && (p->cnt < p->end); i++, dir++, p->cnt++) { if (p->cnt < p->start) continue; if ((error = v7fs_inode_load(fs, &inode, dir->inode_number))) break; v7fs_dirent_filename(filename, dir->name); DPRINTF("inode=%d name=%s %s\n", dir->inode_number, filename, v7fs_inode_isdir(&inode) ? "DIR" : "FILE"); memset(dp, 0, sizeof(*dp)); dp->d_fileno = dir->inode_number; dp->d_type = v7fs_mode_to_d_type(inode.mode); dp->d_namlen = strlen(filename); strcpy(dp->d_name, filename); dp->d_reclen = sizeof(*dp); if ((error = uiomove(dp, dp->d_reclen, p->uio))) { DPRINTF("uiomove failed.\n"); break; } } scratch_free(fs, buf); if (p->cnt == p->end) return V7FS_ITERATOR_BREAK; return error; }
int v7fs_remove(void *v) { struct vop_remove_args /* { struct vnodeop_desc *a_desc; struct vnode * a_dvp; struct vnode * a_vp; struct componentname * a_cnp; } */ *a = v; struct v7fs_node *parent_node = a->a_dvp->v_data; struct v7fs_mount *v7fsmount = parent_node->v7fsmount; struct vnode *vp = a->a_vp; struct vnode *dvp = a->a_dvp; struct v7fs_inode *inode = &((struct v7fs_node *)vp->v_data)->inode; struct v7fs_self *fs = v7fsmount->core; int error = 0; DPRINTF("delete %s\n", a->a_cnp->cn_nameptr); if (vp->v_type == VDIR) { error = EPERM; goto out; } if ((error = v7fs_file_deallocate(fs, &parent_node->inode, a->a_cnp->cn_nameptr))) { DPRINTF("v7fs_file_delete failed.\n"); goto out; } error = v7fs_inode_load(fs, inode, inode->inode_number); if (error) goto out; /* Sync dirent size change. */ uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode)); out: if (dvp == vp) vrele(vp); /* v_usecount-- of unlocked vp */ else vput(vp); /* unlock vp and then v_usecount-- */ vput(dvp); return error; }
static int file_copy(struct v7fs_self *fs, struct v7fs_inode *parent_inode, fsnode *node, const char *filepath) { struct v7fs_inode inode; const char *errmsg; fsinode *fnode = node->inode; int error = 0; int fd; /* Check hard-link */ if ((fnode->nlink > 1) && (fnode->flags & FI_ALLOCATED)) { if ((error = v7fs_inode_load(fs, &inode, fnode->ino))) { errmsg = "inode load"; goto err_exit; } if ((error = v7fs_file_link(fs, parent_inode, &inode, node->name))) { errmsg = "hard link"; goto err_exit; } return 0; } /* Allocate file */ if ((error = allocate(fs, parent_inode, node, 0, &inode))) { errmsg = "file allocate"; goto err_exit; } if ((error = v7fs_datablock_expand(fs, &inode, fnode->st.st_size))) { errmsg = "datablock expand"; goto err_exit; } /* Data copy */ if ((fd = open(filepath, O_RDONLY)) == -1) { error = errno; errmsg = "source file"; goto err_exit; } error = v7fs_datablock_foreach(fs, &inode, copy_subr, &(struct copy_arg){ .fd = fd });
int v7fs_rmdir(void *v) { struct vop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *a = v; struct vnode *vp = a->a_vp; struct vnode *dvp = a->a_dvp; struct v7fs_node *parent_node = dvp->v_data; struct v7fs_mount *v7fsmount = parent_node->v7fsmount; struct v7fs_inode *inode = &((struct v7fs_node *)vp->v_data)->inode; struct v7fs_self *fs = v7fsmount->core; int error = 0; DPRINTF("delete %s\n", a->a_cnp->cn_nameptr); KDASSERT(vp->v_type == VDIR); if ((error = v7fs_file_deallocate(fs, &parent_node->inode, a->a_cnp->cn_nameptr))) { DPRINTF("v7fs_directory_deallocate failed.\n"); goto out; } error = v7fs_inode_load(fs, inode, inode->inode_number); if (error) goto out; uvm_vnp_setsize(vp, v7fs_inode_filesize(inode)); /* Sync dirent size change. */ uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode)); out: vput(vp); vput(dvp); return error; }
int v7fs_file_lookup_by_name(struct v7fs_self *fs, struct v7fs_inode *parent_dir, const char *name, v7fs_ino_t *ino) { char filename[V7FS_NAME_MAX + 1]; char *q; int error; size_t len; if ((q = strchr(name, '/'))) { /* Zap following path. */ len = MIN(V7FS_NAME_MAX, q - name); memcpy(filename, name, len); filename[len] = '\0'; /* '/' -> '\0' */ } else { v7fs_dirent_filename(filename, name); } DPRINTF("%s(%s) dir=%d\n", filename, name, parent_dir->inode_number); struct v7fs_lookup_arg lookup_arg = { .name = filename, .inode_number = 0 }; if ((error = v7fs_datablock_foreach(fs, parent_dir, lookup_subr, &lookup_arg)) != V7FS_ITERATOR_BREAK) { DPRINTF("not found.\n"); return ENOENT; } *ino = lookup_arg.inode_number; DPRINTF("done. ino=%d\n", *ino); return 0; } static int lookup_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz) { struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx; struct v7fs_dirent *dir; const char *name = p->name; void *buf; size_t i, n; int ret = 0; if (!(buf = scratch_read(fs, blk))) return EIO; dir = (struct v7fs_dirent *)buf; n = sz / sizeof(*dir); v7fs_dirent_endian_convert(fs, dir, n); for (i = 0; i < n; i++, dir++) { if (dir->inode_number < 1) { DPRINTF("*** bad inode #%d ***\n", dir->inode_number); continue; } if (strncmp((const char *)dir->name, name, V7FS_NAME_MAX) == 0) { p->inode_number = dir->inode_number; ret = V7FS_ITERATOR_BREAK; /* found */ break; } } scratch_free(fs, buf); return ret; } int v7fs_file_allocate(struct v7fs_self *fs, struct v7fs_inode *parent_dir, const char *srcname, struct v7fs_fileattr *attr, v7fs_ino_t *ino) { struct v7fs_inode inode; char filename[V7FS_NAME_MAX + 1]; struct v7fs_dirent *dir; int error; /* Truncate filename. */ v7fs_dirent_filename(filename, srcname); DPRINTF("%s(%s)\n", filename, srcname); /* Check filename. */ if (v7fs_file_lookup_by_name(fs, parent_dir, filename, ino) == 0) { DPRINTF("%s exists\n", filename); return EEXIST; } /* Get new inode. */ if ((error = v7fs_inode_allocate(fs, ino))) return error; /* Set initial attribute. */ memset(&inode, 0, sizeof(inode)); inode.inode_number = *ino; inode.mode = attr->mode; inode.uid = attr->uid; inode.gid = attr->gid; if (attr->ctime) inode.ctime = attr->ctime; if (attr->mtime) inode.mtime = attr->mtime; if (attr->atime) inode.atime = attr->atime; switch (inode.mode & V7FS_IFMT) { default: DPRINTF("Can't allocate %o type.\n", inode.mode); v7fs_inode_deallocate(fs, *ino); return EINVAL; case V7FS_IFCHR: /* FALLTHROUGH */ case V7FS_IFBLK: inode.nlink = 1; inode.device = attr->device; inode.addr[0] = inode.device; break; case V7FSBSD_IFFIFO: /* FALLTHROUGH */ case V7FSBSD_IFSOCK: /* FALLTHROUGH */ case V7FSBSD_IFLNK: /* FALLTHROUGH */ case V7FS_IFREG: inode.nlink = 1; break; case V7FS_IFDIR: inode.nlink = 2; /* . + .. */ if ((error = v7fs_datablock_expand(fs, &inode, sizeof(*dir) * 2 ))) { v7fs_inode_deallocate(fs, *ino); return error; } v7fs_daddr_t blk = inode.addr[0]; void *buf; if (!(buf = scratch_read(fs, blk))) { v7fs_inode_deallocate(fs, *ino); return EIO; } dir = (struct v7fs_dirent *)buf; strcpy(dir[0].name, "."); dir[0].inode_number = V7FS_VAL16(fs, *ino); strcpy(dir[1].name, ".."); dir[1].inode_number = V7FS_VAL16(fs, parent_dir->inode_number); if (!fs->io.write(fs->io.cookie, buf, blk)) { scratch_free(fs, buf); return EIO; } scratch_free(fs, buf); break; } v7fs_inode_writeback(fs, &inode); /* Link this inode to parent directory. */ if ((error = v7fs_directory_add_entry(fs, parent_dir, *ino, filename))) { DPRINTF("can't add dirent.\n"); return error; } return 0; } int v7fs_file_deallocate(struct v7fs_self *fs, struct v7fs_inode *parent_dir, const char *name) { v7fs_ino_t ino; struct v7fs_inode inode; int error; DPRINTF("%s\n", name); if ((error = v7fs_file_lookup_by_name(fs, parent_dir, name, &ino))) { DPRINTF("no such a file: %s\n", name); return error; } DPRINTF("%s->#%d\n", name, ino); if ((error = v7fs_inode_load(fs, &inode, ino))) return error; if (v7fs_inode_isdir(&inode)) { char filename[V7FS_NAME_MAX + 1]; v7fs_dirent_filename(filename, name); /* Check parent */ if (strncmp(filename, "..", V7FS_NAME_MAX) == 0) { DPRINTF("can not remove '..'\n"); return EINVAL; /* t_vnops rename_dotdot */ } /* Check empty */ if (v7fs_inode_filesize(&inode) != sizeof(struct v7fs_dirent) * 2 /*"." + ".."*/) { DPRINTF("directory not empty.\n"); return ENOTEMPTY;/* t_vnops dir_noempty, rename_dir(6)*/ } error = v7fs_datablock_size_change(fs, 0, &inode); if (error) return error; inode.nlink = 0; /* remove this. */ } else { /* Decrement reference count. */ --inode.nlink; /* regular file. */ DPRINTF("%s nlink=%d\n", name, inode.nlink); } if ((error = v7fs_directory_remove_entry(fs, parent_dir, name))) return error; DPRINTF("remove dirent\n"); v7fs_inode_writeback(fs, &inode); return 0; }
int v7fs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) { struct v7fs_mount *v7fsmount = mp->mnt_data; struct v7fs_self *fs = v7fsmount->core; struct vnode *vp; struct v7fs_node *v7fs_node; struct v7fs_inode inode; int error; /* Lookup requested i-node */ if ((error = v7fs_inode_load(fs, &inode, ino))) { DPRINTF("v7fs_inode_load failed.\n"); return error; } retry: mutex_enter(&mntvnode_lock); for (v7fs_node = LIST_FIRST(&v7fsmount->v7fs_node_head); v7fs_node != NULL; v7fs_node = LIST_NEXT(v7fs_node, link)) { if (v7fs_node->inode.inode_number == ino) { vp = v7fs_node->vnode; mutex_enter(vp->v_interlock); mutex_exit(&mntvnode_lock); if (vget(vp, LK_EXCLUSIVE) == 0) { *vpp = vp; return 0; } else { DPRINTF("retry!\n"); goto retry; } } } mutex_exit(&mntvnode_lock); /* Allocate v-node. */ if ((error = getnewvnode(VT_V7FS, mp, v7fs_vnodeop_p, NULL, &vp))) { DPRINTF("getnewvnode error.\n"); return error; } /* Lock vnode here */ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); /* Allocate i-node */ vp->v_data = pool_get(&v7fs_node_pool, PR_WAITOK); memset(vp->v_data, 0, sizeof(*v7fs_node)); v7fs_node = vp->v_data; mutex_enter(&mntvnode_lock); LIST_INSERT_HEAD(&v7fsmount->v7fs_node_head, v7fs_node, link); mutex_exit(&mntvnode_lock); v7fs_node->vnode = vp; v7fs_node->v7fsmount = v7fsmount; v7fs_node->inode = inode;/*structure copy */ v7fs_node->lockf = NULL; /* advlock */ genfs_node_init(vp, &v7fs_genfsops); uvm_vnp_setsize(vp, v7fs_inode_filesize(&inode)); if (ino == V7FS_ROOT_INODE) { vp->v_type = VDIR; vp->v_vflag |= VV_ROOT; } else { vp->v_type = v7fs_mode_to_vtype(inode.mode); if (vp->v_type == VBLK || vp->v_type == VCHR) { dev_t rdev = inode.device; vp->v_op = v7fs_specop_p; spec_node_init(vp, rdev); } else if (vp->v_type == VFIFO) { vp->v_op = v7fs_fifoop_p; } } *vpp = vp; return 0; }