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)

Reply via email to