/* $NetBSD: msdosfs_lookup.c,v 1.37 1997/11/17 15:36:54 ws Exp $ */12/*-3* SPDX-License-Identifier: BSD-4-Clause4*5* Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.6* Copyright (C) 1994, 1995, 1997 TooLs GmbH.7* All rights reserved.8* Original code by Paul Popelka ([email protected]) (see below).9*10* Redistribution and use in source and binary forms, with or without11* modification, are permitted provided that the following conditions12* are met:13* 1. Redistributions of source code must retain the above copyright14* notice, this list of conditions and the following disclaimer.15* 2. Redistributions in binary form must reproduce the above copyright16* notice, this list of conditions and the following disclaimer in the17* documentation and/or other materials provided with the distribution.18* 3. All advertising materials mentioning features or use of this software19* must display the following acknowledgement:20* This product includes software developed by TooLs GmbH.21* 4. The name of TooLs GmbH may not be used to endorse or promote products22* derived from this software without specific prior written permission.23*24* THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR25* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES26* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.27* IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,28* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,29* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;30* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,31* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR32* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF33* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.34*/35/*-36* Written by Paul Popelka ([email protected])37*38* You can do anything you want with this software, just don't say you wrote39* it, and don't remove this notice.40*41* This software is provided "as is".42*43* The author supplies this software to be publicly redistributed on the44* understanding that the author is not responsible for the correct45* functioning of this software in any circumstances and is not liable for46* any damages caused by this software.47*48* October 199249*/5051#include <sys/param.h>52#include <sys/systm.h>53#include <sys/buf.h>54#include <sys/mount.h>55#include <sys/namei.h>56#include <sys/vnode.h>5758#include <fs/msdosfs/bpb.h>59#include <fs/msdosfs/direntry.h>60#include <fs/msdosfs/denode.h>61#include <fs/msdosfs/fat.h>62#include <fs/msdosfs/msdosfsmount.h>6364static int65msdosfs_lookup_checker(struct msdosfsmount *pmp, struct vnode *dvp,66struct denode *tdp, struct vnode **vpp)67{68struct vnode *vp;6970vp = DETOV(tdp);7172/*73* Lookup assumes that directory cannot be hardlinked.74* Corrupted msdosfs filesystem could break this assumption.75*/76if (vp == dvp) {77vput(vp);78msdosfs_integrity_error(pmp);79*vpp = NULL;80return (EBADF);81}8283*vpp = vp;84return (0);85}8687int88msdosfs_lookup(struct vop_cachedlookup_args *ap)89{9091return (msdosfs_lookup_ino(ap->a_dvp, ap->a_vpp, ap->a_cnp, NULL,92NULL));93}9495struct deget_dotdot {96u_long cluster;97int blkoff;98};99100static int101msdosfs_deget_dotdot(struct mount *mp, void *arg, int lkflags,102struct vnode **rvp)103{104struct deget_dotdot *dd_arg;105struct denode *rdp;106struct msdosfsmount *pmp;107int error;108109pmp = VFSTOMSDOSFS(mp);110dd_arg = arg;111error = deget(pmp, dd_arg->cluster, dd_arg->blkoff,112LK_EXCLUSIVE, &rdp);113if (error == 0)114*rvp = DETOV(rdp);115return (error);116}117118/*119* When we search a directory the blocks containing directory entries are120* read and examined. The directory entries contain information that would121* normally be in the inode of a unix filesystem. This means that some of122* a directory's contents may also be in memory resident denodes (sort of123* an inode). This can cause problems if we are searching while some other124* process is modifying a directory. To prevent one process from accessing125* incompletely modified directory information we depend upon being the126* sole owner of a directory block. bread/brelse provide this service.127* This being the case, when a process modifies a directory it must first128* acquire the disk block that contains the directory entry to be modified.129* Then update the disk block and the denode, and then write the disk block130* out to disk. This way disk blocks containing directory entries and in131* memory denode's will be in synch.132*/133int134msdosfs_lookup_ino(struct vnode *vdp, struct vnode **vpp, struct componentname135*cnp, daddr_t *scnp, u_long *blkoffp)136{137struct mbnambuf nb;138daddr_t bn;139int error;140int slotcount;141int slotoffset = 0;142int frcn;143u_long cluster;144u_long blkoff;145int diroff;146int blsize;147int isadir; /* ~0 if found direntry is a directory */148daddr_t scn; /* starting cluster number */149struct vnode *pdp;150struct denode *dp;151struct denode *tdp;152struct msdosfsmount *pmp;153struct buf *bp = NULL;154struct direntry *dep = NULL;155struct deget_dotdot dd_arg;156u_char dosfilename[12];157int flags = cnp->cn_flags;158int nameiop = cnp->cn_nameiop;159int unlen;160uint64_t inode1;161162int wincnt = 1;163int chksum = -1, chksum_ok;164int olddos = 1;165166#ifdef MSDOSFS_DEBUG167printf("msdosfs_lookup(): looking for %s\n", cnp->cn_nameptr);168#endif169dp = VTODE(vdp);170pmp = dp->de_pmp;171#ifdef MSDOSFS_DEBUG172printf("msdosfs_lookup(): vdp %p, dp %p, Attr %02x\n",173vdp, dp, dp->de_Attributes);174#endif175176restart:177if (vpp != NULL)178*vpp = NULL;179/*180* If they are going after the . or .. entry in the root directory,181* they won't find it. DOS filesystems don't have them in the root182* directory. So, we fake it. deget() is in on this scam too.183*/184if ((vdp->v_vflag & VV_ROOT) && cnp->cn_nameptr[0] == '.' &&185(cnp->cn_namelen == 1 ||186(cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.'))) {187isadir = ATTR_DIRECTORY;188scn = MSDOSFSROOT;189#ifdef MSDOSFS_DEBUG190printf("msdosfs_lookup(): looking for . or .. in root directory\n");191#endif192cluster = MSDOSFSROOT;193blkoff = MSDOSFSROOT_OFS;194goto foundroot;195}196197switch (unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename,198cnp->cn_namelen, 0, pmp)) {199case 0:200if (nameiop == CREATE || nameiop == RENAME)201return (EINVAL);202return (ENOENT);203case 1:204break;205case 2:206wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr,207cnp->cn_namelen, pmp) + 1;208break;209case 3:210olddos = 0;211wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr,212cnp->cn_namelen, pmp) + 1;213break;214}215if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) {216wincnt = 1;217olddos = 1;218}219unlen = winLenFixup(cnp->cn_nameptr, cnp->cn_namelen);220221/*222* Suppress search for slots unless creating223* file and at end of pathname, in which case224* we watch for a place to put the new file in225* case it doesn't already exist.226*/227slotcount = wincnt;228if ((nameiop == CREATE || nameiop == RENAME) &&229(flags & ISLASTCN))230slotcount = 0;231232#ifdef MSDOSFS_DEBUG233printf("msdosfs_lookup(): dos version of filename %s, length %ld\n",234dosfilename, cnp->cn_namelen);235#endif236/*237* Search the directory pointed at by vdp for the name pointed at238* by cnp->cn_nameptr.239*/240tdp = NULL;241mbnambuf_init(&nb);242/*243* The outer loop ranges over the clusters that make up the244* directory. Note that the root directory is different from all245* other directories. It has a fixed number of blocks that are not246* part of the pool of allocatable clusters. So, we treat it a247* little differently. The root directory starts at "cluster" 0.248*/249diroff = 0;250for (frcn = 0;; frcn++) {251error = pcbmap(dp, frcn, &bn, &cluster, &blsize);252if (error) {253if (error == E2BIG)254break;255return (error);256}257error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);258if (error) {259return (error);260}261for (blkoff = 0; blkoff < blsize;262blkoff += sizeof(struct direntry),263diroff += sizeof(struct direntry)) {264dep = (struct direntry *)(bp->b_data + blkoff);265/*266* If the slot is empty and we are still looking267* for an empty then remember this one. If the268* slot is not empty then check to see if it269* matches what we are looking for. If the slot270* has never been filled with anything, then the271* remainder of the directory has never been used,272* so there is no point in searching it.273*/274if (dep->deName[0] == SLOT_EMPTY ||275dep->deName[0] == SLOT_DELETED) {276/*277* Drop memory of previous long matches278*/279chksum = -1;280mbnambuf_init(&nb);281282if (slotcount < wincnt) {283slotcount++;284slotoffset = diroff;285}286if (dep->deName[0] == SLOT_EMPTY) {287brelse(bp);288goto notfound;289}290} else {291/*292* If there wasn't enough space for our winentries,293* forget about the empty space294*/295if (slotcount < wincnt)296slotcount = 0;297298/*299* Check for Win95 long filename entry300*/301if (dep->deAttributes == ATTR_WIN95) {302if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)303continue;304305chksum = win2unixfn(&nb,306(struct winentry *)dep, chksum,307pmp);308continue;309}310311chksum = winChkName(&nb,312(const u_char *)cnp->cn_nameptr, unlen,313chksum, pmp);314if (chksum == -2) {315chksum = -1;316continue;317}318319/*320* Ignore volume labels (anywhere, not just321* the root directory).322*/323if (dep->deAttributes & ATTR_VOLUME) {324chksum = -1;325continue;326}327328/*329* Check for a checksum or name match330*/331chksum_ok = (chksum == winChksum(dep->deName));332if (!chksum_ok333&& (!olddos || bcmp(dosfilename, dep->deName, 11))) {334chksum = -1;335continue;336}337#ifdef MSDOSFS_DEBUG338printf("msdosfs_lookup(): match blkoff %lu, diroff %d\n",339blkoff, diroff);340#endif341/*342* Remember where this directory343* entry came from for whoever did344* this lookup.345*/346dp->de_fndoffset = diroff;347if (chksum_ok && nameiop == RENAME) {348/*349* Target had correct long name350* directory entries, reuse them351* as needed.352*/353dp->de_fndcnt = wincnt - 1;354} else {355/*356* Long name directory entries357* not present or corrupt, can only358* reuse dos directory entry.359*/360dp->de_fndcnt = 0;361}362363goto found;364}365} /* for (blkoff = 0; .... */366/*367* Release the buffer holding the directory cluster just368* searched.369*/370brelse(bp);371} /* for (frcn = 0; ; frcn++) */372373notfound:374/*375* We hold no disk buffers at this point.376*/377378/*379* Fixup the slot description to point to the place where380* we might put the new DOS direntry (putting the Win95381* long name entries before that)382*/383if (!slotcount) {384slotcount = 1;385slotoffset = diroff;386}387if (wincnt > slotcount)388slotoffset += sizeof(struct direntry) * (wincnt - slotcount);389390/*391* If we get here we didn't find the entry we were looking for. But392* that's ok if we are creating or renaming and are at the end of393* the pathname and the directory hasn't been removed.394*/395#ifdef MSDOSFS_DEBUG396printf("msdosfs_lookup(): op %d, refcnt %ld\n",397nameiop, dp->de_refcnt);398printf(" slotcount %d, slotoffset %d\n",399slotcount, slotoffset);400#endif401if ((nameiop == CREATE || nameiop == RENAME) &&402(flags & ISLASTCN) && dp->de_refcnt != 0) {403/*404* Access for write is interpreted as allowing405* creation of files in the directory.406*/407error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, curthread);408if (error)409return (error);410/*411* Return an indication of where the new directory412* entry should be put.413*/414dp->de_fndoffset = slotoffset;415dp->de_fndcnt = wincnt - 1;416417/*418* We return with the directory locked, so that419* the parameters we set up above will still be420* valid if we actually decide to do a direnter().421* We return ni_vp == NULL to indicate that the entry422* does not currently exist; we leave a pointer to423* the (locked) directory inode in ndp->ni_dvp.424*425* NB - if the directory is unlocked, then this426* information cannot be used.427*/428return (EJUSTRETURN);429}430#if 0431/*432* Insert name into cache (as non-existent) if appropriate.433*434* XXX Negative caching is broken for msdosfs because the name435* cache doesn't understand peculiarities such as case insensitivity436* and 8.3 filenames. Hence, it may not invalidate all negative437* entries if a file with this name is later created.438*/439if ((cnp->cn_flags & MAKEENTRY) != 0)440cache_enter(vdp, *vpp, cnp);441#endif442return (ENOENT);443444found:445/*446* NOTE: We still have the buffer with matched directory entry at447* this point.448*/449isadir = dep->deAttributes & ATTR_DIRECTORY;450scn = getushort(dep->deStartCluster);451if (FAT32(pmp)) {452scn |= getushort(dep->deHighClust) << 16;453if (scn == pmp->pm_rootdirblk) {454/*455* There should actually be 0 here.456* Just ignore the error.457*/458scn = MSDOSFSROOT;459}460}461462if (isadir) {463cluster = scn;464if (cluster == MSDOSFSROOT)465blkoff = MSDOSFSROOT_OFS;466else467blkoff = 0;468} else if (cluster == MSDOSFSROOT)469blkoff = diroff;470471/*472* Now release buf to allow deget to read the entry again.473* Reserving it here and giving it to deget could result474* in a deadlock.475*/476brelse(bp);477bp = NULL;478479foundroot:480/*481* If we entered at foundroot, then we are looking for the . or ..482* entry of the filesystems root directory. isadir and scn were483* setup before jumping here. And, bp is already null.484*/485if (FAT32(pmp) && scn == MSDOSFSROOT)486scn = pmp->pm_rootdirblk;487488if (scnp != NULL) {489*scnp = cluster;490*blkoffp = blkoff;491return (0);492}493494/*495* If deleting, and at end of pathname, return496* parameters which can be used to remove file.497*/498if (nameiop == DELETE && (flags & ISLASTCN)) {499/*500* Don't allow deleting the root.501*/502if (blkoff == MSDOSFSROOT_OFS)503return (EBUSY);504505/*506* Write access to directory required to delete files.507*/508error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, curthread);509if (error)510return (error);511512/*513* Return pointer to current entry in dp->i_offset.514* Save directory inode pointer in ndp->ni_dvp for dirremove().515*/516if (dp->de_StartCluster == scn && isadir) { /* "." */517vref(vdp);518*vpp = vdp;519return (0);520}521error = deget(pmp, cluster, blkoff, LK_EXCLUSIVE, &tdp);522if (error)523return (error);524return (msdosfs_lookup_checker(pmp, vdp, tdp, vpp));525}526527/*528* If rewriting (RENAME), return the inode and the529* information required to rewrite the present directory530* Must get inode of directory entry to verify it's a531* regular file, or empty directory.532*/533if (nameiop == RENAME && (flags & ISLASTCN)) {534if (blkoff == MSDOSFSROOT_OFS)535return (EBUSY);536537error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, curthread);538if (error)539return (error);540541/*542* Careful about locking second inode.543* This can only occur if the target is ".".544*/545if (dp->de_StartCluster == scn && isadir)546return (EISDIR);547548if ((error = deget(pmp, cluster, blkoff, LK_EXCLUSIVE,549&tdp)) != 0)550return (error);551if ((error = msdosfs_lookup_checker(pmp, vdp, tdp, vpp))552!= 0)553return (error);554return (0);555}556557/*558* Step through the translation in the name. We do not `vput' the559* directory because we may need it again if a symbolic link560* is relative to the current directory. Instead we save it561* unlocked as "pdp". We must get the target inode before unlocking562* the directory to insure that the inode will not be removed563* before we get it. We prevent deadlock by always fetching564* inodes from the root, moving down the directory tree. Thus565* when following backward pointers ".." we must unlock the566* parent directory before getting the requested directory.567*/568pdp = vdp;569if (flags & ISDOTDOT) {570dd_arg.cluster = cluster;571dd_arg.blkoff = blkoff;572error = vn_vget_ino_gen(vdp, msdosfs_deget_dotdot,573&dd_arg, cnp->cn_lkflags, vpp);574if (error != 0) {575*vpp = NULL;576return (error);577}578/*579* Recheck that ".." still points to the inode we580* looked up before pdp lock was dropped.581*/582error = msdosfs_lookup_ino(pdp, NULL, cnp, &scn, &blkoff);583if (error) {584vput(*vpp);585*vpp = NULL;586return (error);587}588if (FAT32(pmp) && scn == MSDOSFSROOT)589scn = pmp->pm_rootdirblk;590inode1 = DETOI(pmp, scn, blkoff);591if (VTODE(*vpp)->de_inode != inode1) {592vput(*vpp);593goto restart;594}595error = msdosfs_lookup_checker(pmp, vdp, VTODE(*vpp), vpp);596if (error != 0)597return (error);598} else if (dp->de_StartCluster == scn && isadir) {599if (cnp->cn_namelen != 1 || cnp->cn_nameptr[0] != '.') {600/* fs is corrupted, non-dot lookup returned dvp */601msdosfs_integrity_error(pmp);602return (EBADF);603}604vref(vdp); /* we want ourself, ie "." */605*vpp = vdp;606} else {607if ((error = deget(pmp, cluster, blkoff, LK_EXCLUSIVE,608&tdp)) != 0)609return (error);610if ((error = msdosfs_lookup_checker(pmp, vdp, tdp, vpp)) != 0)611return (error);612}613614/*615* Insert name into cache if appropriate.616*/617if (cnp->cn_flags & MAKEENTRY)618cache_enter(vdp, *vpp, cnp);619return (0);620}621622/*623* dep - directory entry to copy into the directory624* ddep - directory to add to625* depp - return the address of the denode for the created directory entry626* if depp != 0627* cnp - componentname needed for Win95 long filenames628*/629int630createde(struct denode *dep, struct denode *ddep, struct denode **depp,631struct componentname *cnp)632{633int error;634u_long dirclust, diroffset;635struct direntry *ndep;636struct msdosfsmount *pmp = ddep->de_pmp;637struct buf *bp;638daddr_t bn;639int blsize;640641#ifdef MSDOSFS_DEBUG642printf("createde(dep %p, ddep %p, depp %p, cnp %p)\n",643dep, ddep, depp, cnp);644#endif645646/*647* If no space left in the directory then allocate another cluster648* and chain it onto the end of the file. There is one exception649* to this. That is, if the root directory has no more space it650* can NOT be expanded. extendfile() checks for and fails attempts651* to extend the root directory. We just return an error in that652* case.653*/654if (ddep->de_fndoffset >= ddep->de_FileSize) {655diroffset = ddep->de_fndoffset + sizeof(struct direntry)656- ddep->de_FileSize;657dirclust = de_clcount(pmp, diroffset);658error = extendfile(ddep, dirclust, 0, 0, DE_CLEAR);659if (error) {660(void)detrunc(ddep, ddep->de_FileSize, 0, NOCRED);661return error;662}663664/*665* Update the size of the directory666*/667ddep->de_FileSize += de_cn2off(pmp, dirclust);668}669670/*671* We just read in the cluster with space. Copy the new directory672* entry in. Then write it to disk. NOTE: DOS directories673* do not get smaller as clusters are emptied.674*/675error = pcbmap(ddep, de_cluster(pmp, ddep->de_fndoffset),676&bn, &dirclust, &blsize);677if (error)678return error;679diroffset = ddep->de_fndoffset;680if (dirclust != MSDOSFSROOT)681diroffset &= pmp->pm_crbomask;682if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp)) != 0) {683brelse(bp);684return error;685}686ndep = bptoep(pmp, bp, ddep->de_fndoffset);687rootde_alloced(ddep);688689DE_EXTERNALIZE(ndep, dep);690691/*692* Now write the Win95 long name693*/694if (ddep->de_fndcnt > 0) {695uint8_t chksum = winChksum(ndep->deName);696const u_char *un = (const u_char *)cnp->cn_nameptr;697int unlen = cnp->cn_namelen;698int cnt = 1;699700while (--ddep->de_fndcnt >= 0) {701if (!(ddep->de_fndoffset & pmp->pm_crbomask)) {702if (DOINGASYNC(DETOV(ddep)))703bdwrite(bp);704else if ((error = bwrite(bp)) != 0)705return error;706707ddep->de_fndoffset -= sizeof(struct direntry);708error = pcbmap(ddep,709de_cluster(pmp,710ddep->de_fndoffset),711&bn, 0, &blsize);712if (error)713return error;714715error = bread(pmp->pm_devvp, bn, blsize,716NOCRED, &bp);717if (error) {718return error;719}720ndep = bptoep(pmp, bp, ddep->de_fndoffset);721} else {722ndep--;723ddep->de_fndoffset -= sizeof(struct direntry);724}725rootde_alloced(ddep);726if (!unix2winfn(un, unlen, (struct winentry *)ndep,727cnt++, chksum, pmp))728break;729}730}731732if (DOINGASYNC(DETOV(ddep)))733bdwrite(bp);734else if ((error = bwrite(bp)) != 0)735return error;736737/*738* If they want us to return with the denode gotten.739*/740if (depp) {741if (dep->de_Attributes & ATTR_DIRECTORY) {742dirclust = dep->de_StartCluster;743if (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)744dirclust = MSDOSFSROOT;745if (dirclust == MSDOSFSROOT)746diroffset = MSDOSFSROOT_OFS;747else748diroffset = 0;749}750return (deget(pmp, dirclust, diroffset, LK_EXCLUSIVE, depp));751}752753return 0;754}755756/*757* Be sure a directory is empty except for "." and "..". Return 1 if empty,758* return 0 if not empty or error.759*/760int761dosdirempty(struct denode *dep)762{763int blsize;764int error;765u_long cn;766daddr_t bn;767struct buf *bp;768struct msdosfsmount *pmp = dep->de_pmp;769struct direntry *dentp;770771/*772* Since the filesize field in directory entries for a directory is773* zero, we just have to feel our way through the directory until774* we hit end of file.775*/776for (cn = 0;; cn++) {777if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) {778if (error == E2BIG)779return (1); /* it's empty */780return (0);781}782error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);783if (error) {784return (0);785}786for (dentp = (struct direntry *)bp->b_data;787(char *)dentp < bp->b_data + blsize;788dentp++) {789if (dentp->deName[0] != SLOT_DELETED &&790(dentp->deAttributes & ATTR_VOLUME) == 0) {791/*792* In dos directories an entry whose name793* starts with SLOT_EMPTY (0) starts the794* beginning of the unused part of the795* directory, so we can just return that it796* is empty.797*/798if (dentp->deName[0] == SLOT_EMPTY) {799brelse(bp);800return (1);801}802/*803* Any names other than "." and ".." in a804* directory mean it is not empty.805*/806if (bcmp(dentp->deName, ". ", 11) &&807bcmp(dentp->deName, ".. ", 11)) {808brelse(bp);809#ifdef MSDOSFS_DEBUG810printf("dosdirempty(): entry found %02x, %02x\n",811dentp->deName[0], dentp->deName[1]);812#endif813return (0); /* not empty */814}815}816}817brelse(bp);818}819/* NOTREACHED */820}821822/*823* Check to see if the directory described by target is in some824* subdirectory of source. This prevents something like the following from825* succeeding and leaving a bunch or files and directories orphaned. mv826* /a/b/c /a/b/c/d/e/f Where c and f are directories.827*828* source - the inode for /a/b/c829* target - the inode for /a/b/c/d/e/f830*831* Returns 0 if target is NOT a subdirectory of source.832* Otherwise returns a non-zero error number.833*/834int835doscheckpath(struct denode *source, struct denode *target, daddr_t *wait_scn)836{837daddr_t scn;838struct msdosfsmount *pmp;839struct direntry *ep;840struct denode *dep;841struct buf *bp = NULL;842int error = 0;843844*wait_scn = 0;845846pmp = target->de_pmp;847KASSERT(pmp == source->de_pmp,848("doscheckpath: source and target on different filesystems"));849850if ((target->de_Attributes & ATTR_DIRECTORY) == 0 ||851(source->de_Attributes & ATTR_DIRECTORY) == 0)852return (ENOTDIR);853854if (target->de_StartCluster == source->de_StartCluster)855return (EEXIST);856857if (target->de_StartCluster == MSDOSFSROOT ||858(FAT32(pmp) && target->de_StartCluster == pmp->pm_rootdirblk))859return (0);860861dep = target;862vget(DETOV(dep), LK_EXCLUSIVE);863for (;;) {864if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {865error = ENOTDIR;866break;867}868scn = dep->de_StartCluster;869error = bread(pmp->pm_devvp, cntobn(pmp, scn),870pmp->pm_bpcluster, NOCRED, &bp);871if (error != 0)872break;873874ep = (struct direntry *)bp->b_data + 1;875if ((ep->deAttributes & ATTR_DIRECTORY) == 0 ||876bcmp(ep->deName, ".. ", 11) != 0) {877error = ENOTDIR;878brelse(bp);879break;880}881882scn = getushort(ep->deStartCluster);883if (FAT32(pmp))884scn |= getushort(ep->deHighClust) << 16;885brelse(bp);886887if (scn == source->de_StartCluster) {888error = EINVAL;889break;890}891if (scn == MSDOSFSROOT)892break;893if (FAT32(pmp) && scn == pmp->pm_rootdirblk) {894/*895* scn should be 0 in this case,896* but we silently ignore the error.897*/898break;899}900901vput(DETOV(dep));902dep = NULL;903/* NOTE: deget() clears dep on error */904error = deget(pmp, scn, 0, LK_EXCLUSIVE | LK_NOWAIT, &dep);905if (error != 0) {906*wait_scn = scn;907break;908}909}910#ifdef MSDOSFS_DEBUG911if (error == ENOTDIR)912printf("doscheckpath(): .. not a directory?\n");913#endif914if (dep != NULL)915vput(DETOV(dep));916return (error);917}918919/*920* Read in the disk block containing the directory entry (dirclu, dirofs)921* and return the address of the buf header, and the address of the922* directory entry within the block.923*/924int925readep(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset,926struct buf **bpp, struct direntry **epp)927{928int error;929daddr_t bn;930int blsize;931932blsize = pmp->pm_bpcluster;933if (dirclust == MSDOSFSROOT934&& de_blk(pmp, diroffset + blsize) > pmp->pm_rootdirsize)935blsize = de_bn2off(pmp, pmp->pm_rootdirsize) & pmp->pm_crbomask;936bn = detobn(pmp, dirclust, diroffset);937if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, bpp)) != 0) {938brelse(*bpp);939*bpp = NULL;940return (error);941}942if (epp)943*epp = bptoep(pmp, *bpp, diroffset);944return (0);945}946947/*948* Read in the disk block containing the directory entry dep came from and949* return the address of the buf header, and the address of the directory950* entry within the block.951*/952int953readde(struct denode *dep, struct buf **bpp, struct direntry **epp)954{955956return (readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset,957bpp, epp));958}959960/*961* Remove a directory entry. At this point the file represented by the962* directory entry to be removed is still full length until no one has it963* open. When the file no longer being used msdosfs_inactive() is called964* and will truncate the file to 0 length. When the vnode containing the965* denode is needed for some other purpose by VFS it will call966* msdosfs_reclaim() which will remove the denode from the denode cache.967*968* pdep directory where the entry is removed969* dep file to be removed970*/971int972removede(struct denode *pdep, struct denode *dep)973{974int error;975struct direntry *ep;976struct buf *bp;977daddr_t bn;978int blsize;979struct msdosfsmount *pmp = pdep->de_pmp;980u_long offset = pdep->de_fndoffset;981982#ifdef MSDOSFS_DEBUG983printf("removede(): filename %s, dep %p, offset %08lx\n",984dep->de_Name, dep, offset);985#endif986987dep->de_refcnt--;988offset += sizeof(struct direntry);989do {990offset -= sizeof(struct direntry);991error = pcbmap(pdep, de_cluster(pmp, offset), &bn, 0, &blsize);992if (error)993return error;994error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);995if (error) {996return error;997}998ep = bptoep(pmp, bp, offset);999/*1000* Check whether, if we came here the second time, i.e.1001* when underflowing into the previous block, the last1002* entry in this block is a longfilename entry, too.1003*/1004if (ep->deAttributes != ATTR_WIN951005&& offset != pdep->de_fndoffset) {1006brelse(bp);1007break;1008}1009offset += sizeof(struct direntry);1010while (1) {1011/*1012* We are a bit aggressive here in that we delete any Win951013* entries preceding this entry, not just the ones we "own".1014* Since these presumably aren't valid anyway,1015* there should be no harm.1016*/1017offset -= sizeof(struct direntry);1018ep--->deName[0] = SLOT_DELETED;1019rootde_freed(pdep);1020if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95)1021|| !(offset & pmp->pm_crbomask)1022|| ep->deAttributes != ATTR_WIN95)1023break;1024}1025if (DOINGASYNC(DETOV(pdep)))1026bdwrite(bp);1027else if ((error = bwrite(bp)) != 0)1028return error;1029} while (!(pmp->pm_flags & MSDOSFSMNT_NOWIN95)1030&& !(offset & pmp->pm_crbomask)1031&& offset);1032return 0;1033}10341035/*1036* Create a unique DOS name in dvp1037*/1038int1039uniqdosname(struct denode *dep, struct componentname *cnp, u_char *cp)1040{1041struct msdosfsmount *pmp = dep->de_pmp;1042struct direntry *dentp;1043int gen;1044int blsize;1045u_long cn;1046daddr_t bn;1047struct buf *bp;1048int error;10491050if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)1051return (unix2dosfn((const u_char *)cnp->cn_nameptr, cp,1052cnp->cn_namelen, 0, pmp) ? 0 : EINVAL);10531054for (gen = 1;; gen++) {1055/*1056* Generate DOS name with generation number1057*/1058if (!unix2dosfn((const u_char *)cnp->cn_nameptr, cp,1059cnp->cn_namelen, gen, pmp))1060return gen == 1 ? EINVAL : EEXIST;10611062/*1063* Now look for a dir entry with this exact name1064*/1065for (cn = error = 0; !error; cn++) {1066if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) {1067if (error == E2BIG) /* EOF reached and not found */1068return 0;1069return error;1070}1071error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);1072if (error) {1073return error;1074}1075for (dentp = (struct direntry *)bp->b_data;1076(char *)dentp < bp->b_data + blsize;1077dentp++) {1078if (dentp->deName[0] == SLOT_EMPTY) {1079/*1080* Last used entry and not found1081*/1082brelse(bp);1083return 0;1084}1085/*1086* Ignore volume labels and Win95 entries1087*/1088if (dentp->deAttributes & ATTR_VOLUME)1089continue;1090if (!bcmp(dentp->deName, cp, 11)) {1091error = EEXIST;1092break;1093}1094}1095brelse(bp);1096}1097}1098}109911001101