Module Name: src Committed By: hannken Date: Sat Oct 23 07:41:38 UTC 2021
Modified Files: src/sys/fs/msdosfs: files.msdosfs msdosfs_lookup.c msdosfs_vnops.c src/sys/modules/msdos: Makefile src/sys/rump/fs/lib/libmsdos: Makefile Added Files: src/sys/fs/msdosfs: msdosfs_rename.c Log Message: Move msdosfs_rename() and doscheckpath() to new file msdosfs_rename.c. No functional change. To generate a diff of this commit: cvs rdiff -u -r1.3 -r1.4 src/sys/fs/msdosfs/files.msdosfs cvs rdiff -u -r1.38 -r1.39 src/sys/fs/msdosfs/msdosfs_lookup.c cvs rdiff -u -r0 -r1.1 src/sys/fs/msdosfs/msdosfs_rename.c cvs rdiff -u -r1.108 -r1.109 src/sys/fs/msdosfs/msdosfs_vnops.c cvs rdiff -u -r1.3 -r1.4 src/sys/modules/msdos/Makefile cvs rdiff -u -r1.5 -r1.6 src/sys/rump/fs/lib/libmsdos/Makefile 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/msdosfs/files.msdosfs diff -u src/sys/fs/msdosfs/files.msdosfs:1.3 src/sys/fs/msdosfs/files.msdosfs:1.4 --- src/sys/fs/msdosfs/files.msdosfs:1.3 Sat Feb 6 10:40:58 2016 +++ src/sys/fs/msdosfs/files.msdosfs Sat Oct 23 07:41:37 2021 @@ -1,4 +1,4 @@ -# $NetBSD: files.msdosfs,v 1.3 2016/02/06 10:40:58 mlelstv Exp $ +# $NetBSD: files.msdosfs,v 1.4 2021/10/23 07:41:37 hannken Exp $ deffs MSDOSFS @@ -7,6 +7,7 @@ file fs/msdosfs/msdosfs_conv.c msdosfs file fs/msdosfs/msdosfs_denode.c msdosfs file fs/msdosfs/msdosfs_fat.c msdosfs file fs/msdosfs/msdosfs_lookup.c msdosfs +file fs/msdosfs/msdosfs_rename.c msdosfs file fs/msdosfs/msdosfs_vfsops.c msdosfs file fs/msdosfs/msdosfs_vnops.c msdosfs file fs/msdosfs/msdosfs_unicode.c msdosfs Index: src/sys/fs/msdosfs/msdosfs_lookup.c diff -u src/sys/fs/msdosfs/msdosfs_lookup.c:1.38 src/sys/fs/msdosfs/msdosfs_lookup.c:1.39 --- src/sys/fs/msdosfs/msdosfs_lookup.c:1.38 Sat Oct 23 07:38:33 2021 +++ src/sys/fs/msdosfs/msdosfs_lookup.c Sat Oct 23 07:41:37 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: msdosfs_lookup.c,v 1.38 2021/10/23 07:38:33 hannken Exp $ */ +/* $NetBSD: msdosfs_lookup.c,v 1.39 2021/10/23 07:41:37 hannken Exp $ */ /*- * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. @@ -52,7 +52,7 @@ #endif #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: msdosfs_lookup.c,v 1.38 2021/10/23 07:38:33 hannken Exp $"); +__KERNEL_RCSID(0, "$NetBSD: msdosfs_lookup.c,v 1.39 2021/10/23 07:41:37 hannken Exp $"); #include <sys/param.h> @@ -830,116 +830,6 @@ dosdirempty(struct denode *dep) } /* - * Check to see if the directory described by target is in some - * subdirectory of source. This prevents something like the following from - * succeeding and leaving a bunch or files and directories orphaned. mv - * /a/b/c /a/b/c/d/e/f Where c and f are directories. - * - * source - the inode for /a/b/c - * target - the inode for /a/b/c/d/e/f - * - * Returns 0 if target is NOT a subdirectory of source. - * Otherwise returns a non-zero error number. - * The target inode is always unlocked on return. - */ -int -doscheckpath(struct denode *source, struct denode *target) -{ - u_long scn; - struct msdosfsmount *pmp; - struct direntry *ep; - struct denode *dep; - struct buf *bp = NULL; - int error = 0; - - dep = target; - if ((target->de_Attributes & ATTR_DIRECTORY) == 0 || - (source->de_Attributes & ATTR_DIRECTORY) == 0) { - error = ENOTDIR; - goto out; - } - if (dep->de_StartCluster == source->de_StartCluster) { - error = EEXIST; - goto out; - } - if (dep->de_StartCluster == MSDOSFSROOT) - goto out; - pmp = dep->de_pmp; -#ifdef DIAGNOSTIC - if (pmp != source->de_pmp) - panic("doscheckpath: source and target on different filesystems"); -#endif - if (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk) - goto out; - - for (;;) { - if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) { - error = ENOTDIR; - break; - } - scn = dep->de_StartCluster; - error = bread(pmp->pm_devvp, de_bn2kb(pmp, cntobn(pmp, scn)), - pmp->pm_bpcluster, 0, &bp); - if (error) - break; - - ep = (struct direntry *) bp->b_data + 1; - if ((ep->deAttributes & ATTR_DIRECTORY) == 0 || - memcmp(ep->deName, ".. ", 11) != 0) { - error = ENOTDIR; - break; - } - scn = getushort(ep->deStartCluster); - if (FAT32(pmp)) - scn |= getushort(ep->deHighClust) << 16; - - if (scn == source->de_StartCluster) { - error = EINVAL; - break; - } - if (scn == MSDOSFSROOT) - break; - if (FAT32(pmp) && scn == pmp->pm_rootdirblk) { - /* - * scn should be 0 in this case, - * but we silently ignore the error. - */ - break; - } - - vput(DETOV(dep)); - brelse(bp, 0); - bp = NULL; -#ifdef MAKEFS - /* NOTE: deget() clears dep on error */ - if ((error = deget(pmp, scn, 0, &dep)) != 0) - break; -#else - struct vnode *vp; - - dep = NULL; - error = deget(pmp, scn, 0, &vp); - if (error) - break; - error = vn_lock(vp, LK_EXCLUSIVE); - if (error) { - vrele(vp); - break; - } - dep = VTODE(vp); -#endif - } -out: - if (bp) - brelse(bp, 0); - if (error == ENOTDIR) - printf("doscheckpath(): .. not a directory?\n"); - if (dep != NULL) - vput(DETOV(dep)); - return (error); -} - -/* * Read in the disk block containing the directory entry (dirclu, dirofs) * and return the address of the buf header, and the address of the * directory entry within the block. Index: src/sys/fs/msdosfs/msdosfs_vnops.c diff -u src/sys/fs/msdosfs/msdosfs_vnops.c:1.108 src/sys/fs/msdosfs/msdosfs_vnops.c:1.109 --- src/sys/fs/msdosfs/msdosfs_vnops.c:1.108 Sat Oct 23 07:38:33 2021 +++ src/sys/fs/msdosfs/msdosfs_vnops.c Sat Oct 23 07:41:37 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: msdosfs_vnops.c,v 1.108 2021/10/23 07:38:33 hannken Exp $ */ +/* $NetBSD: msdosfs_vnops.c,v 1.109 2021/10/23 07:41:37 hannken Exp $ */ /*- * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. @@ -48,7 +48,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.108 2021/10/23 07:38:33 hannken Exp $"); +__KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.109 2021/10/23 07:41:37 hannken Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -739,389 +739,6 @@ msdosfs_remove(void *v) return (error); } -/* - * Renames on files require moving the denode to a new hash queue since the - * denode's location is used to compute which hash queue to put the file - * in. Unless it is a rename in place. For example "mv a b". - * - * What follows is the basic algorithm: - * - * if (file move) { - * if (dest file exists) { - * remove dest file - * } - * if (dest and src in same directory) { - * rewrite name in existing directory slot - * } else { - * write new entry in dest directory - * update offset and dirclust in denode - * move denode to new hash chain - * clear old directory entry - * } - * } else { - * directory move - * if (dest directory exists) { - * if (dest is not empty) { - * return ENOTEMPTY - * } - * remove dest directory - * } - * if (dest and src in same directory) { - * rewrite name in existing entry - * } else { - * be sure dest is not a child of src directory - * write entry in dest directory - * update "." and ".." in moved directory - * update offset and dirclust in denode - * move denode to new hash chain - * clear old directory entry for moved directory - * } - * } - * - * On entry: - * source's parent directory is unlocked - * source file or directory is unlocked - * destination's parent directory is locked - * destination file or directory is locked if it exists - * - * On exit: - * all denodes should be released - * - * Notes: - * I'm not sure how the memory containing the pathnames pointed at by the - * componentname structures is freed, there may be some memory bleeding - * for each rename done. - * - * --More-- Notes: - * This routine needs help. badly. - */ -int -msdosfs_rename(void *v) -{ - struct vop_rename_args /* { - struct vnode *a_fdvp; - struct vnode *a_fvp; - struct componentname *a_fcnp; - struct vnode *a_tdvp; - struct vnode *a_tvp; - struct componentname *a_tcnp; - } */ *ap = v; - struct vnode *tvp = ap->a_tvp; - struct vnode *tdvp = ap->a_tdvp; - struct vnode *fvp = ap->a_fvp; - struct vnode *fdvp = ap->a_fdvp; - struct componentname *tcnp = ap->a_tcnp; - struct componentname *fcnp = ap->a_fcnp; - struct denode *ip, *xp, *dp, *zp; - u_char toname[12], oldname[12]; - u_long from_diroffset, to_diroffset; - u_char to_count; - int doingdirectory = 0, newparent = 0; - int error; - u_long cn; - daddr_t bn; - struct msdosfsmount *pmp; - struct direntry *dotdotp; - struct buf *bp; - - pmp = VFSTOMSDOSFS(fdvp->v_mount); - - /* - * Check for cross-device rename. - */ - if ((fvp->v_mount != tdvp->v_mount) || - (tvp && (fvp->v_mount != tvp->v_mount))) { - error = EXDEV; -abortit: - VOP_ABORTOP(tdvp, tcnp); - if (tdvp == tvp) - vrele(tdvp); - else - vput(tdvp); - if (tvp) - vput(tvp); - VOP_ABORTOP(fdvp, fcnp); - vrele(fdvp); - vrele(fvp); - return (error); - } - - /* - * If source and dest are the same, do nothing. - */ - if (tvp == fvp) { - error = 0; - goto abortit; - } - - /* - * XXX: This can deadlock since we hold tdvp/tvp locked. - * But I'm not going to fix it now. - */ - if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0) - goto abortit; - dp = VTODE(fdvp); - ip = VTODE(fvp); - - /* - * Be sure we are not renaming ".", "..", or an alias of ".". This - * leads to a crippled directory tree. It's pretty tough to do a - * "ls" or "pwd" with the "." directory entry missing, and "cd .." - * doesn't work if the ".." entry is missing. - */ - if (ip->de_Attributes & ATTR_DIRECTORY) { - /* - * Avoid ".", "..", and aliases of "." for obvious reasons. - */ - if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') || - dp == ip || - (fcnp->cn_flags & ISDOTDOT) || - (tcnp->cn_flags & ISDOTDOT) || - (ip->de_flag & DE_RENAME)) { - VOP_UNLOCK(fvp); - error = EINVAL; - goto abortit; - } - ip->de_flag |= DE_RENAME; - doingdirectory++; - } - VN_KNOTE(fdvp, NOTE_WRITE); /* XXXLUKEM/XXX: right place? */ - - /* - * When the target exists, both the directory - * and target vnodes are returned locked. - */ - dp = VTODE(tdvp); - xp = tvp ? VTODE(tvp) : NULL; - /* - * Remember direntry place to use for destination - */ - to_diroffset = dp->de_crap.mlr_fndoffset; - to_count = dp->de_crap.mlr_fndcnt; - - /* - * If ".." must be changed (ie the directory gets a new - * parent) then the source directory must not be in the - * directory hierarchy above the target, as this would - * orphan everything below the source directory. Also - * the user must have write permission in the source so - * as to be able to change "..". We must repeat the call - * to namei, as the parent directory is unlocked by the - * call to doscheckpath(). - */ - error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred); - VOP_UNLOCK(fvp); - if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster) - newparent = 1; - - if (doingdirectory && newparent) { - if (error) /* write access check above */ - goto tdvpbad; - if (xp != NULL) - vput(tvp); - tvp = NULL; - /* - * doscheckpath() vput()'s tdvp (dp == VTODE(tdvp)), - * so we have to get an extra ref to it first, and - * because it's been unlocked we need to do a relookup - * afterwards in case tvp has changed. - */ - vref(tdvp); - if ((error = doscheckpath(ip, dp)) != 0) - goto bad; - vn_lock(tdvp, LK_EXCLUSIVE | LK_RETRY); - if ((error = relookup(tdvp, &tvp, tcnp, 0)) != 0) { - VOP_UNLOCK(tdvp); - goto bad; - } - dp = VTODE(tdvp); - xp = tvp ? VTODE(tvp) : NULL; - } - - if (xp != NULL) { - /* - * Target must be empty if a directory and have no links - * to it. Also, ensure source and target are compatible - * (both directories, or both not directories). - */ - if (xp->de_Attributes & ATTR_DIRECTORY) { - if (!dosdirempty(xp)) { - error = ENOTEMPTY; - goto tdvpbad; - } - if (!doingdirectory) { - error = ENOTDIR; - goto tdvpbad; - } - } else if (doingdirectory) { - error = EISDIR; - goto tdvpbad; - } - if ((error = removede(dp, xp, &dp->de_crap)) != 0) - goto tdvpbad; - VN_KNOTE(tdvp, NOTE_WRITE); - VN_KNOTE(tvp, NOTE_DELETE); - cache_purge(tvp); - vput(tvp); - tvp = NULL; - xp = NULL; - } - - /* - * Convert the filename in tcnp into a dos filename. We copy this - * into the denode and directory entry for the destination - * file/directory. - */ - if ((error = uniqdosname(VTODE(tdvp), tcnp, toname)) != 0) { - goto abortit; - } - - /* - * Since from wasn't locked at various places above, - * have to do a relookup here. - */ - fcnp->cn_flags &= ~MODMASK; - fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; - VOP_UNLOCK(tdvp); - vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY); - if ((error = relookup(fdvp, &fvp, fcnp, 0))) { - VOP_UNLOCK(fdvp); - vrele(ap->a_fvp); - vrele(tdvp); - return (error); - } - if (fvp == NULL) { - /* - * From name has disappeared. - */ - if (doingdirectory) - panic("rename: lost dir entry"); - vput(fdvp); - vrele(ap->a_fvp); - vrele(tdvp); - return 0; - } - VOP_UNLOCK(fdvp); - xp = VTODE(fvp); - zp = VTODE(fdvp); - from_diroffset = zp->de_crap.mlr_fndoffset; - - /* - * Ensure that the directory entry still exists and has not - * changed till now. If the source is a file the entry may - * have been unlinked or renamed. In either case there is - * no further work to be done. If the source is a directory - * then it cannot have been rmdir'ed or renamed; this is - * prohibited by the DE_RENAME flag. - */ - if (xp != ip) { - if (doingdirectory) - panic("rename: lost dir entry"); - vrele(ap->a_fvp); - xp = NULL; - } else { - vrele(fvp); - xp = NULL; - - /* - * First write a new entry in the destination - * directory and mark the entry in the source directory - * as deleted. Then move the denode to the correct hash - * chain for its new location in the filesystem. And, if - * we moved a directory, then update its .. entry to point - * to the new parent directory. - */ - memcpy(oldname, ip->de_Name, 11); - memcpy(ip->de_Name, toname, 11); /* update denode */ - dp->de_crap.mlr_fndoffset = to_diroffset; - dp->de_crap.mlr_fndcnt = to_count; - error = createde(ip, dp, &dp->de_crap, (struct denode **)0, - tcnp); - if (error) { - memcpy(ip->de_Name, oldname, 11); - VOP_UNLOCK(fvp); - goto bad; - } - ip->de_refcnt++; - zp->de_crap.mlr_fndoffset = from_diroffset; - if ((error = removede(zp, ip, &zp->de_crap)) != 0) { - /* XXX should really panic here, fs is corrupt */ - VOP_UNLOCK(fvp); - goto bad; - } - cache_purge(fvp); - if (!doingdirectory) { - struct denode_key old_key = ip->de_key; - struct denode_key new_key = ip->de_key; - - error = pcbmap(dp, de_cluster(pmp, to_diroffset), 0, - &new_key.dk_dirclust, 0); - if (error) { - /* XXX should really panic here, fs is corrupt */ - VOP_UNLOCK(fvp); - goto bad; - } - new_key.dk_diroffset = to_diroffset; - if (new_key.dk_dirclust != MSDOSFSROOT) - new_key.dk_diroffset &= pmp->pm_crbomask; - vcache_rekey_enter(pmp->pm_mountp, fvp, &old_key, - sizeof(old_key), &new_key, sizeof(new_key)); - ip->de_key = new_key; - vcache_rekey_exit(pmp->pm_mountp, fvp, &old_key, - sizeof(old_key), &ip->de_key, sizeof(ip->de_key)); - } - } - - /* - * If we moved a directory to a new parent directory, then we must - * fixup the ".." entry in the moved directory. - */ - if (doingdirectory && newparent) { - cn = ip->de_StartCluster; - if (cn == MSDOSFSROOT) { - /* this should never happen */ - panic("msdosfs_rename: updating .. in root directory?"); - } else - bn = cntobn(pmp, cn); - error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), - pmp->pm_bpcluster, B_MODIFY, &bp); - if (error) { - /* XXX should really panic here, fs is corrupt */ - VOP_UNLOCK(fvp); - goto bad; - } - dotdotp = (struct direntry *)bp->b_data + 1; - putushort(dotdotp->deStartCluster, dp->de_StartCluster); - if (FAT32(pmp)) { - putushort(dotdotp->deHighClust, - dp->de_StartCluster >> 16); - } else { - putushort(dotdotp->deHighClust, 0); - } - if ((error = bwrite(bp)) != 0) { - /* XXX should really panic here, fs is corrupt */ - VOP_UNLOCK(fvp); - goto bad; - } - } - - VN_KNOTE(fvp, NOTE_RENAME); - VOP_UNLOCK(fvp); -bad: - if (tvp) - vput(tvp); - vrele(tdvp); - ip->de_flag &= ~DE_RENAME; - vrele(fdvp); - vrele(fvp); - return (error); - - /* XXX: uuuh */ -tdvpbad: - VOP_UNLOCK(tdvp); - goto bad; -} - static const struct { struct direntry dot; struct direntry dotdot; Index: src/sys/modules/msdos/Makefile diff -u src/sys/modules/msdos/Makefile:1.3 src/sys/modules/msdos/Makefile:1.4 --- src/sys/modules/msdos/Makefile:1.3 Sun Feb 17 04:05:54 2019 +++ src/sys/modules/msdos/Makefile Sat Oct 23 07:41:37 2021 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.3 2019/02/17 04:05:54 rin Exp $ +# $NetBSD: Makefile,v 1.4 2021/10/23 07:41:37 hannken Exp $ .include "../Makefile.inc" @@ -6,7 +6,7 @@ KMOD= msdos SRCS= msdosfs_conv.c msdosfs_denode.c msdosfs_fat.c msdosfs_lookup.c \ - msdosfs_vfsops.c msdosfs_vnops.c msdosfs_unicode.c + msdosfs_rename.c msdosfs_vfsops.c msdosfs_vnops.c msdosfs_unicode.c WARNS= 3 Index: src/sys/rump/fs/lib/libmsdos/Makefile diff -u src/sys/rump/fs/lib/libmsdos/Makefile:1.5 src/sys/rump/fs/lib/libmsdos/Makefile:1.6 --- src/sys/rump/fs/lib/libmsdos/Makefile:1.5 Sun Sep 6 07:20:30 2020 +++ src/sys/rump/fs/lib/libmsdos/Makefile Sat Oct 23 07:41:38 2021 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.5 2020/09/06 07:20:30 mrg Exp $ +# $NetBSD: Makefile,v 1.6 2021/10/23 07:41:38 hannken Exp $ # .PATH: ${.CURDIR}/../../../../fs/msdosfs @@ -7,7 +7,7 @@ LIB= rumpfs_msdos COMMENT=FAT SRCS= msdosfs_conv.c msdosfs_fat.c msdosfs_vfsops.c msdosfs_denode.c \ - msdosfs_lookup.c msdosfs_vnops.c msdosfs_unicode.c + msdosfs_lookup.c msdosfs_rename.c msdosfs_vnops.c msdosfs_unicode.c CWARNFLAGS.gcc+= ${GCC_NO_ADDR_OF_PACKED_MEMBER} Added files: Index: src/sys/fs/msdosfs/msdosfs_rename.c diff -u /dev/null src/sys/fs/msdosfs/msdosfs_rename.c:1.1 --- /dev/null Sat Oct 23 07:41:38 2021 +++ src/sys/fs/msdosfs/msdosfs_rename.c Sat Oct 23 07:41:37 2021 @@ -0,0 +1,513 @@ +/* $NetBSD: msdosfs_rename.c,v 1.1 2021/10/23 07:41:37 hannken Exp $ */ + +/*- + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (pa...@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Written by Paul Popelka (pa...@uts.amdahl.com) + * + * You can do anything you want with this software, just don't say you wrote + * it, and don't remove this notice. + * + * This software is provided "as is". + * + * The author supplies this software to be publicly redistributed on the + * understanding that the author is not responsible for the correct + * functioning of this software in any circumstances and is not liable for + * any damages caused by this software. + * + * October 1992 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/namei.h> +#include <sys/resourcevar.h> /* defines plimit structure in proc struct */ +#include <sys/kernel.h> +#include <sys/file.h> /* define FWRITE ... */ +#include <sys/stat.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/mount.h> +#include <sys/vnode.h> +#include <sys/signalvar.h> +#include <sys/malloc.h> +#include <sys/dirent.h> +#include <sys/lockf.h> +#include <sys/kauth.h> + +#include <miscfs/genfs/genfs.h> +#include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */ + +#include <uvm/uvm_extern.h> + +#include <fs/msdosfs/bpb.h> +#include <fs/msdosfs/direntry.h> +#include <fs/msdosfs/denode.h> +#include <fs/msdosfs/msdosfsmount.h> +#include <fs/msdosfs/fat.h> + +int +msdosfs_rename(void *v) +{ + struct vop_rename_args /* { + struct vnode *a_fdvp; + struct vnode *a_fvp; + struct componentname *a_fcnp; + struct vnode *a_tdvp; + struct vnode *a_tvp; + struct componentname *a_tcnp; + } */ *ap = v; + struct vnode *tvp = ap->a_tvp; + struct vnode *tdvp = ap->a_tdvp; + struct vnode *fvp = ap->a_fvp; + struct vnode *fdvp = ap->a_fdvp; + struct componentname *tcnp = ap->a_tcnp; + struct componentname *fcnp = ap->a_fcnp; + struct denode *ip, *xp, *dp, *zp; + u_char toname[12], oldname[12]; + u_long from_diroffset, to_diroffset; + u_char to_count; + int doingdirectory = 0, newparent = 0; + int error; + u_long cn; + daddr_t bn; + struct msdosfsmount *pmp; + struct direntry *dotdotp; + struct buf *bp; + + pmp = VFSTOMSDOSFS(fdvp->v_mount); + + /* + * Check for cross-device rename. + */ + if ((fvp->v_mount != tdvp->v_mount) || + (tvp && (fvp->v_mount != tvp->v_mount))) { + error = EXDEV; +abortit: + VOP_ABORTOP(tdvp, tcnp); + if (tdvp == tvp) + vrele(tdvp); + else + vput(tdvp); + if (tvp) + vput(tvp); + VOP_ABORTOP(fdvp, fcnp); + vrele(fdvp); + vrele(fvp); + return (error); + } + + /* + * If source and dest are the same, do nothing. + */ + if (tvp == fvp) { + error = 0; + goto abortit; + } + + /* + * XXX: This can deadlock since we hold tdvp/tvp locked. + * But I'm not going to fix it now. + */ + if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0) + goto abortit; + dp = VTODE(fdvp); + ip = VTODE(fvp); + + /* + * Be sure we are not renaming ".", "..", or an alias of ".". This + * leads to a crippled directory tree. It's pretty tough to do a + * "ls" or "pwd" with the "." directory entry missing, and "cd .." + * doesn't work if the ".." entry is missing. + */ + if (ip->de_Attributes & ATTR_DIRECTORY) { + /* + * Avoid ".", "..", and aliases of "." for obvious reasons. + */ + if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') || + dp == ip || + (fcnp->cn_flags & ISDOTDOT) || + (tcnp->cn_flags & ISDOTDOT) || + (ip->de_flag & DE_RENAME)) { + VOP_UNLOCK(fvp); + error = EINVAL; + goto abortit; + } + ip->de_flag |= DE_RENAME; + doingdirectory++; + } + VN_KNOTE(fdvp, NOTE_WRITE); /* XXXLUKEM/XXX: right place? */ + + /* + * When the target exists, both the directory + * and target vnodes are returned locked. + */ + dp = VTODE(tdvp); + xp = tvp ? VTODE(tvp) : NULL; + /* + * Remember direntry place to use for destination + */ + to_diroffset = dp->de_crap.mlr_fndoffset; + to_count = dp->de_crap.mlr_fndcnt; + + /* + * If ".." must be changed (ie the directory gets a new + * parent) then the source directory must not be in the + * directory hierarchy above the target, as this would + * orphan everything below the source directory. Also + * the user must have write permission in the source so + * as to be able to change "..". We must repeat the call + * to namei, as the parent directory is unlocked by the + * call to doscheckpath(). + */ + error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred); + VOP_UNLOCK(fvp); + if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster) + newparent = 1; + + if (doingdirectory && newparent) { + if (error) /* write access check above */ + goto tdvpbad; + if (xp != NULL) + vput(tvp); + tvp = NULL; + /* + * doscheckpath() vput()'s tdvp (dp == VTODE(tdvp)), + * so we have to get an extra ref to it first, and + * because it's been unlocked we need to do a relookup + * afterwards in case tvp has changed. + */ + vref(tdvp); + if ((error = doscheckpath(ip, dp)) != 0) + goto bad; + vn_lock(tdvp, LK_EXCLUSIVE | LK_RETRY); + if ((error = relookup(tdvp, &tvp, tcnp, 0)) != 0) { + VOP_UNLOCK(tdvp); + goto bad; + } + dp = VTODE(tdvp); + xp = tvp ? VTODE(tvp) : NULL; + } + + if (xp != NULL) { + /* + * Target must be empty if a directory and have no links + * to it. Also, ensure source and target are compatible + * (both directories, or both not directories). + */ + if (xp->de_Attributes & ATTR_DIRECTORY) { + if (!dosdirempty(xp)) { + error = ENOTEMPTY; + goto tdvpbad; + } + if (!doingdirectory) { + error = ENOTDIR; + goto tdvpbad; + } + } else if (doingdirectory) { + error = EISDIR; + goto tdvpbad; + } + if ((error = removede(dp, xp, &dp->de_crap)) != 0) + goto tdvpbad; + VN_KNOTE(tdvp, NOTE_WRITE); + VN_KNOTE(tvp, NOTE_DELETE); + cache_purge(tvp); + vput(tvp); + tvp = NULL; + xp = NULL; + } + + /* + * Convert the filename in tcnp into a dos filename. We copy this + * into the denode and directory entry for the destination + * file/directory. + */ + if ((error = uniqdosname(VTODE(tdvp), tcnp, toname)) != 0) { + goto abortit; + } + + /* + * Since from wasn't locked at various places above, + * have to do a relookup here. + */ + fcnp->cn_flags &= ~MODMASK; + fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; + VOP_UNLOCK(tdvp); + vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY); + if ((error = relookup(fdvp, &fvp, fcnp, 0))) { + VOP_UNLOCK(fdvp); + vrele(ap->a_fvp); + vrele(tdvp); + return (error); + } + if (fvp == NULL) { + /* + * From name has disappeared. + */ + if (doingdirectory) + panic("rename: lost dir entry"); + vput(fdvp); + vrele(ap->a_fvp); + vrele(tdvp); + return 0; + } + VOP_UNLOCK(fdvp); + xp = VTODE(fvp); + zp = VTODE(fdvp); + from_diroffset = zp->de_crap.mlr_fndoffset; + + /* + * Ensure that the directory entry still exists and has not + * changed till now. If the source is a file the entry may + * have been unlinked or renamed. In either case there is + * no further work to be done. If the source is a directory + * then it cannot have been rmdir'ed or renamed; this is + * prohibited by the DE_RENAME flag. + */ + if (xp != ip) { + if (doingdirectory) + panic("rename: lost dir entry"); + vrele(ap->a_fvp); + xp = NULL; + } else { + vrele(fvp); + xp = NULL; + + /* + * First write a new entry in the destination + * directory and mark the entry in the source directory + * as deleted. Then move the denode to the correct hash + * chain for its new location in the filesystem. And, if + * we moved a directory, then update its .. entry to point + * to the new parent directory. + */ + memcpy(oldname, ip->de_Name, 11); + memcpy(ip->de_Name, toname, 11); /* update denode */ + dp->de_crap.mlr_fndoffset = to_diroffset; + dp->de_crap.mlr_fndcnt = to_count; + error = createde(ip, dp, &dp->de_crap, (struct denode **)0, + tcnp); + if (error) { + memcpy(ip->de_Name, oldname, 11); + VOP_UNLOCK(fvp); + goto bad; + } + ip->de_refcnt++; + zp->de_crap.mlr_fndoffset = from_diroffset; + if ((error = removede(zp, ip, &zp->de_crap)) != 0) { + /* XXX should really panic here, fs is corrupt */ + VOP_UNLOCK(fvp); + goto bad; + } + cache_purge(fvp); + if (!doingdirectory) { + struct denode_key old_key = ip->de_key; + struct denode_key new_key = ip->de_key; + + error = pcbmap(dp, de_cluster(pmp, to_diroffset), 0, + &new_key.dk_dirclust, 0); + if (error) { + /* XXX should really panic here, fs is corrupt */ + VOP_UNLOCK(fvp); + goto bad; + } + new_key.dk_diroffset = to_diroffset; + if (new_key.dk_dirclust != MSDOSFSROOT) + new_key.dk_diroffset &= pmp->pm_crbomask; + vcache_rekey_enter(pmp->pm_mountp, fvp, &old_key, + sizeof(old_key), &new_key, sizeof(new_key)); + ip->de_key = new_key; + vcache_rekey_exit(pmp->pm_mountp, fvp, &old_key, + sizeof(old_key), &ip->de_key, sizeof(ip->de_key)); + } + } + + /* + * If we moved a directory to a new parent directory, then we must + * fixup the ".." entry in the moved directory. + */ + if (doingdirectory && newparent) { + cn = ip->de_StartCluster; + if (cn == MSDOSFSROOT) { + /* this should never happen */ + panic("msdosfs_rename: updating .. in root directory?"); + } else + bn = cntobn(pmp, cn); + error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), + pmp->pm_bpcluster, B_MODIFY, &bp); + if (error) { + /* XXX should really panic here, fs is corrupt */ + VOP_UNLOCK(fvp); + goto bad; + } + dotdotp = (struct direntry *)bp->b_data + 1; + putushort(dotdotp->deStartCluster, dp->de_StartCluster); + if (FAT32(pmp)) { + putushort(dotdotp->deHighClust, + dp->de_StartCluster >> 16); + } else { + putushort(dotdotp->deHighClust, 0); + } + if ((error = bwrite(bp)) != 0) { + /* XXX should really panic here, fs is corrupt */ + VOP_UNLOCK(fvp); + goto bad; + } + } + + VN_KNOTE(fvp, NOTE_RENAME); + VOP_UNLOCK(fvp); +bad: + if (tvp) + vput(tvp); + vrele(tdvp); + ip->de_flag &= ~DE_RENAME; + vrele(fdvp); + vrele(fvp); + return (error); + + /* XXX: uuuh */ +tdvpbad: + VOP_UNLOCK(tdvp); + goto bad; +} + +/* + * Check to see if the directory described by target is in some + * subdirectory of source. This prevents something like the following from + * succeeding and leaving a bunch or files and directories orphaned. mv + * /a/b/c /a/b/c/d/e/f Where c and f are directories. + * + * source - the inode for /a/b/c + * target - the inode for /a/b/c/d/e/f + * + * Returns 0 if target is NOT a subdirectory of source. + * Otherwise returns a non-zero error number. + * The target inode is always unlocked on return. + */ +int +doscheckpath(struct denode *source, struct denode *target) +{ + u_long scn; + struct msdosfsmount *pmp; + struct direntry *ep; + struct denode *dep; + struct buf *bp = NULL; + int error = 0; + + dep = target; + if ((target->de_Attributes & ATTR_DIRECTORY) == 0 || + (source->de_Attributes & ATTR_DIRECTORY) == 0) { + error = ENOTDIR; + goto out; + } + if (dep->de_StartCluster == source->de_StartCluster) { + error = EEXIST; + goto out; + } + if (dep->de_StartCluster == MSDOSFSROOT) + goto out; + pmp = dep->de_pmp; +#ifdef DIAGNOSTIC + if (pmp != source->de_pmp) + panic("doscheckpath: source and target on different filesystems"); +#endif + if (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk) + goto out; + + for (;;) { + if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) { + error = ENOTDIR; + break; + } + scn = dep->de_StartCluster; + error = bread(pmp->pm_devvp, de_bn2kb(pmp, cntobn(pmp, scn)), + pmp->pm_bpcluster, 0, &bp); + if (error) + break; + + ep = (struct direntry *) bp->b_data + 1; + if ((ep->deAttributes & ATTR_DIRECTORY) == 0 || + memcmp(ep->deName, ".. ", 11) != 0) { + error = ENOTDIR; + break; + } + scn = getushort(ep->deStartCluster); + if (FAT32(pmp)) + scn |= getushort(ep->deHighClust) << 16; + + if (scn == source->de_StartCluster) { + error = EINVAL; + break; + } + if (scn == MSDOSFSROOT) + break; + if (FAT32(pmp) && scn == pmp->pm_rootdirblk) { + /* + * scn should be 0 in this case, + * but we silently ignore the error. + */ + break; + } + + vput(DETOV(dep)); + brelse(bp, 0); + bp = NULL; +#ifdef MAKEFS + /* NOTE: deget() clears dep on error */ + if ((error = deget(pmp, scn, 0, &dep)) != 0) + break; +#else + struct vnode *vp; + + dep = NULL; + error = deget(pmp, scn, 0, &vp); + if (error) + break; + error = vn_lock(vp, LK_EXCLUSIVE); + if (error) { + vrele(vp); + break; + } + dep = VTODE(vp); +#endif + } +out: + if (bp) + brelse(bp, 0); + if (error == ENOTDIR) + printf("doscheckpath(): .. not a directory?\n"); + if (dep != NULL) + vput(DETOV(dep)); + return (error); +}