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)