1952a6212SJordan K. Hubbard /* $Id: msdosfs_lookup.c,v 1.14 1997/09/10 19:44:36 phk Exp $ */ 2952a6212SJordan K. Hubbard /* $NetBSD: msdosfs_lookup.c,v 1.37 1997/11/17 15:36:54 ws Exp $ */ 327a0bc89SDoug Rabson 427a0bc89SDoug Rabson /*- 5952a6212SJordan K. Hubbard * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. 6952a6212SJordan K. Hubbard * Copyright (C) 1994, 1995, 1997 TooLs GmbH. 727a0bc89SDoug Rabson * All rights reserved. 827a0bc89SDoug Rabson * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). 927a0bc89SDoug Rabson * 1027a0bc89SDoug Rabson * Redistribution and use in source and binary forms, with or without 1127a0bc89SDoug Rabson * modification, are permitted provided that the following conditions 1227a0bc89SDoug Rabson * are met: 1327a0bc89SDoug Rabson * 1. Redistributions of source code must retain the above copyright 1427a0bc89SDoug Rabson * notice, this list of conditions and the following disclaimer. 1527a0bc89SDoug Rabson * 2. Redistributions in binary form must reproduce the above copyright 1627a0bc89SDoug Rabson * notice, this list of conditions and the following disclaimer in the 1727a0bc89SDoug Rabson * documentation and/or other materials provided with the distribution. 1827a0bc89SDoug Rabson * 3. All advertising materials mentioning features or use of this software 1927a0bc89SDoug Rabson * must display the following acknowledgement: 2027a0bc89SDoug Rabson * This product includes software developed by TooLs GmbH. 2127a0bc89SDoug Rabson * 4. The name of TooLs GmbH may not be used to endorse or promote products 2227a0bc89SDoug Rabson * derived from this software without specific prior written permission. 2327a0bc89SDoug Rabson * 2427a0bc89SDoug Rabson * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 2527a0bc89SDoug Rabson * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2627a0bc89SDoug Rabson * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2727a0bc89SDoug Rabson * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2827a0bc89SDoug Rabson * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 2927a0bc89SDoug Rabson * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 3027a0bc89SDoug Rabson * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 3127a0bc89SDoug Rabson * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 3227a0bc89SDoug Rabson * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 3327a0bc89SDoug Rabson * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3427a0bc89SDoug Rabson */ 3527a0bc89SDoug Rabson /* 3627a0bc89SDoug Rabson * Written by Paul Popelka (paulp@uts.amdahl.com) 3727a0bc89SDoug Rabson * 3827a0bc89SDoug Rabson * You can do anything you want with this software, just don't say you wrote 3927a0bc89SDoug Rabson * it, and don't remove this notice. 4027a0bc89SDoug Rabson * 4127a0bc89SDoug Rabson * This software is provided "as is". 4227a0bc89SDoug Rabson * 4327a0bc89SDoug Rabson * The author supplies this software to be publicly redistributed on the 4427a0bc89SDoug Rabson * understanding that the author is not responsible for the correct 4527a0bc89SDoug Rabson * functioning of this software in any circumstances and is not liable for 4627a0bc89SDoug Rabson * any damages caused by this software. 4727a0bc89SDoug Rabson * 4827a0bc89SDoug Rabson * October 1992 4927a0bc89SDoug Rabson */ 5027a0bc89SDoug Rabson 5127a0bc89SDoug Rabson #include <sys/param.h> 5227a0bc89SDoug Rabson #include <sys/namei.h> 5327a0bc89SDoug Rabson #include <sys/buf.h> 5427a0bc89SDoug Rabson #include <sys/vnode.h> 5527a0bc89SDoug Rabson #include <sys/mount.h> 56c3c6d51eSPoul-Henning Kamp #include <sys/systm.h> 5727a0bc89SDoug Rabson 5827a0bc89SDoug Rabson #include <msdosfs/bpb.h> 5927a0bc89SDoug Rabson #include <msdosfs/direntry.h> 6027a0bc89SDoug Rabson #include <msdosfs/denode.h> 6127a0bc89SDoug Rabson #include <msdosfs/msdosfsmount.h> 6227a0bc89SDoug Rabson #include <msdosfs/fat.h> 6327a0bc89SDoug Rabson 6458c27bcfSBruce Evans static int markdeleted __P((struct msdosfsmount *pmp, u_long dirclust, 6558c27bcfSBruce Evans u_long diroffset)); 6658c27bcfSBruce Evans 6727a0bc89SDoug Rabson /* 6827a0bc89SDoug Rabson * When we search a directory the blocks containing directory entries are 6927a0bc89SDoug Rabson * read and examined. The directory entries contain information that would 7027a0bc89SDoug Rabson * normally be in the inode of a unix filesystem. This means that some of 7127a0bc89SDoug Rabson * a directory's contents may also be in memory resident denodes (sort of 7227a0bc89SDoug Rabson * an inode). This can cause problems if we are searching while some other 7327a0bc89SDoug Rabson * process is modifying a directory. To prevent one process from accessing 7427a0bc89SDoug Rabson * incompletely modified directory information we depend upon being the 75b84d2921SPoul-Henning Kamp * sole owner of a directory block. bread/brelse provide this service. 7627a0bc89SDoug Rabson * This being the case, when a process modifies a directory it must first 7727a0bc89SDoug Rabson * acquire the disk block that contains the directory entry to be modified. 7827a0bc89SDoug Rabson * Then update the disk block and the denode, and then write the disk block 7927a0bc89SDoug Rabson * out to disk. This way disk blocks containing directory entries and in 8027a0bc89SDoug Rabson * memory denode's will be in synch. 8127a0bc89SDoug Rabson */ 8227a0bc89SDoug Rabson int 8327a0bc89SDoug Rabson msdosfs_lookup(ap) 840fa2443fSPoul-Henning Kamp struct vop_cachedlookup_args /* { 8527a0bc89SDoug Rabson struct vnode *a_dvp; 8627a0bc89SDoug Rabson struct vnode **a_vpp; 8727a0bc89SDoug Rabson struct componentname *a_cnp; 8827a0bc89SDoug Rabson } */ *ap; 8927a0bc89SDoug Rabson { 9027a0bc89SDoug Rabson struct vnode *vdp = ap->a_dvp; 9127a0bc89SDoug Rabson struct vnode **vpp = ap->a_vpp; 9227a0bc89SDoug Rabson struct componentname *cnp = ap->a_cnp; 9327a0bc89SDoug Rabson daddr_t bn; 9427a0bc89SDoug Rabson int error; 9527a0bc89SDoug Rabson int lockparent; 9627a0bc89SDoug Rabson int wantparent; 97952a6212SJordan K. Hubbard int slotcount; 98952a6212SJordan K. Hubbard int slotoffset = 0; 9927a0bc89SDoug Rabson int frcn; 10027a0bc89SDoug Rabson u_long cluster; 101952a6212SJordan K. Hubbard int blkoff; 10227a0bc89SDoug Rabson int diroff; 103952a6212SJordan K. Hubbard int blsize; 10427a0bc89SDoug Rabson int isadir; /* ~0 if found direntry is a directory */ 10527a0bc89SDoug Rabson u_long scn; /* starting cluster number */ 10627a0bc89SDoug Rabson struct vnode *pdp; 10727a0bc89SDoug Rabson struct denode *dp; 10827a0bc89SDoug Rabson struct denode *tdp; 10927a0bc89SDoug Rabson struct msdosfsmount *pmp; 11027a0bc89SDoug Rabson struct buf *bp = 0; 11127a0bc89SDoug Rabson struct direntry *dep = NULL; 112d8762fa6SBruce Evans struct ucred *cred = cnp->cn_cred; 11327a0bc89SDoug Rabson u_char dosfilename[12]; 11427a0bc89SDoug Rabson int flags = cnp->cn_flags; 11527a0bc89SDoug Rabson int nameiop = cnp->cn_nameiop; 116af3f60d5SBruce Evans struct proc *p = cnp->cn_proc; 11727a0bc89SDoug Rabson 118952a6212SJordan K. Hubbard int wincnt = 1; 119952a6212SJordan K. Hubbard int chksum = -1; 120952a6212SJordan K. Hubbard int olddos = 1; 121952a6212SJordan K. Hubbard 12227a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG 12327a0bc89SDoug Rabson printf("msdosfs_lookup(): looking for %s\n", cnp->cn_nameptr); 12427a0bc89SDoug Rabson #endif 12527a0bc89SDoug Rabson dp = VTODE(vdp); 12627a0bc89SDoug Rabson pmp = dp->de_pmp; 12727a0bc89SDoug Rabson *vpp = NULL; 12827a0bc89SDoug Rabson lockparent = flags & LOCKPARENT; 12927a0bc89SDoug Rabson wantparent = flags & (LOCKPARENT | WANTPARENT); 13027a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG 131952a6212SJordan K. Hubbard printf("msdosfs_lookup(): vdp %p, dp %p, Attr %02x\n", 13227a0bc89SDoug Rabson vdp, dp, dp->de_Attributes); 13327a0bc89SDoug Rabson #endif 13427a0bc89SDoug Rabson 13527a0bc89SDoug Rabson /* 13627a0bc89SDoug Rabson * If they are going after the . or .. entry in the root directory, 13727a0bc89SDoug Rabson * they won't find it. DOS filesystems don't have them in the root 13827a0bc89SDoug Rabson * directory. So, we fake it. deget() is in on this scam too. 13927a0bc89SDoug Rabson */ 14027a0bc89SDoug Rabson if ((vdp->v_flag & VROOT) && cnp->cn_nameptr[0] == '.' && 14127a0bc89SDoug Rabson (cnp->cn_namelen == 1 || 14227a0bc89SDoug Rabson (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.'))) { 14327a0bc89SDoug Rabson isadir = ATTR_DIRECTORY; 14427a0bc89SDoug Rabson scn = MSDOSFSROOT; 14527a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG 14627a0bc89SDoug Rabson printf("msdosfs_lookup(): looking for . or .. in root directory\n"); 14727a0bc89SDoug Rabson #endif 14827a0bc89SDoug Rabson cluster = MSDOSFSROOT; 149952a6212SJordan K. Hubbard blkoff = MSDOSFSROOT_OFS; 15027a0bc89SDoug Rabson goto foundroot; 15127a0bc89SDoug Rabson } 15227a0bc89SDoug Rabson 153952a6212SJordan K. Hubbard switch (unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename, 154952a6212SJordan K. Hubbard cnp->cn_namelen, 0)) { 155952a6212SJordan K. Hubbard case 0: 156952a6212SJordan K. Hubbard return (EINVAL); 157952a6212SJordan K. Hubbard case 1: 158952a6212SJordan K. Hubbard break; 159952a6212SJordan K. Hubbard case 2: 160952a6212SJordan K. Hubbard wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, 161952a6212SJordan K. Hubbard cnp->cn_namelen) + 1; 162952a6212SJordan K. Hubbard break; 163952a6212SJordan K. Hubbard case 3: 164952a6212SJordan K. Hubbard olddos = 0; 165952a6212SJordan K. Hubbard wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, 166952a6212SJordan K. Hubbard cnp->cn_namelen) + 1; 167952a6212SJordan K. Hubbard break; 16827a0bc89SDoug Rabson } 169952a6212SJordan K. Hubbard if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) 170952a6212SJordan K. Hubbard wincnt = 1; 17127a0bc89SDoug Rabson 172952a6212SJordan K. Hubbard /* 173952a6212SJordan K. Hubbard * Suppress search for slots unless creating 174952a6212SJordan K. Hubbard * file and at end of pathname, in which case 175952a6212SJordan K. Hubbard * we watch for a place to put the new file in 176952a6212SJordan K. Hubbard * case it doesn't already exist. 177952a6212SJordan K. Hubbard */ 178952a6212SJordan K. Hubbard slotcount = wincnt; 179952a6212SJordan K. Hubbard if ((nameiop == CREATE || nameiop == RENAME) && 180952a6212SJordan K. Hubbard (flags & ISLASTCN)) 181952a6212SJordan K. Hubbard slotcount = 0; 182952a6212SJordan K. Hubbard 18327a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG 184952a6212SJordan K. Hubbard printf("msdosfs_lookup(): dos version of filename %s, length %ld\n", 18527a0bc89SDoug Rabson dosfilename, cnp->cn_namelen); 18627a0bc89SDoug Rabson #endif 18727a0bc89SDoug Rabson /* 18827a0bc89SDoug Rabson * Search the directory pointed at by vdp for the name pointed at 18927a0bc89SDoug Rabson * by cnp->cn_nameptr. 19027a0bc89SDoug Rabson */ 19127a0bc89SDoug Rabson tdp = NULL; 19227a0bc89SDoug Rabson /* 19327a0bc89SDoug Rabson * The outer loop ranges over the clusters that make up the 19427a0bc89SDoug Rabson * directory. Note that the root directory is different from all 19527a0bc89SDoug Rabson * other directories. It has a fixed number of blocks that are not 19627a0bc89SDoug Rabson * part of the pool of allocatable clusters. So, we treat it a 19727a0bc89SDoug Rabson * little differently. The root directory starts at "cluster" 0. 19827a0bc89SDoug Rabson */ 199952a6212SJordan K. Hubbard diroff = 0; 20027a0bc89SDoug Rabson for (frcn = 0;; frcn++) { 201952a6212SJordan K. Hubbard error = pcbmap(dp, frcn, &bn, &cluster, &blsize); 202c3c6d51eSPoul-Henning Kamp if (error) { 20327a0bc89SDoug Rabson if (error == E2BIG) 20427a0bc89SDoug Rabson break; 205952a6212SJordan K. Hubbard return (error); 20627a0bc89SDoug Rabson } 207952a6212SJordan K. Hubbard error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); 208952a6212SJordan K. Hubbard if (error) { 209952a6212SJordan K. Hubbard brelse(bp); 210952a6212SJordan K. Hubbard return (error); 211952a6212SJordan K. Hubbard } 212952a6212SJordan K. Hubbard for (blkoff = 0; blkoff < blsize; 213952a6212SJordan K. Hubbard blkoff += sizeof(struct direntry), 214952a6212SJordan K. Hubbard diroff += sizeof(struct direntry)) { 215952a6212SJordan K. Hubbard dep = (struct direntry *)(bp->b_data + blkoff); 21627a0bc89SDoug Rabson /* 21727a0bc89SDoug Rabson * If the slot is empty and we are still looking 21827a0bc89SDoug Rabson * for an empty then remember this one. If the 21927a0bc89SDoug Rabson * slot is not empty then check to see if it 22027a0bc89SDoug Rabson * matches what we are looking for. If the slot 22127a0bc89SDoug Rabson * has never been filled with anything, then the 22227a0bc89SDoug Rabson * remainder of the directory has never been used, 22327a0bc89SDoug Rabson * so there is no point in searching it. 22427a0bc89SDoug Rabson */ 22527a0bc89SDoug Rabson if (dep->deName[0] == SLOT_EMPTY || 22627a0bc89SDoug Rabson dep->deName[0] == SLOT_DELETED) { 227952a6212SJordan K. Hubbard /* 228952a6212SJordan K. Hubbard * Drop memory of previous long matches 229952a6212SJordan K. Hubbard */ 230952a6212SJordan K. Hubbard chksum = -1; 231952a6212SJordan K. Hubbard 232952a6212SJordan K. Hubbard if (slotcount < wincnt) { 233952a6212SJordan K. Hubbard slotcount++; 23427a0bc89SDoug Rabson slotoffset = diroff; 23527a0bc89SDoug Rabson } 23627a0bc89SDoug Rabson if (dep->deName[0] == SLOT_EMPTY) { 23727a0bc89SDoug Rabson brelse(bp); 23827a0bc89SDoug Rabson goto notfound; 23927a0bc89SDoug Rabson } 24027a0bc89SDoug Rabson } else { 24127a0bc89SDoug Rabson /* 242952a6212SJordan K. Hubbard * If there wasn't enough space for our winentries, 243952a6212SJordan K. Hubbard * forget about the empty space 244952a6212SJordan K. Hubbard */ 245952a6212SJordan K. Hubbard if (slotcount < wincnt) 246952a6212SJordan K. Hubbard slotcount = 0; 247952a6212SJordan K. Hubbard 248952a6212SJordan K. Hubbard /* 249952a6212SJordan K. Hubbard * Check for Win95 long filename entry 250952a6212SJordan K. Hubbard */ 251952a6212SJordan K. Hubbard if (dep->deAttributes == ATTR_WIN95) { 252952a6212SJordan K. Hubbard if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) 253952a6212SJordan K. Hubbard continue; 254952a6212SJordan K. Hubbard 255952a6212SJordan K. Hubbard chksum = winChkName((const u_char *)cnp->cn_nameptr, 256952a6212SJordan K. Hubbard cnp->cn_namelen, 257952a6212SJordan K. Hubbard (struct winentry *)dep, 258952a6212SJordan K. Hubbard chksum); 259952a6212SJordan K. Hubbard continue; 260952a6212SJordan K. Hubbard } 261952a6212SJordan K. Hubbard 262952a6212SJordan K. Hubbard /* 26327a0bc89SDoug Rabson * Ignore volume labels (anywhere, not just 26427a0bc89SDoug Rabson * the root directory). 26527a0bc89SDoug Rabson */ 266952a6212SJordan K. Hubbard if (dep->deAttributes & ATTR_VOLUME) { 267952a6212SJordan K. Hubbard chksum = -1; 268952a6212SJordan K. Hubbard continue; 269952a6212SJordan K. Hubbard } 270952a6212SJordan K. Hubbard 271952a6212SJordan K. Hubbard /* 272952a6212SJordan K. Hubbard * Check for a checksum or name match 273952a6212SJordan K. Hubbard */ 274952a6212SJordan K. Hubbard if (chksum != winChksum(dep->deName) 275952a6212SJordan K. Hubbard && (!olddos || bcmp(dosfilename, dep->deName, 11))) { 276952a6212SJordan K. Hubbard chksum = -1; 277952a6212SJordan K. Hubbard continue; 278952a6212SJordan K. Hubbard } 27927a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG 280952a6212SJordan K. Hubbard printf("msdosfs_lookup(): match blkoff %d, diroff %d\n", 281952a6212SJordan K. Hubbard blkoff, diroff); 28227a0bc89SDoug Rabson #endif 28327a0bc89SDoug Rabson /* 28427a0bc89SDoug Rabson * Remember where this directory 28527a0bc89SDoug Rabson * entry came from for whoever did 286952a6212SJordan K. Hubbard * this lookup. 28727a0bc89SDoug Rabson */ 28827a0bc89SDoug Rabson dp->de_fndoffset = diroff; 289952a6212SJordan K. Hubbard dp->de_fndcnt = 0; /* unused anyway */ 290952a6212SJordan K. Hubbard 29127a0bc89SDoug Rabson goto found; 29227a0bc89SDoug Rabson } 293952a6212SJordan K. Hubbard } /* for (blkoff = 0; .... */ 29427a0bc89SDoug Rabson /* 29527a0bc89SDoug Rabson * Release the buffer holding the directory cluster just 29627a0bc89SDoug Rabson * searched. 29727a0bc89SDoug Rabson */ 29827a0bc89SDoug Rabson brelse(bp); 29927a0bc89SDoug Rabson } /* for (frcn = 0; ; frcn++) */ 300952a6212SJordan K. Hubbard 301952a6212SJordan K. Hubbard notfound: 30227a0bc89SDoug Rabson /* 30327a0bc89SDoug Rabson * We hold no disk buffers at this point. 30427a0bc89SDoug Rabson */ 30527a0bc89SDoug Rabson 30627a0bc89SDoug Rabson /* 307952a6212SJordan K. Hubbard * Fixup the slot description to point to the place where 308952a6212SJordan K. Hubbard * we might put the new DOS direntry (putting the Win95 309952a6212SJordan K. Hubbard * long name entries before that) 310952a6212SJordan K. Hubbard */ 311952a6212SJordan K. Hubbard if (!slotcount) { 312952a6212SJordan K. Hubbard slotcount = 1; 313952a6212SJordan K. Hubbard slotoffset = diroff; 314952a6212SJordan K. Hubbard } 315952a6212SJordan K. Hubbard if (wincnt > slotcount) 316952a6212SJordan K. Hubbard slotoffset += sizeof(struct direntry) * (wincnt - slotcount); 317952a6212SJordan K. Hubbard 318952a6212SJordan K. Hubbard /* 31927a0bc89SDoug Rabson * If we get here we didn't find the entry we were looking for. But 32027a0bc89SDoug Rabson * that's ok if we are creating or renaming and are at the end of 32127a0bc89SDoug Rabson * the pathname and the directory hasn't been removed. 32227a0bc89SDoug Rabson */ 32327a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG 324952a6212SJordan K. Hubbard printf("msdosfs_lookup(): op %d, refcnt %ld\n", 325952a6212SJordan K. Hubbard nameiop, dp->de_refcnt); 326952a6212SJordan K. Hubbard printf(" slotcount %d, slotoffset %d\n", 327952a6212SJordan K. Hubbard slotcount, slotoffset); 32827a0bc89SDoug Rabson #endif 32927a0bc89SDoug Rabson if ((nameiop == CREATE || nameiop == RENAME) && 33027a0bc89SDoug Rabson (flags & ISLASTCN) && dp->de_refcnt != 0) { 331952a6212SJordan K. Hubbard /* 332952a6212SJordan K. Hubbard * Access for write is interpreted as allowing 333952a6212SJordan K. Hubbard * creation of files in the directory. 334952a6212SJordan K. Hubbard */ 335952a6212SJordan K. Hubbard error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_proc); 336d8762fa6SBruce Evans if (error) 337952a6212SJordan K. Hubbard return (error); 338952a6212SJordan K. Hubbard /* 339952a6212SJordan K. Hubbard * Return an indication of where the new directory 340952a6212SJordan K. Hubbard * entry should be put. 341952a6212SJordan K. Hubbard */ 34227a0bc89SDoug Rabson dp->de_fndoffset = slotoffset; 343952a6212SJordan K. Hubbard dp->de_fndcnt = wincnt - 1; 344952a6212SJordan K. Hubbard 345952a6212SJordan K. Hubbard /* 346952a6212SJordan K. Hubbard * We return with the directory locked, so that 347952a6212SJordan K. Hubbard * the parameters we set up above will still be 348952a6212SJordan K. Hubbard * valid if we actually decide to do a direnter(). 349952a6212SJordan K. Hubbard * We return ni_vp == NULL to indicate that the entry 350952a6212SJordan K. Hubbard * does not currently exist; we leave a pointer to 351952a6212SJordan K. Hubbard * the (locked) directory inode in ndp->ni_dvp. 352952a6212SJordan K. Hubbard * The pathname buffer is saved so that the name 353952a6212SJordan K. Hubbard * can be obtained later. 354952a6212SJordan K. Hubbard * 355952a6212SJordan K. Hubbard * NB - if the directory is unlocked, then this 356952a6212SJordan K. Hubbard * information cannot be used. 357952a6212SJordan K. Hubbard */ 35827a0bc89SDoug Rabson cnp->cn_flags |= SAVENAME; 359952a6212SJordan K. Hubbard if (!lockparent) 360af3f60d5SBruce Evans VOP_UNLOCK(vdp, 0, p); 361952a6212SJordan K. Hubbard return (EJUSTRETURN); 36227a0bc89SDoug Rabson } 36327a0bc89SDoug Rabson /* 364952a6212SJordan K. Hubbard * Insert name into cache (as non-existent) if appropriate. 36527a0bc89SDoug Rabson */ 36627a0bc89SDoug Rabson if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) 36727a0bc89SDoug Rabson cache_enter(vdp, *vpp, cnp); 368952a6212SJordan K. Hubbard return (ENOENT); 36927a0bc89SDoug Rabson 370952a6212SJordan K. Hubbard found: 37127a0bc89SDoug Rabson /* 37227a0bc89SDoug Rabson * NOTE: We still have the buffer with matched directory entry at 37327a0bc89SDoug Rabson * this point. 37427a0bc89SDoug Rabson */ 37527a0bc89SDoug Rabson isadir = dep->deAttributes & ATTR_DIRECTORY; 37627a0bc89SDoug Rabson scn = getushort(dep->deStartCluster); 377952a6212SJordan K. Hubbard if (FAT32(pmp)) { 378952a6212SJordan K. Hubbard scn |= getushort(dep->deHighClust) << 16; 379952a6212SJordan K. Hubbard if (scn == pmp->pm_rootdirblk) { 380952a6212SJordan K. Hubbard /* 381952a6212SJordan K. Hubbard * There should actually be 0 here. 382952a6212SJordan K. Hubbard * Just ignore the error. 383952a6212SJordan K. Hubbard */ 384952a6212SJordan K. Hubbard scn = MSDOSFSROOT; 385952a6212SJordan K. Hubbard } 386952a6212SJordan K. Hubbard } 38727a0bc89SDoug Rabson 388952a6212SJordan K. Hubbard if (isadir) { 389952a6212SJordan K. Hubbard cluster = scn; 390952a6212SJordan K. Hubbard if (cluster == MSDOSFSROOT) 391952a6212SJordan K. Hubbard blkoff = MSDOSFSROOT_OFS; 392952a6212SJordan K. Hubbard else 393952a6212SJordan K. Hubbard blkoff = 0; 394952a6212SJordan K. Hubbard } else if (cluster == MSDOSFSROOT) 395952a6212SJordan K. Hubbard blkoff = diroff; 396952a6212SJordan K. Hubbard 397952a6212SJordan K. Hubbard /* 398952a6212SJordan K. Hubbard * Now release buf to allow deget to read the entry again. 399952a6212SJordan K. Hubbard * Reserving it here and giving it to deget could result 400952a6212SJordan K. Hubbard * in a deadlock. 401952a6212SJordan K. Hubbard */ 402952a6212SJordan K. Hubbard brelse(bp); 403952a6212SJordan K. Hubbard bp = 0; 404952a6212SJordan K. Hubbard 405952a6212SJordan K. Hubbard foundroot: 40627a0bc89SDoug Rabson /* 40727a0bc89SDoug Rabson * If we entered at foundroot, then we are looking for the . or .. 40827a0bc89SDoug Rabson * entry of the filesystems root directory. isadir and scn were 409952a6212SJordan K. Hubbard * setup before jumping here. And, bp is already null. 41027a0bc89SDoug Rabson */ 411952a6212SJordan K. Hubbard if (FAT32(pmp) && scn == MSDOSFSROOT) 412952a6212SJordan K. Hubbard scn = pmp->pm_rootdirblk; 41327a0bc89SDoug Rabson 41427a0bc89SDoug Rabson /* 415952a6212SJordan K. Hubbard * If deleting, and at end of pathname, return 416952a6212SJordan K. Hubbard * parameters which can be used to remove file. 417952a6212SJordan K. Hubbard * If the wantparent flag isn't set, we return only 418952a6212SJordan K. Hubbard * the directory (in ndp->ni_dvp), otherwise we go 419952a6212SJordan K. Hubbard * on and lock the inode, being careful with ".". 42027a0bc89SDoug Rabson */ 42127a0bc89SDoug Rabson if (nameiop == DELETE && (flags & ISLASTCN)) { 422952a6212SJordan K. Hubbard /* 423952a6212SJordan K. Hubbard * Don't allow deleting the root. 424952a6212SJordan K. Hubbard */ 425952a6212SJordan K. Hubbard if (blkoff == MSDOSFSROOT_OFS) 426952a6212SJordan K. Hubbard return EROFS; /* really? XXX */ 427952a6212SJordan K. Hubbard 428952a6212SJordan K. Hubbard /* 429952a6212SJordan K. Hubbard * Write access to directory required to delete files. 430952a6212SJordan K. Hubbard */ 431952a6212SJordan K. Hubbard error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_proc); 432952a6212SJordan K. Hubbard if (error) 433952a6212SJordan K. Hubbard return (error); 434952a6212SJordan K. Hubbard 435952a6212SJordan K. Hubbard /* 436952a6212SJordan K. Hubbard * Return pointer to current entry in dp->i_offset. 437952a6212SJordan K. Hubbard * Save directory inode pointer in ndp->ni_dvp for dirremove(). 438952a6212SJordan K. Hubbard */ 43927a0bc89SDoug Rabson if (dp->de_StartCluster == scn && isadir) { /* "." */ 44027a0bc89SDoug Rabson VREF(vdp); 44127a0bc89SDoug Rabson *vpp = vdp; 442952a6212SJordan K. Hubbard return (0); 44327a0bc89SDoug Rabson } 444952a6212SJordan K. Hubbard error = deget(pmp, cluster, blkoff, &tdp); 445952a6212SJordan K. Hubbard if (error) 446952a6212SJordan K. Hubbard return (error); 44727a0bc89SDoug Rabson *vpp = DETOV(tdp); 44827a0bc89SDoug Rabson if (!lockparent) 449af3f60d5SBruce Evans VOP_UNLOCK(vdp, 0, p); 450952a6212SJordan K. Hubbard return (0); 45127a0bc89SDoug Rabson } 45227a0bc89SDoug Rabson 45327a0bc89SDoug Rabson /* 454952a6212SJordan K. Hubbard * If rewriting (RENAME), return the inode and the 455952a6212SJordan K. Hubbard * information required to rewrite the present directory 456952a6212SJordan K. Hubbard * Must get inode of directory entry to verify it's a 457952a6212SJordan K. Hubbard * regular file, or empty directory. 45827a0bc89SDoug Rabson */ 459952a6212SJordan K. Hubbard if (nameiop == RENAME && wantparent && 460952a6212SJordan K. Hubbard (flags & ISLASTCN)) { 461952a6212SJordan K. Hubbard if (blkoff == MSDOSFSROOT_OFS) 462952a6212SJordan K. Hubbard return EROFS; /* really? XXX */ 463952a6212SJordan K. Hubbard 464952a6212SJordan K. Hubbard error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_proc); 465952a6212SJordan K. Hubbard if (error) 466952a6212SJordan K. Hubbard return (error); 467952a6212SJordan K. Hubbard 468952a6212SJordan K. Hubbard /* 469952a6212SJordan K. Hubbard * Careful about locking second inode. 470952a6212SJordan K. Hubbard * This can only occur if the target is ".". 471952a6212SJordan K. Hubbard */ 472952a6212SJordan K. Hubbard if (dp->de_StartCluster == scn && isadir) 473952a6212SJordan K. Hubbard return (EISDIR); 474952a6212SJordan K. Hubbard 475952a6212SJordan K. Hubbard if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0) 476952a6212SJordan K. Hubbard return (error); 47727a0bc89SDoug Rabson *vpp = DETOV(tdp); 47827a0bc89SDoug Rabson cnp->cn_flags |= SAVENAME; 47927a0bc89SDoug Rabson if (!lockparent) 480af3f60d5SBruce Evans VOP_UNLOCK(vdp, 0, p); 481952a6212SJordan K. Hubbard return (0); 48227a0bc89SDoug Rabson } 48327a0bc89SDoug Rabson 48427a0bc89SDoug Rabson /* 485952a6212SJordan K. Hubbard * Step through the translation in the name. We do not `vput' the 486952a6212SJordan K. Hubbard * directory because we may need it again if a symbolic link 487952a6212SJordan K. Hubbard * is relative to the current directory. Instead we save it 488952a6212SJordan K. Hubbard * unlocked as "pdp". We must get the target inode before unlocking 489952a6212SJordan K. Hubbard * the directory to insure that the inode will not be removed 490952a6212SJordan K. Hubbard * before we get it. We prevent deadlock by always fetching 491952a6212SJordan K. Hubbard * inodes from the root, moving down the directory tree. Thus 492952a6212SJordan K. Hubbard * when following backward pointers ".." we must unlock the 493952a6212SJordan K. Hubbard * parent directory before getting the requested directory. 494952a6212SJordan K. Hubbard * There is a potential race condition here if both the current 495952a6212SJordan K. Hubbard * and parent directories are removed before the VFS_VGET for the 496952a6212SJordan K. Hubbard * inode associated with ".." returns. We hope that this occurs 497952a6212SJordan K. Hubbard * infrequently since we cannot avoid this race condition without 498952a6212SJordan K. Hubbard * implementing a sophisticated deadlock detection algorithm. 499952a6212SJordan K. Hubbard * Note also that this simple deadlock detection scheme will not 500952a6212SJordan K. Hubbard * work if the file system has any hard links other than ".." 501952a6212SJordan K. Hubbard * that point backwards in the directory structure. 50227a0bc89SDoug Rabson */ 50327a0bc89SDoug Rabson pdp = vdp; 50427a0bc89SDoug Rabson if (flags & ISDOTDOT) { 505af3f60d5SBruce Evans VOP_UNLOCK(pdp, 0, p); 506952a6212SJordan K. Hubbard error = deget(pmp, cluster, blkoff, &tdp); 50727a0bc89SDoug Rabson if (error) { 508af3f60d5SBruce Evans vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p); 509952a6212SJordan K. Hubbard return (error); 51027a0bc89SDoug Rabson } 511952a6212SJordan K. Hubbard if (lockparent && (flags & ISLASTCN) && 512952a6212SJordan K. Hubbard (error = vn_lock(pdp, LK_EXCLUSIVE, p))) { 51327a0bc89SDoug Rabson vput(DETOV(tdp)); 514952a6212SJordan K. Hubbard return (error); 51527a0bc89SDoug Rabson } 51627a0bc89SDoug Rabson *vpp = DETOV(tdp); 517952a6212SJordan K. Hubbard } else if (dp->de_StartCluster == scn && isadir) { 518952a6212SJordan K. Hubbard VREF(vdp); /* we want ourself, ie "." */ 51927a0bc89SDoug Rabson *vpp = vdp; 52027a0bc89SDoug Rabson } else { 521952a6212SJordan K. Hubbard if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0) 522952a6212SJordan K. Hubbard return (error); 52327a0bc89SDoug Rabson if (!lockparent || !(flags & ISLASTCN)) 524af3f60d5SBruce Evans VOP_UNLOCK(pdp, 0, p); 52527a0bc89SDoug Rabson *vpp = DETOV(tdp); 52627a0bc89SDoug Rabson } 52727a0bc89SDoug Rabson 52827a0bc89SDoug Rabson /* 529952a6212SJordan K. Hubbard * Insert name into cache if appropriate. 53027a0bc89SDoug Rabson */ 53127a0bc89SDoug Rabson if (cnp->cn_flags & MAKEENTRY) 53227a0bc89SDoug Rabson cache_enter(vdp, *vpp, cnp); 533952a6212SJordan K. Hubbard return (0); 53427a0bc89SDoug Rabson } 53527a0bc89SDoug Rabson 53627a0bc89SDoug Rabson /* 53727a0bc89SDoug Rabson * dep - directory entry to copy into the directory 53827a0bc89SDoug Rabson * ddep - directory to add to 53927a0bc89SDoug Rabson * depp - return the address of the denode for the created directory entry 54027a0bc89SDoug Rabson * if depp != 0 541952a6212SJordan K. Hubbard * cnp - componentname needed for Win95 long filenames 54227a0bc89SDoug Rabson */ 54327a0bc89SDoug Rabson int 544952a6212SJordan K. Hubbard createde(dep, ddep, depp, cnp) 54527a0bc89SDoug Rabson struct denode *dep; 54627a0bc89SDoug Rabson struct denode *ddep; 54727a0bc89SDoug Rabson struct denode **depp; 548952a6212SJordan K. Hubbard struct componentname *cnp; 54927a0bc89SDoug Rabson { 55027a0bc89SDoug Rabson int error; 55127a0bc89SDoug Rabson u_long dirclust, diroffset; 55227a0bc89SDoug Rabson struct direntry *ndep; 55327a0bc89SDoug Rabson struct msdosfsmount *pmp = ddep->de_pmp; 55427a0bc89SDoug Rabson struct buf *bp; 555952a6212SJordan K. Hubbard daddr_t bn; 556952a6212SJordan K. Hubbard int blsize; 55727a0bc89SDoug Rabson 55827a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG 559952a6212SJordan K. Hubbard printf("createde(dep %p, ddep %p, depp %p, cnp %p)\n", 560952a6212SJordan K. Hubbard dep, ddep, depp, cnp); 56127a0bc89SDoug Rabson #endif 56227a0bc89SDoug Rabson 56327a0bc89SDoug Rabson /* 56427a0bc89SDoug Rabson * If no space left in the directory then allocate another cluster 56527a0bc89SDoug Rabson * and chain it onto the end of the file. There is one exception 56627a0bc89SDoug Rabson * to this. That is, if the root directory has no more space it 56727a0bc89SDoug Rabson * can NOT be expanded. extendfile() checks for and fails attempts 56827a0bc89SDoug Rabson * to extend the root directory. We just return an error in that 56927a0bc89SDoug Rabson * case. 57027a0bc89SDoug Rabson */ 571952a6212SJordan K. Hubbard if (ddep->de_fndoffset >= ddep->de_FileSize) { 572952a6212SJordan K. Hubbard diroffset = ddep->de_fndoffset + sizeof(struct direntry) 573952a6212SJordan K. Hubbard - ddep->de_FileSize; 574952a6212SJordan K. Hubbard dirclust = de_clcount(pmp, diroffset); 575952a6212SJordan K. Hubbard error = extendfile(ddep, dirclust, 0, 0, DE_CLEAR); 576952a6212SJordan K. Hubbard if (error) { 577952a6212SJordan K. Hubbard (void)detrunc(ddep, ddep->de_FileSize, 0, NOCRED, NULL); 57827a0bc89SDoug Rabson return error; 579952a6212SJordan K. Hubbard } 580952a6212SJordan K. Hubbard 58127a0bc89SDoug Rabson /* 58227a0bc89SDoug Rabson * Update the size of the directory 58327a0bc89SDoug Rabson */ 584952a6212SJordan K. Hubbard ddep->de_FileSize += de_cn2off(pmp, dirclust); 585952a6212SJordan K. Hubbard } 586952a6212SJordan K. Hubbard 58727a0bc89SDoug Rabson /* 588952a6212SJordan K. Hubbard * We just read in the cluster with space. Copy the new directory 58927a0bc89SDoug Rabson * entry in. Then write it to disk. NOTE: DOS directories 59027a0bc89SDoug Rabson * do not get smaller as clusters are emptied. 59127a0bc89SDoug Rabson */ 592952a6212SJordan K. Hubbard error = pcbmap(ddep, de_cluster(pmp, ddep->de_fndoffset), 593952a6212SJordan K. Hubbard &bn, &dirclust, &blsize); 59427a0bc89SDoug Rabson if (error) 59527a0bc89SDoug Rabson return error; 596952a6212SJordan K. Hubbard diroffset = ddep->de_fndoffset; 597952a6212SJordan K. Hubbard if (dirclust != MSDOSFSROOT) 598952a6212SJordan K. Hubbard diroffset &= pmp->pm_crbomask; 599952a6212SJordan K. Hubbard if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp)) != 0) { 600952a6212SJordan K. Hubbard brelse(bp); 601952a6212SJordan K. Hubbard return error; 60227a0bc89SDoug Rabson } 603952a6212SJordan K. Hubbard ndep = bptoep(pmp, bp, ddep->de_fndoffset); 604952a6212SJordan K. Hubbard 60527a0bc89SDoug Rabson DE_EXTERNALIZE(ndep, dep); 60627a0bc89SDoug Rabson 60727a0bc89SDoug Rabson /* 608952a6212SJordan K. Hubbard * Now write the Win95 long name 609952a6212SJordan K. Hubbard */ 610952a6212SJordan K. Hubbard if (ddep->de_fndcnt > 0) { 611952a6212SJordan K. Hubbard u_int8_t chksum = winChksum(ndep->deName); 612952a6212SJordan K. Hubbard const u_char *un = (const u_char *)cnp->cn_nameptr; 613952a6212SJordan K. Hubbard int unlen = cnp->cn_namelen; 614952a6212SJordan K. Hubbard int cnt = 1; 615952a6212SJordan K. Hubbard 616952a6212SJordan K. Hubbard while (--ddep->de_fndcnt >= 0) { 617952a6212SJordan K. Hubbard if (!(ddep->de_fndoffset & pmp->pm_crbomask)) { 618952a6212SJordan K. Hubbard if ((error = bwrite(bp)) != 0) 619952a6212SJordan K. Hubbard return error; 620952a6212SJordan K. Hubbard 621952a6212SJordan K. Hubbard ddep->de_fndoffset -= sizeof(struct direntry); 622952a6212SJordan K. Hubbard error = pcbmap(ddep, 623952a6212SJordan K. Hubbard de_cluster(pmp, 624952a6212SJordan K. Hubbard ddep->de_fndoffset), 625952a6212SJordan K. Hubbard &bn, 0, &blsize); 626952a6212SJordan K. Hubbard if (error) 627952a6212SJordan K. Hubbard return error; 628952a6212SJordan K. Hubbard 629952a6212SJordan K. Hubbard error = bread(pmp->pm_devvp, bn, blsize, 630952a6212SJordan K. Hubbard NOCRED, &bp); 631952a6212SJordan K. Hubbard if (error) { 632952a6212SJordan K. Hubbard brelse(bp); 633952a6212SJordan K. Hubbard return error; 634952a6212SJordan K. Hubbard } 635952a6212SJordan K. Hubbard ndep = bptoep(pmp, bp, ddep->de_fndoffset); 636952a6212SJordan K. Hubbard } else { 637952a6212SJordan K. Hubbard ndep--; 638952a6212SJordan K. Hubbard ddep->de_fndoffset -= sizeof(struct direntry); 639952a6212SJordan K. Hubbard } 640952a6212SJordan K. Hubbard if (!unix2winfn(un, unlen, (struct winentry *)ndep, cnt++, chksum)) 641952a6212SJordan K. Hubbard break; 642952a6212SJordan K. Hubbard } 643952a6212SJordan K. Hubbard } 644952a6212SJordan K. Hubbard 645952a6212SJordan K. Hubbard if ((error = bwrite(bp)) != 0) 646952a6212SJordan K. Hubbard return error; 647952a6212SJordan K. Hubbard 648952a6212SJordan K. Hubbard /* 64927a0bc89SDoug Rabson * If they want us to return with the denode gotten. 65027a0bc89SDoug Rabson */ 65127a0bc89SDoug Rabson if (depp) { 652952a6212SJordan K. Hubbard if (dep->de_Attributes & ATTR_DIRECTORY) { 653952a6212SJordan K. Hubbard dirclust = dep->de_StartCluster; 654952a6212SJordan K. Hubbard if (FAT32(pmp) && dirclust == pmp->pm_rootdirblk) 655952a6212SJordan K. Hubbard dirclust = MSDOSFSROOT; 656952a6212SJordan K. Hubbard if (dirclust == MSDOSFSROOT) 657952a6212SJordan K. Hubbard diroffset = MSDOSFSROOT_OFS; 658952a6212SJordan K. Hubbard else 659952a6212SJordan K. Hubbard diroffset = 0; 66027a0bc89SDoug Rabson } 661952a6212SJordan K. Hubbard return deget(pmp, dirclust, diroffset, depp); 66227a0bc89SDoug Rabson } 663952a6212SJordan K. Hubbard 66427a0bc89SDoug Rabson return 0; 66527a0bc89SDoug Rabson } 66627a0bc89SDoug Rabson 66727a0bc89SDoug Rabson /* 66827a0bc89SDoug Rabson * Be sure a directory is empty except for "." and "..". Return 1 if empty, 66927a0bc89SDoug Rabson * return 0 if not empty or error. 67027a0bc89SDoug Rabson */ 67127a0bc89SDoug Rabson int 67227a0bc89SDoug Rabson dosdirempty(dep) 67327a0bc89SDoug Rabson struct denode *dep; 67427a0bc89SDoug Rabson { 675952a6212SJordan K. Hubbard int blsize; 67627a0bc89SDoug Rabson int error; 67727a0bc89SDoug Rabson u_long cn; 67827a0bc89SDoug Rabson daddr_t bn; 67927a0bc89SDoug Rabson struct buf *bp; 68027a0bc89SDoug Rabson struct msdosfsmount *pmp = dep->de_pmp; 68127a0bc89SDoug Rabson struct direntry *dentp; 68227a0bc89SDoug Rabson 68327a0bc89SDoug Rabson /* 68427a0bc89SDoug Rabson * Since the filesize field in directory entries for a directory is 68527a0bc89SDoug Rabson * zero, we just have to feel our way through the directory until 68627a0bc89SDoug Rabson * we hit end of file. 68727a0bc89SDoug Rabson */ 68827a0bc89SDoug Rabson for (cn = 0;; cn++) { 689952a6212SJordan K. Hubbard if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) { 69027a0bc89SDoug Rabson if (error == E2BIG) 691952a6212SJordan K. Hubbard return (1); /* it's empty */ 692952a6212SJordan K. Hubbard return (0); 693952a6212SJordan K. Hubbard } 694952a6212SJordan K. Hubbard error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); 695952a6212SJordan K. Hubbard if (error) { 696952a6212SJordan K. Hubbard brelse(bp); 697952a6212SJordan K. Hubbard return (0); 698952a6212SJordan K. Hubbard } 699952a6212SJordan K. Hubbard for (dentp = (struct direntry *)bp->b_data; 700952a6212SJordan K. Hubbard (char *)dentp < bp->b_data + blsize; 701952a6212SJordan K. Hubbard dentp++) { 702952a6212SJordan K. Hubbard if (dentp->deName[0] != SLOT_DELETED && 703952a6212SJordan K. Hubbard (dentp->deAttributes & ATTR_VOLUME) == 0) { 70427a0bc89SDoug Rabson /* 70527a0bc89SDoug Rabson * In dos directories an entry whose name 70627a0bc89SDoug Rabson * starts with SLOT_EMPTY (0) starts the 70727a0bc89SDoug Rabson * beginning of the unused part of the 70827a0bc89SDoug Rabson * directory, so we can just return that it 70927a0bc89SDoug Rabson * is empty. 71027a0bc89SDoug Rabson */ 71127a0bc89SDoug Rabson if (dentp->deName[0] == SLOT_EMPTY) { 71227a0bc89SDoug Rabson brelse(bp); 713952a6212SJordan K. Hubbard return (1); 71427a0bc89SDoug Rabson } 71527a0bc89SDoug Rabson /* 71627a0bc89SDoug Rabson * Any names other than "." and ".." in a 71727a0bc89SDoug Rabson * directory mean it is not empty. 71827a0bc89SDoug Rabson */ 71927a0bc89SDoug Rabson if (bcmp(dentp->deName, ". ", 11) && 72027a0bc89SDoug Rabson bcmp(dentp->deName, ".. ", 11)) { 72127a0bc89SDoug Rabson brelse(bp); 72227a0bc89SDoug Rabson #ifdef MSDOSFS_DEBUG 723952a6212SJordan K. Hubbard printf("dosdirempty(): entry found %02x, %02x\n", 724952a6212SJordan K. Hubbard dentp->deName[0], dentp->deName[1]); 72527a0bc89SDoug Rabson #endif 726952a6212SJordan K. Hubbard return (0); /* not empty */ 72727a0bc89SDoug Rabson } 72827a0bc89SDoug Rabson } 72927a0bc89SDoug Rabson } 73027a0bc89SDoug Rabson brelse(bp); 73127a0bc89SDoug Rabson } 73227a0bc89SDoug Rabson /* NOTREACHED */ 73327a0bc89SDoug Rabson } 73427a0bc89SDoug Rabson 73527a0bc89SDoug Rabson /* 73627a0bc89SDoug Rabson * Check to see if the directory described by target is in some 73727a0bc89SDoug Rabson * subdirectory of source. This prevents something like the following from 73827a0bc89SDoug Rabson * succeeding and leaving a bunch or files and directories orphaned. mv 73927a0bc89SDoug Rabson * /a/b/c /a/b/c/d/e/f Where c and f are directories. 74027a0bc89SDoug Rabson * 74127a0bc89SDoug Rabson * source - the inode for /a/b/c 74227a0bc89SDoug Rabson * target - the inode for /a/b/c/d/e/f 74327a0bc89SDoug Rabson * 74427a0bc89SDoug Rabson * Returns 0 if target is NOT a subdirectory of source. 74527a0bc89SDoug Rabson * Otherwise returns a non-zero error number. 74627a0bc89SDoug Rabson * The target inode is always unlocked on return. 74727a0bc89SDoug Rabson */ 74827a0bc89SDoug Rabson int 74927a0bc89SDoug Rabson doscheckpath(source, target) 75027a0bc89SDoug Rabson struct denode *source; 75127a0bc89SDoug Rabson struct denode *target; 75227a0bc89SDoug Rabson { 75327a0bc89SDoug Rabson daddr_t scn; 75427a0bc89SDoug Rabson struct msdosfsmount *pmp; 75527a0bc89SDoug Rabson struct direntry *ep; 75627a0bc89SDoug Rabson struct denode *dep; 75727a0bc89SDoug Rabson struct buf *bp = NULL; 75827a0bc89SDoug Rabson int error = 0; 75927a0bc89SDoug Rabson 76027a0bc89SDoug Rabson dep = target; 76127a0bc89SDoug Rabson if ((target->de_Attributes & ATTR_DIRECTORY) == 0 || 76227a0bc89SDoug Rabson (source->de_Attributes & ATTR_DIRECTORY) == 0) { 76327a0bc89SDoug Rabson error = ENOTDIR; 76427a0bc89SDoug Rabson goto out; 76527a0bc89SDoug Rabson } 76627a0bc89SDoug Rabson if (dep->de_StartCluster == source->de_StartCluster) { 76727a0bc89SDoug Rabson error = EEXIST; 76827a0bc89SDoug Rabson goto out; 76927a0bc89SDoug Rabson } 77027a0bc89SDoug Rabson if (dep->de_StartCluster == MSDOSFSROOT) 77127a0bc89SDoug Rabson goto out; 772952a6212SJordan K. Hubbard pmp = dep->de_pmp; 773952a6212SJordan K. Hubbard #ifdef DIAGNOSTIC 774952a6212SJordan K. Hubbard if (pmp != source->de_pmp) 775952a6212SJordan K. Hubbard panic("doscheckpath: source and target on different filesystems"); 776952a6212SJordan K. Hubbard #endif 777952a6212SJordan K. Hubbard if (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk) 778952a6212SJordan K. Hubbard goto out; 779952a6212SJordan K. Hubbard 78027a0bc89SDoug Rabson for (;;) { 78127a0bc89SDoug Rabson if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) { 78227a0bc89SDoug Rabson error = ENOTDIR; 783952a6212SJordan K. Hubbard break; 78427a0bc89SDoug Rabson } 78527a0bc89SDoug Rabson scn = dep->de_StartCluster; 78627a0bc89SDoug Rabson error = bread(pmp->pm_devvp, cntobn(pmp, scn), 78727a0bc89SDoug Rabson pmp->pm_bpcluster, NOCRED, &bp); 788952a6212SJordan K. Hubbard if (error) 78927a0bc89SDoug Rabson break; 790952a6212SJordan K. Hubbard 79127a0bc89SDoug Rabson ep = (struct direntry *) bp->b_data + 1; 79227a0bc89SDoug Rabson if ((ep->deAttributes & ATTR_DIRECTORY) == 0 || 79327a0bc89SDoug Rabson bcmp(ep->deName, ".. ", 11) != 0) { 79427a0bc89SDoug Rabson error = ENOTDIR; 79527a0bc89SDoug Rabson break; 79627a0bc89SDoug Rabson } 79727a0bc89SDoug Rabson scn = getushort(ep->deStartCluster); 798952a6212SJordan K. Hubbard if (FAT32(pmp)) 799952a6212SJordan K. Hubbard scn |= getushort(ep->deHighClust) << 16; 800952a6212SJordan K. Hubbard 80127a0bc89SDoug Rabson if (scn == source->de_StartCluster) { 80227a0bc89SDoug Rabson error = EINVAL; 80327a0bc89SDoug Rabson break; 80427a0bc89SDoug Rabson } 80527a0bc89SDoug Rabson if (scn == MSDOSFSROOT) 80627a0bc89SDoug Rabson break; 807952a6212SJordan K. Hubbard if (FAT32(pmp) && scn == pmp->pm_rootdirblk) { 808952a6212SJordan K. Hubbard /* 809952a6212SJordan K. Hubbard * scn should be 0 in this case, 810952a6212SJordan K. Hubbard * but we silently ignore the error. 811952a6212SJordan K. Hubbard */ 812952a6212SJordan K. Hubbard break; 813952a6212SJordan K. Hubbard } 814952a6212SJordan K. Hubbard 81527a0bc89SDoug Rabson vput(DETOV(dep)); 81627a0bc89SDoug Rabson brelse(bp); 81727a0bc89SDoug Rabson bp = NULL; 818952a6212SJordan K. Hubbard /* NOTE: deget() clears dep on error */ 819952a6212SJordan K. Hubbard if ((error = deget(pmp, scn, 0, &dep)) != 0) 82027a0bc89SDoug Rabson break; 82127a0bc89SDoug Rabson } 82227a0bc89SDoug Rabson out:; 82327a0bc89SDoug Rabson if (bp) 82427a0bc89SDoug Rabson brelse(bp); 82527a0bc89SDoug Rabson if (error == ENOTDIR) 82627a0bc89SDoug Rabson printf("doscheckpath(): .. not a directory?\n"); 82727a0bc89SDoug Rabson if (dep != NULL) 82827a0bc89SDoug Rabson vput(DETOV(dep)); 829952a6212SJordan K. Hubbard return (error); 83027a0bc89SDoug Rabson } 83127a0bc89SDoug Rabson 83227a0bc89SDoug Rabson /* 83327a0bc89SDoug Rabson * Read in the disk block containing the directory entry (dirclu, dirofs) 83427a0bc89SDoug Rabson * and return the address of the buf header, and the address of the 83527a0bc89SDoug Rabson * directory entry within the block. 83627a0bc89SDoug Rabson */ 83727a0bc89SDoug Rabson int 838952a6212SJordan K. Hubbard readep(pmp, dirclust, diroffset, bpp, epp) 83927a0bc89SDoug Rabson struct msdosfsmount *pmp; 840952a6212SJordan K. Hubbard u_long dirclust, diroffset; 84127a0bc89SDoug Rabson struct buf **bpp; 84227a0bc89SDoug Rabson struct direntry **epp; 84327a0bc89SDoug Rabson { 84427a0bc89SDoug Rabson int error; 84527a0bc89SDoug Rabson daddr_t bn; 846952a6212SJordan K. Hubbard int blsize; 847952a6212SJordan K. Hubbard u_long boff; 84827a0bc89SDoug Rabson 849952a6212SJordan K. Hubbard boff = diroffset & ~pmp->pm_crbomask; 850952a6212SJordan K. Hubbard blsize = pmp->pm_bpcluster; 851952a6212SJordan K. Hubbard if (dirclust == MSDOSFSROOT 852952a6212SJordan K. Hubbard && de_blk(pmp, diroffset + blsize) > pmp->pm_rootdirsize) 853952a6212SJordan K. Hubbard blsize = de_bn2off(pmp, pmp->pm_rootdirsize) & pmp->pm_crbomask; 854952a6212SJordan K. Hubbard bn = detobn(pmp, dirclust, diroffset); 855952a6212SJordan K. Hubbard if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, bpp)) != 0) { 856952a6212SJordan K. Hubbard brelse(*bpp); 85727a0bc89SDoug Rabson *bpp = NULL; 858952a6212SJordan K. Hubbard return (error); 85927a0bc89SDoug Rabson } 86027a0bc89SDoug Rabson if (epp) 861952a6212SJordan K. Hubbard *epp = bptoep(pmp, *bpp, diroffset); 862952a6212SJordan K. Hubbard return (0); 86327a0bc89SDoug Rabson } 86427a0bc89SDoug Rabson 86527a0bc89SDoug Rabson /* 86627a0bc89SDoug Rabson * Read in the disk block containing the directory entry dep came from and 86727a0bc89SDoug Rabson * return the address of the buf header, and the address of the directory 86827a0bc89SDoug Rabson * entry within the block. 86927a0bc89SDoug Rabson */ 87027a0bc89SDoug Rabson int 87127a0bc89SDoug Rabson readde(dep, bpp, epp) 87227a0bc89SDoug Rabson struct denode *dep; 87327a0bc89SDoug Rabson struct buf **bpp; 87427a0bc89SDoug Rabson struct direntry **epp; 87527a0bc89SDoug Rabson { 876952a6212SJordan K. Hubbard 877952a6212SJordan K. Hubbard return (readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset, 878952a6212SJordan K. Hubbard bpp, epp)); 879952a6212SJordan K. Hubbard } 880952a6212SJordan K. Hubbard 881952a6212SJordan K. Hubbard /* 882952a6212SJordan K. Hubbard * Remove a directory entry. At this point the file represented by the 883952a6212SJordan K. Hubbard * directory entry to be removed is still full length until noone has it 884952a6212SJordan K. Hubbard * open. When the file no longer being used msdosfs_inactive() is called 885952a6212SJordan K. Hubbard * and will truncate the file to 0 length. When the vnode containing the 886952a6212SJordan K. Hubbard * denode is needed for some other purpose by VFS it will call 887952a6212SJordan K. Hubbard * msdosfs_reclaim() which will remove the denode from the denode cache. 888952a6212SJordan K. Hubbard */ 889952a6212SJordan K. Hubbard int 890952a6212SJordan K. Hubbard removede(pdep, dep) 891952a6212SJordan K. Hubbard struct denode *pdep; /* directory where the entry is removed */ 892952a6212SJordan K. Hubbard struct denode *dep; /* file to be removed */ 893952a6212SJordan K. Hubbard { 894952a6212SJordan K. Hubbard int error; 895952a6212SJordan K. Hubbard struct direntry *ep; 896952a6212SJordan K. Hubbard struct buf *bp; 897952a6212SJordan K. Hubbard daddr_t bn; 898952a6212SJordan K. Hubbard int blsize; 899952a6212SJordan K. Hubbard struct msdosfsmount *pmp = pdep->de_pmp; 900952a6212SJordan K. Hubbard u_long offset = pdep->de_fndoffset; 901952a6212SJordan K. Hubbard 902952a6212SJordan K. Hubbard #ifdef MSDOSFS_DEBUG 903952a6212SJordan K. Hubbard printf("removede(): filename %s, dep %p, offset %08lx\n", 904952a6212SJordan K. Hubbard dep->de_Name, dep, offset); 905952a6212SJordan K. Hubbard #endif 906952a6212SJordan K. Hubbard 907952a6212SJordan K. Hubbard dep->de_refcnt--; 908952a6212SJordan K. Hubbard offset += sizeof(struct direntry); 909952a6212SJordan K. Hubbard do { 910952a6212SJordan K. Hubbard offset -= sizeof(struct direntry); 911952a6212SJordan K. Hubbard error = pcbmap(pdep, de_cluster(pmp, offset), &bn, 0, &blsize); 912952a6212SJordan K. Hubbard if (error) 913952a6212SJordan K. Hubbard return error; 914952a6212SJordan K. Hubbard error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); 915952a6212SJordan K. Hubbard if (error) { 916952a6212SJordan K. Hubbard brelse(bp); 917952a6212SJordan K. Hubbard return error; 918952a6212SJordan K. Hubbard } 919952a6212SJordan K. Hubbard ep = bptoep(pmp, bp, offset); 920952a6212SJordan K. Hubbard /* 921952a6212SJordan K. Hubbard * Check whether, if we came here the second time, i.e. 922952a6212SJordan K. Hubbard * when underflowing into the previous block, the last 923952a6212SJordan K. Hubbard * entry in this block is a longfilename entry, too. 924952a6212SJordan K. Hubbard */ 925952a6212SJordan K. Hubbard if (ep->deAttributes != ATTR_WIN95 926952a6212SJordan K. Hubbard && offset != pdep->de_fndoffset) { 927952a6212SJordan K. Hubbard brelse(bp); 928952a6212SJordan K. Hubbard break; 929952a6212SJordan K. Hubbard } 930952a6212SJordan K. Hubbard offset += sizeof(struct direntry); 931952a6212SJordan K. Hubbard while (1) { 932952a6212SJordan K. Hubbard /* 933952a6212SJordan K. Hubbard * We are a bit agressive here in that we delete any Win95 934952a6212SJordan K. Hubbard * entries preceding this entry, not just the ones we "own". 935952a6212SJordan K. Hubbard * Since these presumably aren't valid anyway, 936952a6212SJordan K. Hubbard * there should be no harm. 937952a6212SJordan K. Hubbard */ 938952a6212SJordan K. Hubbard offset -= sizeof(struct direntry); 939952a6212SJordan K. Hubbard ep--->deName[0] = SLOT_DELETED; 940952a6212SJordan K. Hubbard if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) 941952a6212SJordan K. Hubbard || !(offset & pmp->pm_crbomask) 942952a6212SJordan K. Hubbard || ep->deAttributes != ATTR_WIN95) 943952a6212SJordan K. Hubbard break; 944952a6212SJordan K. Hubbard } 945952a6212SJordan K. Hubbard if ((error = bwrite(bp)) != 0) 946952a6212SJordan K. Hubbard return error; 947952a6212SJordan K. Hubbard } while (!(pmp->pm_flags & MSDOSFSMNT_NOWIN95) 948952a6212SJordan K. Hubbard && !(offset & pmp->pm_crbomask) 949952a6212SJordan K. Hubbard && offset); 950952a6212SJordan K. Hubbard return 0; 951952a6212SJordan K. Hubbard } 952952a6212SJordan K. Hubbard 953952a6212SJordan K. Hubbard /* 954952a6212SJordan K. Hubbard * Create a unique DOS name in dvp 955952a6212SJordan K. Hubbard */ 956952a6212SJordan K. Hubbard int 957952a6212SJordan K. Hubbard uniqdosname(dep, cnp, cp) 958952a6212SJordan K. Hubbard struct denode *dep; 959952a6212SJordan K. Hubbard struct componentname *cnp; 960952a6212SJordan K. Hubbard u_char *cp; 961952a6212SJordan K. Hubbard { 962952a6212SJordan K. Hubbard struct msdosfsmount *pmp = dep->de_pmp; 963952a6212SJordan K. Hubbard struct direntry *dentp; 964952a6212SJordan K. Hubbard int gen; 965952a6212SJordan K. Hubbard int blsize; 966952a6212SJordan K. Hubbard u_long cn; 967952a6212SJordan K. Hubbard daddr_t bn; 968952a6212SJordan K. Hubbard struct buf *bp; 969952a6212SJordan K. Hubbard int error; 970952a6212SJordan K. Hubbard 971952a6212SJordan K. Hubbard for (gen = 1;; gen++) { 972952a6212SJordan K. Hubbard /* 973952a6212SJordan K. Hubbard * Generate DOS name with generation number 974952a6212SJordan K. Hubbard */ 975952a6212SJordan K. Hubbard if (!unix2dosfn((const u_char *)cnp->cn_nameptr, cp, 976952a6212SJordan K. Hubbard cnp->cn_namelen, gen)) 977952a6212SJordan K. Hubbard return gen == 1 ? EINVAL : EEXIST; 978952a6212SJordan K. Hubbard 979952a6212SJordan K. Hubbard /* 980952a6212SJordan K. Hubbard * Now look for a dir entry with this exact name 981952a6212SJordan K. Hubbard */ 982952a6212SJordan K. Hubbard for (cn = error = 0; !error; cn++) { 983952a6212SJordan K. Hubbard if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) { 984952a6212SJordan K. Hubbard if (error == E2BIG) /* EOF reached and not found */ 985952a6212SJordan K. Hubbard return 0; 986952a6212SJordan K. Hubbard return error; 987952a6212SJordan K. Hubbard } 988952a6212SJordan K. Hubbard error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); 989952a6212SJordan K. Hubbard if (error) { 990952a6212SJordan K. Hubbard brelse(bp); 991952a6212SJordan K. Hubbard return error; 992952a6212SJordan K. Hubbard } 993952a6212SJordan K. Hubbard for (dentp = (struct direntry *)bp->b_data; 994952a6212SJordan K. Hubbard (char *)dentp < bp->b_data + blsize; 995952a6212SJordan K. Hubbard dentp++) { 996952a6212SJordan K. Hubbard if (dentp->deName[0] == SLOT_EMPTY) { 997952a6212SJordan K. Hubbard /* 998952a6212SJordan K. Hubbard * Last used entry and not found 999952a6212SJordan K. Hubbard */ 1000952a6212SJordan K. Hubbard brelse(bp); 1001952a6212SJordan K. Hubbard return 0; 1002952a6212SJordan K. Hubbard } 1003952a6212SJordan K. Hubbard /* 1004952a6212SJordan K. Hubbard * Ignore volume labels and Win95 entries 1005952a6212SJordan K. Hubbard */ 1006952a6212SJordan K. Hubbard if (dentp->deAttributes & ATTR_VOLUME) 1007952a6212SJordan K. Hubbard continue; 1008952a6212SJordan K. Hubbard if (!bcmp(dentp->deName, cp, 11)) { 1009952a6212SJordan K. Hubbard error = EEXIST; 1010952a6212SJordan K. Hubbard break; 1011952a6212SJordan K. Hubbard } 1012952a6212SJordan K. Hubbard } 1013952a6212SJordan K. Hubbard brelse(bp); 1014952a6212SJordan K. Hubbard } 1015952a6212SJordan K. Hubbard } 1016952a6212SJordan K. Hubbard } 1017952a6212SJordan K. Hubbard 1018952a6212SJordan K. Hubbard /* 1019952a6212SJordan K. Hubbard * Find any Win'95 long filename entry in directory dep 1020952a6212SJordan K. Hubbard */ 1021952a6212SJordan K. Hubbard int 1022952a6212SJordan K. Hubbard findwin95(dep) 1023952a6212SJordan K. Hubbard struct denode *dep; 1024952a6212SJordan K. Hubbard { 1025952a6212SJordan K. Hubbard struct msdosfsmount *pmp = dep->de_pmp; 1026952a6212SJordan K. Hubbard struct direntry *dentp; 1027952a6212SJordan K. Hubbard int blsize; 1028952a6212SJordan K. Hubbard u_long cn; 1029952a6212SJordan K. Hubbard daddr_t bn; 1030952a6212SJordan K. Hubbard struct buf *bp; 1031952a6212SJordan K. Hubbard 1032952a6212SJordan K. Hubbard /* 1033952a6212SJordan K. Hubbard * Read through the directory looking for Win'95 entries 1034952a6212SJordan K. Hubbard * Note: Error currently handled just as EOF XXX 1035952a6212SJordan K. Hubbard */ 1036952a6212SJordan K. Hubbard for (cn = 0;; cn++) { 1037952a6212SJordan K. Hubbard if (pcbmap(dep, cn, &bn, 0, &blsize)) 1038952a6212SJordan K. Hubbard return 0; 1039952a6212SJordan K. Hubbard if (bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp)) { 1040952a6212SJordan K. Hubbard brelse(bp); 1041952a6212SJordan K. Hubbard return 0; 1042952a6212SJordan K. Hubbard } 1043952a6212SJordan K. Hubbard for (dentp = (struct direntry *)bp->b_data; 1044952a6212SJordan K. Hubbard (char *)dentp < bp->b_data + blsize; 1045952a6212SJordan K. Hubbard dentp++) { 1046952a6212SJordan K. Hubbard if (dentp->deName[0] == SLOT_EMPTY) { 1047952a6212SJordan K. Hubbard /* 1048952a6212SJordan K. Hubbard * Last used entry and not found 1049952a6212SJordan K. Hubbard */ 1050952a6212SJordan K. Hubbard brelse(bp); 1051952a6212SJordan K. Hubbard return 0; 1052952a6212SJordan K. Hubbard } 1053952a6212SJordan K. Hubbard if (dentp->deName[0] == SLOT_DELETED) { 1054952a6212SJordan K. Hubbard /* 1055952a6212SJordan K. Hubbard * Ignore deleted files 1056952a6212SJordan K. Hubbard * Note: might be an indication of Win'95 anyway XXX 1057952a6212SJordan K. Hubbard */ 1058952a6212SJordan K. Hubbard continue; 1059952a6212SJordan K. Hubbard } 1060952a6212SJordan K. Hubbard if (dentp->deAttributes == ATTR_WIN95) { 1061952a6212SJordan K. Hubbard brelse(bp); 1062952a6212SJordan K. Hubbard return 1; 1063952a6212SJordan K. Hubbard } 1064952a6212SJordan K. Hubbard } 1065952a6212SJordan K. Hubbard brelse(bp); 1066952a6212SJordan K. Hubbard } 106727a0bc89SDoug Rabson } 1068