Module Name: src Committed By: uch Date: Sat Jul 30 03:52:05 UTC 2011
Modified Files: src/sys/fs/v7fs: v7fs_file_util.c Log Message: When rename directory, check hierarchy. Pass t_vnops rename_dir(5) To generate a diff of this commit: cvs rdiff -u -r1.3 -r1.4 src/sys/fs/v7fs/v7fs_file_util.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/fs/v7fs/v7fs_file_util.c diff -u src/sys/fs/v7fs/v7fs_file_util.c:1.3 src/sys/fs/v7fs/v7fs_file_util.c:1.4 --- src/sys/fs/v7fs/v7fs_file_util.c:1.3 Mon Jul 18 21:51:49 2011 +++ src/sys/fs/v7fs/v7fs_file_util.c Sat Jul 30 03:52:04 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: v7fs_file_util.c,v 1.3 2011/07/18 21:51:49 apb Exp $ */ +/* $NetBSD: v7fs_file_util.c,v 1.4 2011/07/30 03:52:04 uch Exp $ */ /*- * Copyright (c) 2011 The NetBSD Foundation, Inc. @@ -34,7 +34,7 @@ #endif #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: v7fs_file_util.c,v 1.3 2011/07/18 21:51:49 apb Exp $"); +__KERNEL_RCSID(0, "$NetBSD: v7fs_file_util.c,v 1.4 2011/07/30 03:52:04 uch Exp $"); #ifdef _KERNEL #include <sys/systm.h> #include <sys/param.h> @@ -61,6 +61,9 @@ static int replace_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t); static int lookup_by_number_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t); +static int can_dirmove(struct v7fs_self *, v7fs_ino_t, v7fs_ino_t); +static int lookup_parent_from_dir_subr(struct v7fs_self *, void *, + v7fs_daddr_t, size_t); int v7fs_file_link(struct v7fs_self *fs, struct v7fs_inode *parent_dir, @@ -118,26 +121,38 @@ const char *from, struct v7fs_inode *parent_to, const char *to) { v7fs_ino_t from_ino, to_ino; + struct v7fs_inode inode; int error; + bool dir_move; + /* Check source file */ if ((error = v7fs_file_lookup_by_name(fs, parent_from, from, &from_ino))) { DPRINTF("%s don't exists\n", from); return error; } + v7fs_inode_load(fs, &inode, from_ino); + dir_move = v7fs_inode_isdir(&inode); - /* If target file exists, remove. */ + /* Check target file */ error = v7fs_file_lookup_by_name(fs, parent_to, to, &to_ino); - if (error == 0) { + if (error == 0) { /* found */ DPRINTF("%s already exists\n", to); if ((error = v7fs_file_deallocate(fs, parent_to, to))) { - DPRINTF("%s can't remove\n", to); + DPRINTF("%s can't remove %d\n", to, error); return error; } } else if (error != ENOENT) { DPRINTF("error=%d\n", error); return error; } + /* Check directory hierarchy. t_vnops rename_dir(5) */ + if (dir_move && (error = can_dirmove(fs, from_ino, + parent_to->inode_number))) { + DPRINTF("dst '%s' is child dir of '%s'. error=%d\n", to, from, + error); + return error; + } if ((error = v7fs_directory_add_entry(fs, parent_to, from_ino, to))) { DPRINTF("can't add entry"); @@ -149,18 +164,14 @@ return error; } - if (parent_from != parent_to) { + if (dir_move && (parent_from != parent_to)) { /* If directory move, update ".." */ - struct v7fs_inode inode; - v7fs_inode_load(fs, &inode, from_ino); - if (v7fs_inode_isdir(&inode)) { - if ((error = v7fs_directory_replace_entry(fs, &inode, - "..", parent_to->inode_number))) { - DPRINTF("can't replace parent dir"); - return error; - } - v7fs_inode_writeback(fs, &inode); + if ((error = v7fs_directory_replace_entry(fs, &inode, "..", + parent_to->inode_number))) { + DPRINTF("can't replace parent dir"); + return error; } + v7fs_inode_writeback(fs, &inode); } return 0; @@ -258,3 +269,76 @@ return ret; } + +struct lookup_parent_arg { + v7fs_ino_t parent_ino; +}; + +static int +can_dirmove(struct v7fs_self *fs, v7fs_ino_t from_ino, v7fs_ino_t to_ino) +{ + struct v7fs_inode inode; + v7fs_ino_t parent; + int error; + + /* Start dir. */ + if ((error = v7fs_inode_load(fs, &inode, to_ino))) + return error; + + if (!v7fs_inode_isdir(&inode)) + return ENOTDIR; + + /* Lookup the parent. */ + do { + struct lookup_parent_arg arg; + /* Search parent dir */ + arg.parent_ino = 0; + v7fs_datablock_foreach(fs, &inode, lookup_parent_from_dir_subr, + &arg); + if ((parent = arg.parent_ino) == 0) { + DPRINTF("***parent missing\n"); + return ENOENT; + } + /* Load parent dir */ + if ((error = v7fs_inode_load(fs, &inode, parent))) + return error; + if (parent == from_ino) { + DPRINTF("#%d is child dir of #%d\n", to_ino, from_ino); + return EINVAL; + } + } while (parent != V7FS_ROOT_INODE); + + return 0; +} + +static int +lookup_parent_from_dir_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, + size_t sz) +{ + struct lookup_parent_arg *arg = (struct lookup_parent_arg *)ctx; + char name[V7FS_NAME_MAX + 1]; + void *buf; + int ret = 0; + + if (!(buf = scratch_read(fs, blk))) + return 0; + struct v7fs_dirent *dir = (struct v7fs_dirent *)buf; + size_t i, n = sz / sizeof(*dir); + if (!v7fs_dirent_endian_convert(fs, dir, n)) { + scratch_free(fs, buf); + return V7FS_ITERATOR_ERROR; + } + + for (i = 0; i < n; i++, dir++) { + v7fs_dirent_filename(name, dir->name); + if (strncmp(dir->name, "..", V7FS_NAME_MAX) != 0) + continue; + + arg->parent_ino = dir->inode_number; + ret = V7FS_ITERATOR_BREAK; + break; + } + + scratch_free(fs, buf); + return ret; +}