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 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)*/ } 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"); if (inode.nlink == 0) { v7fs_datablock_contract(fs, &inode, inode.filesize); DPRINTF("remove datablock\n"); v7fs_inode_deallocate(fs, ino); DPRINTF("remove inode\n"); } else { v7fs_inode_writeback(fs, &inode); } return 0; }