Module Name: src
Committed By: reinoud
Date: Tue Jun 23 20:09:07 UTC 2009
Modified Files:
src/sys/fs/udf: udf_subr.c udf_subr.h udf_vnops.c
Log Message:
Renaming in UDF was already possible but directories could only be renamed in
the same directory.
This patch finally allows a directory to be moved between parent directories.
To generate a diff of this commit:
cvs rdiff -u -r1.93 -r1.94 src/sys/fs/udf/udf_subr.c
cvs rdiff -u -r1.13 -r1.14 src/sys/fs/udf/udf_subr.h
cvs rdiff -u -r1.44 -r1.45 src/sys/fs/udf/udf_vnops.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/udf/udf_subr.c
diff -u src/sys/fs/udf/udf_subr.c:1.93 src/sys/fs/udf/udf_subr.c:1.94
--- src/sys/fs/udf/udf_subr.c:1.93 Thu Jun 18 15:03:34 2009
+++ src/sys/fs/udf/udf_subr.c Tue Jun 23 20:09:07 2009
@@ -1,4 +1,4 @@
-/* $NetBSD: udf_subr.c,v 1.93 2009/06/18 15:03:34 reinoud Exp $ */
+/* $NetBSD: udf_subr.c,v 1.94 2009/06/23 20:09:07 reinoud Exp $ */
/*
* Copyright (c) 2006, 2008 Reinoud Zandijk
@@ -29,7 +29,7 @@
#include <sys/cdefs.h>
#ifndef lint
-__KERNEL_RCSID(0, "$NetBSD: udf_subr.c,v 1.93 2009/06/18 15:03:34 reinoud Exp $");
+__KERNEL_RCSID(0, "$NetBSD: udf_subr.c,v 1.94 2009/06/23 20:09:07 reinoud Exp $");
#endif /* not lint */
@@ -4804,6 +4804,123 @@
/* --------------------------------------------------------------------- */
+int
+udf_dir_update_rootentry(struct udf_mount *ump, struct udf_node *dir_node,
+ struct udf_node *new_parent_node)
+{
+ struct vnode *dvp = dir_node->vnode;
+ struct dirhash *dirh;
+ struct dirhash_entry *dirh_ep;
+ struct file_entry *fe;
+ struct extfile_entry *efe;
+ struct fileid_desc *fid;
+ struct dirent *dirent;
+ uint64_t file_size, diroffset;
+ uint64_t new_parent_unique_id;
+ uint32_t lb_size, fidsize;
+ int found, error;
+ char const *name = "..";
+ int namelen = 2;
+ int hit;
+
+ /* get our dirhash and make sure its read in */
+ dirhash_get(&dir_node->dir_hash);
+ error = dirhash_fill(dir_node);
+ if (error) {
+ dirhash_put(dir_node->dir_hash);
+ return error;
+ }
+ dirh = dir_node->dir_hash;
+
+ /* get new parent's unique ID */
+ fe = new_parent_node->fe;
+ efe = new_parent_node->efe;
+ if (fe) {
+ new_parent_unique_id = udf_rw64(fe->unique_id);
+ } else {
+ assert(efe);
+ new_parent_unique_id = udf_rw64(efe->unique_id);
+ }
+
+ /* get directory filesize */
+ fe = dir_node->fe;
+ efe = dir_node->efe;
+ if (fe) {
+ file_size = udf_rw64(fe->inf_len);
+ } else {
+ assert(efe);
+ file_size = udf_rw64(efe->inf_len);
+ }
+
+ /* allocate temporary space for fid */
+ lb_size = udf_rw32(dir_node->ump->logical_vol->lb_size);
+ fid = malloc(lb_size, M_UDFTEMP, M_WAITOK);
+ dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK);
+
+ /*
+ * NOTE the standard does not dictate the FID entry '..' should be
+ * first, though in practice it will most likely be.
+ */
+
+ /* search our dirhash hits */
+ found = 0;
+ dirh_ep = NULL;
+ for (;;) {
+ hit = dirhash_lookup(dirh, name, namelen, &dirh_ep);
+ /* if no hit, abort the search */
+ if (!hit)
+ break;
+
+ /* check this hit */
+ diroffset = dirh_ep->offset;
+
+ /* transfer a new fid/dirent */
+ error = udf_read_fid_stream(dvp, &diroffset, fid, dirent);
+ if (error)
+ break;
+
+ /* see if its our entry */
+ KASSERT(dirent->d_namlen == namelen);
+ if (strncmp(dirent->d_name, name, namelen) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ error = ENOENT;
+ if (error)
+ goto error_out;
+
+ /* update our ICB to the new parent, hit of lower 32 bits of uniqueid */
+ fid->icb = new_parent_node->write_loc;
+ fid->icb.longad_uniqueid = udf_rw32(new_parent_unique_id);
+
+ (void) udf_validate_tag_and_crc_sums((union dscrptr *) fid);
+
+ /* get size of fid and compensate for the read_fid_stream advance */
+ fidsize = udf_fidsize(fid);
+ diroffset -= fidsize;
+
+ /* write out */
+ error = vn_rdwr(UIO_WRITE, dir_node->vnode,
+ fid, fidsize, diroffset,
+ UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED,
+ FSCRED, NULL, NULL);
+
+ /* nothing to be done in the dirhash */
+
+error_out:
+ free(fid, M_UDFTEMP);
+ free(dirent, M_UDFTEMP);
+
+ dirhash_put(dir_node->dir_hash);
+
+ return error;
+}
+
+/* --------------------------------------------------------------------- */
+
/*
* We are not allowed to split the fid tag itself over an logical block so
* check the space remaining in the logical block.
Index: src/sys/fs/udf/udf_subr.h
diff -u src/sys/fs/udf/udf_subr.h:1.13 src/sys/fs/udf/udf_subr.h:1.14
--- src/sys/fs/udf/udf_subr.h:1.13 Sun Feb 8 19:14:52 2009
+++ src/sys/fs/udf/udf_subr.h Tue Jun 23 20:09:07 2009
@@ -1,4 +1,4 @@
-/* $NetBSD: udf_subr.h,v 1.13 2009/02/08 19:14:52 reinoud Exp $ */
+/* $NetBSD: udf_subr.h,v 1.14 2009/06/23 20:09:07 reinoud Exp $ */
/*
* Copyright (c) 2006, 2008 Reinoud Zandijk
@@ -164,6 +164,7 @@
int udf_chsize(struct vnode *vp, u_quad_t newsize, kauth_cred_t cred);
int udf_dir_detach(struct udf_mount *ump, struct udf_node *dir_node, struct udf_node *udf_node, struct componentname *cnp);
int udf_dir_attach(struct udf_mount *ump, struct udf_node *dir_node, struct udf_node *udf_node, struct vattr *vap, struct componentname *cnp);
+int udf_dir_update_rootentry(struct udf_mount *ump, struct udf_node *dir_node, struct udf_node *new_parent_node);
/* update and times */
void udf_add_to_dirtylist(struct udf_node *udf_node);
Index: src/sys/fs/udf/udf_vnops.c
diff -u src/sys/fs/udf/udf_vnops.c:1.44 src/sys/fs/udf/udf_vnops.c:1.45
--- src/sys/fs/udf/udf_vnops.c:1.44 Tue Jun 23 19:36:39 2009
+++ src/sys/fs/udf/udf_vnops.c Tue Jun 23 20:09:07 2009
@@ -1,4 +1,4 @@
-/* $NetBSD: udf_vnops.c,v 1.44 2009/06/23 19:36:39 elad Exp $ */
+/* $NetBSD: udf_vnops.c,v 1.45 2009/06/23 20:09:07 reinoud Exp $ */
/*
* Copyright (c) 2006, 2008 Reinoud Zandijk
@@ -32,7 +32,7 @@
#include <sys/cdefs.h>
#ifndef lint
-__KERNEL_RCSID(0, "$NetBSD: udf_vnops.c,v 1.44 2009/06/23 19:36:39 elad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: udf_vnops.c,v 1.45 2009/06/23 20:09:07 reinoud Exp $");
#endif /* not lint */
@@ -1818,6 +1818,57 @@
/* --------------------------------------------------------------------- */
+static int
+udf_on_rootpath(struct udf_node *fnode, struct udf_node *tdnode)
+{
+ struct udf_mount *ump = tdnode->ump;
+ struct udf_node *res_node;
+ struct long_ad icb_loc;
+ const char *name;
+ int namelen;
+ int error, found;
+
+ /* if fnode is on the path from tdnode to the root, return error */
+ name = "..";
+ namelen = 2;
+ while (fnode != tdnode) {
+ DPRINTF(NODE, ("udf_on_rootpath : fnode %p, tdnode %p\n",
+ fnode, tdnode));
+ if (tdnode->vnode->v_vflag & VV_ROOT) {
+ /* found root, accept */
+ /* DPRINTF(NODE, ("\tCOUGHT, pre-vput\n")); */
+ vput(tdnode->vnode);
+ DPRINTF(NODE, ("\tCOUGHT: valid\n"));
+ return 0;
+ }
+ /* go down one level */
+ error = udf_lookup_name_in_dir(tdnode->vnode, name, namelen,
+ &icb_loc, &found);
+
+ DPRINTF(NODE, ("\tlookup of '..' resulted in error %d, "
+ "found %d\n", error, found));
+ /* DPRINTF(NODE, ("\tvput %p\n", tdnode->vnode)); */
+ vput(tdnode->vnode);
+
+ if (error) /* now what? bail out */
+ return EINVAL;
+ if (!found) /* unlikely */
+ return EINVAL;
+
+ DPRINTF(NODE, ("\tgetting .. node\n"));
+ error = udf_get_node(ump, &icb_loc, &res_node);
+ DPRINTF(NODE, ("\treported error %d\n", error));
+ if (error) /* argh, bail out */
+ return EINVAL;
+
+ tdnode = res_node;
+ }
+ DPRINTF(NODE, ("\tCOUGHT: invalid\n"));
+ vput(tdnode->vnode);
+
+ return EINVAL;
+}
+
/* note: i tried to follow the logics of the tmpfs rename code */
int
udf_rename(void *v)
@@ -1890,12 +1941,17 @@
}
}
- /* dont allow renaming directories acros directory for now */
- if (fdnode != tdnode) {
- if (fvp->v_type == VDIR) {
- error = EINVAL;
+ /* check if moving a directory to a new parent is allowed */
+ if ((fdnode != tdnode) && (fvp->v_type == VDIR)) {
+ vref(tdvp);
+ error = udf_on_rootpath(fnode, tdnode);
+ DPRINTF(NODE, ("Dir rename allowed ? %s\n", error ? "NO":"YES"));
+
+ /* tdnode is vput()'ed, relock */
+ (void) vn_lock(tdvp, LK_EXCLUSIVE | LK_RETRY);
+
+ if (error)
goto out;
- }
}
/* remove existing entry if present */
@@ -1911,6 +1967,19 @@
error = udf_dir_detach(tdnode->ump, fdnode, fnode, fcnp);
if (error)
udf_dir_detach(tdnode->ump, tdnode, fnode, tcnp);
+ if (error)
+ goto out;
+
+ /* update tnode's '..' if moving directory to new parent */
+ if ((fdnode != tdnode) && (fvp->v_type == VDIR)) {
+ /* update fnode's '..' entry */
+ error = udf_dir_update_rootentry(fnode->ump, fnode, tdnode);
+ if (error) {
+ /* 'try' to recover from this situation */
+ udf_dir_attach(tdnode->ump, fdnode, fnode, &fvap, fcnp);
+ udf_dir_detach(tdnode->ump, tdnode, fnode, tcnp);
+ }
+ }
out:
if (fdnode != tdnode)