123401Smckusick /*
263376Sbostic * Copyright (c) 1989, 1993
363376Sbostic * The Regents of the University of California. All rights reserved.
465774Sbostic * (c) UNIX System Laboratories, Inc.
565774Sbostic * All or some portions of this file are derived from material licensed
665774Sbostic * to the University of California by American Telephone and Telegraph
765774Sbostic * Co. or Unix System Laboratories, Inc. and are reproduced herein with
865774Sbostic * the permission of UNIX System Laboratories, Inc.
923401Smckusick *
1044538Sbostic * %sccs.include.redist.c%
1137739Smckusick *
12*67266Smckusick * @(#)ufs_lookup.c 8.7 (Berkeley) 05/29/94
1323401Smckusick */
1430Sbill
1551511Sbostic #include <sys/param.h>
1651511Sbostic #include <sys/namei.h>
1751511Sbostic #include <sys/buf.h>
1851511Sbostic #include <sys/file.h>
1951511Sbostic #include <sys/mount.h>
2051511Sbostic #include <sys/vnode.h>
2130Sbill
2251511Sbostic #include <ufs/ufs/quota.h>
2351511Sbostic #include <ufs/ufs/inode.h>
2451511Sbostic #include <ufs/ufs/dir.h>
2551511Sbostic #include <ufs/ufs/ufsmount.h>
2651511Sbostic #include <ufs/ufs/ufs_extern.h>
2747571Skarels
2837739Smckusick struct nchstats nchstats;
2948068Smckusick #ifdef DIAGNOSTIC
3037582Smarc int dirchk = 1;
3148068Smckusick #else
3248068Smckusick int dirchk = 0;
3348068Smckusick #endif
3415798Smckusick
3554607Smckusick #define FSFMT(vp) ((vp)->v_mount->mnt_maxsymlinklen <= 0)
3654607Smckusick
3730Sbill /*
3837739Smckusick * Convert a component of a pathname into a pointer to a locked inode.
397534Sroot * This is a very central and rather complicated routine.
4027268Smckusick * If the file system is not maintained in a strict tree hierarchy,
4127268Smckusick * this can result in a deadlock situation (see comments in code below).
4230Sbill *
4353061Smckusick * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
4453061Smckusick * on whether the name is to be looked up, created, renamed, or deleted.
4537739Smckusick * When CREATE, RENAME, or DELETE is specified, information usable in
4637739Smckusick * creating, renaming, or deleting a directory entry may be calculated.
4737739Smckusick * If flag has LOCKPARENT or'ed into it and the target of the pathname
4837739Smckusick * exists, lookup returns both the target and its parent directory locked.
4937739Smckusick * When creating or renaming and LOCKPARENT is specified, the target may
5037739Smckusick * not be ".". When deleting and LOCKPARENT is specified, the target may
5156803Smckusick * be "."., but the caller must check to ensure it does an vrele and vput
5256803Smckusick * instead of two vputs.
539166Ssam *
5437739Smckusick * Overall outline of ufs_lookup:
559166Ssam *
567534Sroot * check accessibility of directory
5715798Smckusick * look for name in cache, if found, then if at end of path
5837739Smckusick * and deleting or creating, drop it, else return name
597534Sroot * search for name in directory, to found or notfound
607534Sroot * notfound:
6137739Smckusick * if creating, return locked directory, leaving info on available slots
627534Sroot * else return error
637534Sroot * found:
647534Sroot * if at end of path and deleting, return information to allow delete
6537739Smckusick * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
669166Ssam * inode and return info to allow rewrite
6727268Smckusick * if not at end, add name to cache; if at end and neither creating
6827268Smckusick * nor deleting, add name to cache
6930Sbill */
7051511Sbostic int
ufs_lookup(ap)7154649Smckusick ufs_lookup(ap)
7254453Smckusick struct vop_lookup_args /* {
7354529Smckusick struct vnode *a_dvp;
7454529Smckusick struct vnode **a_vpp;
7554529Smckusick struct componentname *a_cnp;
7654453Smckusick } */ *ap;
7730Sbill {
7854529Smckusick register struct vnode *vdp; /* vnode for directory being searched */
7954529Smckusick register struct inode *dp; /* inode for directory being searched */
8051511Sbostic struct buf *bp; /* a buffer of directory entries */
817534Sroot register struct direct *ep; /* the current directory entry */
827534Sroot int entryoffsetinblock; /* offset of ep in bp's buffer */
837534Sroot enum {NONE, COMPACT, FOUND} slotstatus;
8453061Smckusick doff_t slotoffset; /* offset of area with free space */
857534Sroot int slotsize; /* size of area at slotoffset */
867534Sroot int slotfreespace; /* amount of space free in slot */
877534Sroot int slotneeded; /* size of the entry we're seeking */
8815660Smckusick int numdirpasses; /* strategy for directory search */
8953061Smckusick doff_t endsearch; /* offset to end directory search */
9053061Smckusick doff_t prevoff; /* prev entry dp->i_offset */
9156803Smckusick struct vnode *pdp; /* saved dp during symlink work */
9254649Smckusick struct vnode *tdp; /* returned by VFS_VGET */
9353061Smckusick doff_t enduseful; /* pointer past last used dir slot */
9451511Sbostic u_long bmask; /* block offset mask */
9537739Smckusick int lockparent; /* 1 => lockparent flag is set */
9637739Smckusick int wantparent; /* 1 => wantparent or lockparent flag */
9754607Smckusick int namlen, error;
9854529Smckusick struct vnode **vpp = ap->a_vpp;
9954529Smckusick struct componentname *cnp = ap->a_cnp;
10054529Smckusick struct ucred *cred = cnp->cn_cred;
10154529Smckusick int flags = cnp->cn_flags;
10254529Smckusick int nameiop = cnp->cn_nameiop;
10330Sbill
10451511Sbostic bp = NULL;
10551511Sbostic slotoffset = -1;
10654529Smckusick *vpp = NULL;
10754529Smckusick vdp = ap->a_dvp;
10854529Smckusick dp = VTOI(vdp);
10954529Smckusick lockparent = flags & LOCKPARENT;
11054529Smckusick wantparent = flags & (LOCKPARENT|WANTPARENT);
1117534Sroot
1125972Swnj /*
1137534Sroot * Check accessiblity of directory.
11430Sbill */
11552337Smckusick if ((dp->i_mode & IFMT) != IFDIR)
11637739Smckusick return (ENOTDIR);
11754529Smckusick if (error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc))
11837739Smckusick return (error);
1197534Sroot
1207534Sroot /*
12115798Smckusick * We now have a segment name to search for, and a directory to search.
12215798Smckusick *
12315798Smckusick * Before tediously performing a linear scan of the directory,
12415798Smckusick * check the name cache to see if the directory/name pair
12537739Smckusick * we are looking for is known already.
12615798Smckusick */
12754529Smckusick if (error = cache_lookup(vdp, vpp, cnp)) {
12838770Smckusick int vpid; /* capability number of vnode */
12938770Smckusick
13038770Smckusick if (error == ENOENT)
13138770Smckusick return (error);
13237739Smckusick /*
13337739Smckusick * Get the next vnode in the path.
13438770Smckusick * See comment below starting `Step through' for
13537739Smckusick * an explaination of the locking protocol.
13637739Smckusick */
13756803Smckusick pdp = vdp;
13854529Smckusick dp = VTOI(*vpp);
13954529Smckusick vdp = *vpp;
14054529Smckusick vpid = vdp->v_id;
14156803Smckusick if (pdp == vdp) { /* lookup on "." */
14254529Smckusick VREF(vdp);
14339434Smckusick error = 0;
14454529Smckusick } else if (flags & ISDOTDOT) {
14556803Smckusick VOP_UNLOCK(pdp);
14665232Smckusick error = vget(vdp, 1);
14754529Smckusick if (!error && lockparent && (flags & ISLASTCN))
14856803Smckusick error = VOP_LOCK(pdp);
14915798Smckusick } else {
15065232Smckusick error = vget(vdp, 1);
15154529Smckusick if (!lockparent || error || !(flags & ISLASTCN))
15256803Smckusick VOP_UNLOCK(pdp);
15315798Smckusick }
15438770Smckusick /*
15538770Smckusick * Check that the capability number did not change
15638770Smckusick * while we were waiting for the lock.
15738770Smckusick */
15839434Smckusick if (!error) {
15954529Smckusick if (vpid == vdp->v_id)
16039434Smckusick return (0);
16156803Smckusick vput(vdp);
16256803Smckusick if (lockparent && pdp != vdp && (flags & ISLASTCN))
16356803Smckusick VOP_UNLOCK(pdp);
16439434Smckusick }
16556803Smckusick if (error = VOP_LOCK(pdp))
16656803Smckusick return (error);
16756803Smckusick vdp = pdp;
16856803Smckusick dp = VTOI(pdp);
16954529Smckusick *vpp = NULL;
17015798Smckusick }
17115798Smckusick
17215798Smckusick /*
1737534Sroot * Suppress search for slots unless creating
1747534Sroot * file and at end of pathname, in which case
1757534Sroot * we watch for a place to put the new file in
1767534Sroot * case it doesn't already exist.
1776571Smckusic */
1787534Sroot slotstatus = FOUND;
17952950Smckusick slotfreespace = slotsize = slotneeded = 0;
18054529Smckusick if ((nameiop == CREATE || nameiop == RENAME) &&
18154529Smckusick (flags & ISLASTCN)) {
1827534Sroot slotstatus = NONE;
18353061Smckusick slotneeded = (sizeof(struct direct) - MAXNAMLEN +
18454529Smckusick cnp->cn_namelen + 3) &~ 3;
1857534Sroot }
18637739Smckusick
18715660Smckusick /*
18837739Smckusick * If there is cached information on a previous search of
18937739Smckusick * this directory, pick up where we last left off.
19015798Smckusick * We cache only lookups as these are the most common
19115660Smckusick * and have the greatest payoff. Caching CREATE has little
19215660Smckusick * benefit as it usually must search the entire directory
19315660Smckusick * to determine that the entry does not exist. Caching the
19437739Smckusick * location of the last DELETE or RENAME has not reduced
19537739Smckusick * profiling time and hence has been removed in the interest
19637739Smckusick * of simplicity.
19715660Smckusick */
19854529Smckusick bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
19954529Smckusick if (nameiop != LOOKUP || dp->i_diroff == 0 ||
20052337Smckusick dp->i_diroff > dp->i_size) {
20153061Smckusick entryoffsetinblock = 0;
20253061Smckusick dp->i_offset = 0;
20315660Smckusick numdirpasses = 1;
20415660Smckusick } else {
20552337Smckusick dp->i_offset = dp->i_diroff;
20652337Smckusick if ((entryoffsetinblock = dp->i_offset & bmask) &&
20754529Smckusick (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)))
20851511Sbostic return (error);
20915660Smckusick numdirpasses = 2;
21015798Smckusick nchstats.ncs_2passes++;
21115660Smckusick }
21252950Smckusick prevoff = dp->i_offset;
21315660Smckusick endsearch = roundup(dp->i_size, DIRBLKSIZ);
21418027Smckusick enduseful = 0;
2157534Sroot
21615660Smckusick searchloop:
21752337Smckusick while (dp->i_offset < endsearch) {
2185972Swnj /*
21959345Smckusick * If necessary, get the next directory block.
2205972Swnj */
22152337Smckusick if ((dp->i_offset & bmask) == 0) {
2225972Swnj if (bp != NULL)
2235972Swnj brelse(bp);
22453061Smckusick if (error =
22554529Smckusick VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp))
22637739Smckusick return (error);
2277534Sroot entryoffsetinblock = 0;
2285972Swnj }
2295972Swnj /*
2307534Sroot * If still looking for a slot, and at a DIRBLKSIZE
23116657Smckusick * boundary, have to start looking for free space again.
2326571Smckusic */
2337534Sroot if (slotstatus == NONE &&
23437739Smckusick (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
2357534Sroot slotoffset = -1;
2367534Sroot slotfreespace = 0;
2377534Sroot }
2387534Sroot /*
23916657Smckusick * Get pointer to next entry.
24016657Smckusick * Full validation checks are slow, so we only check
24116657Smckusick * enough to insure forward progress through the
24216657Smckusick * directory. Complete checks can be run by patching
24316657Smckusick * "dirchk" to be true.
2447534Sroot */
24564516Sbostic ep = (struct direct *)((char *)bp->b_data + entryoffsetinblock);
24626273Skarels if (ep->d_reclen == 0 ||
24754607Smckusick dirchk && ufs_dirbadentry(vdp, ep, entryoffsetinblock)) {
24837739Smckusick int i;
24937739Smckusick
25052337Smckusick ufs_dirbad(dp, dp->i_offset, "mangled entry");
25116657Smckusick i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
25252337Smckusick dp->i_offset += i;
2537534Sroot entryoffsetinblock += i;
2546571Smckusic continue;
2556571Smckusic }
2567534Sroot
2576571Smckusic /*
2587534Sroot * If an appropriate sized slot has not yet been found,
2596571Smckusic * check to see if one is available. Also accumulate space
2606571Smckusic * in the current block so that we can determine if
2616571Smckusic * compaction is viable.
2626571Smckusic */
2637534Sroot if (slotstatus != FOUND) {
2647534Sroot int size = ep->d_reclen;
2657534Sroot
2666571Smckusic if (ep->d_ino != 0)
26754607Smckusick size -= DIRSIZ(FSFMT(vdp), ep);
2686571Smckusic if (size > 0) {
2697534Sroot if (size >= slotneeded) {
2707534Sroot slotstatus = FOUND;
27152337Smckusick slotoffset = dp->i_offset;
2727534Sroot slotsize = ep->d_reclen;
2737534Sroot } else if (slotstatus == NONE) {
2747534Sroot slotfreespace += size;
2757534Sroot if (slotoffset == -1)
27652337Smckusick slotoffset = dp->i_offset;
2777534Sroot if (slotfreespace >= slotneeded) {
2787534Sroot slotstatus = COMPACT;
27952337Smckusick slotsize = dp->i_offset +
28016688Smckusick ep->d_reclen - slotoffset;
2817534Sroot }
2826571Smckusic }
2836571Smckusic }
2846571Smckusic }
2857534Sroot
2866571Smckusic /*
2877534Sroot * Check for a name match.
2885972Swnj */
2897534Sroot if (ep->d_ino) {
29054607Smckusick # if (BYTE_ORDER == LITTLE_ENDIAN)
29154607Smckusick if (vdp->v_mount->mnt_maxsymlinklen > 0)
29254607Smckusick namlen = ep->d_namlen;
29354607Smckusick else
29454607Smckusick namlen = ep->d_type;
29554607Smckusick # else
29654607Smckusick namlen = ep->d_namlen;
29754607Smckusick # endif
29854607Smckusick if (namlen == cnp->cn_namelen &&
29954529Smckusick !bcmp(cnp->cn_nameptr, ep->d_name,
30054607Smckusick (unsigned)namlen)) {
30137739Smckusick /*
30237739Smckusick * Save directory entry's inode number and
30349738Smckusick * reclen in ndp->ni_ufs area, and release
30437739Smckusick * directory buffer.
30537739Smckusick */
30652337Smckusick dp->i_ino = ep->d_ino;
30752337Smckusick dp->i_reclen = ep->d_reclen;
30837739Smckusick brelse(bp);
3097534Sroot goto found;
31037739Smckusick }
3117534Sroot }
31252337Smckusick prevoff = dp->i_offset;
31352337Smckusick dp->i_offset += ep->d_reclen;
3147534Sroot entryoffsetinblock += ep->d_reclen;
31518027Smckusick if (ep->d_ino)
31652337Smckusick enduseful = dp->i_offset;
3177534Sroot }
31815798Smckusick /* notfound: */
31915660Smckusick /*
32015798Smckusick * If we started in the middle of the directory and failed
32115660Smckusick * to find our target, we must check the beginning as well.
32215660Smckusick */
32315660Smckusick if (numdirpasses == 2) {
32415660Smckusick numdirpasses--;
32552337Smckusick dp->i_offset = 0;
32637739Smckusick endsearch = dp->i_diroff;
32715660Smckusick goto searchloop;
32815660Smckusick }
32937739Smckusick if (bp != NULL)
33037739Smckusick brelse(bp);
3317534Sroot /*
3327534Sroot * If creating, and at end of pathname and current
3339166Ssam * directory has not been removed, then can consider
3349166Ssam * allowing file to be created.
3357534Sroot */
33654529Smckusick if ((nameiop == CREATE || nameiop == RENAME) &&
33754529Smckusick (flags & ISLASTCN) && dp->i_nlink != 0) {
3385972Swnj /*
3397534Sroot * Access for write is interpreted as allowing
3407534Sroot * creation of files in the directory.
3415972Swnj */
34254529Smckusick if (error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc))
34337739Smckusick return (error);
3445972Swnj /*
3457534Sroot * Return an indication of where the new directory
3467534Sroot * entry should be put. If we didn't find a slot,
34752337Smckusick * then set dp->i_count to 0 indicating
34849738Smckusick * that the new slot belongs at the end of the
34949738Smckusick * directory. If we found a slot, then the new entry
35052337Smckusick * can be put in the range from dp->i_offset to
35152337Smckusick * dp->i_offset + dp->i_count.
3525972Swnj */
35315660Smckusick if (slotstatus == NONE) {
35452337Smckusick dp->i_offset = roundup(dp->i_size, DIRBLKSIZ);
35552337Smckusick dp->i_count = 0;
35652337Smckusick enduseful = dp->i_offset;
35715660Smckusick } else {
35852337Smckusick dp->i_offset = slotoffset;
35952337Smckusick dp->i_count = slotsize;
36018027Smckusick if (enduseful < slotoffset + slotsize)
36118027Smckusick enduseful = slotoffset + slotsize;
3625972Swnj }
36352337Smckusick dp->i_endoff = roundup(enduseful, DIRBLKSIZ);
36464599Sbostic dp->i_flag |= IN_CHANGE | IN_UPDATE;
3655972Swnj /*
3667534Sroot * We return with the directory locked, so that
3677534Sroot * the parameters we set up above will still be
3687534Sroot * valid if we actually decide to do a direnter().
36937739Smckusick * We return ni_vp == NULL to indicate that the entry
37037739Smckusick * does not currently exist; we leave a pointer to
37137739Smckusick * the (locked) directory inode in ndp->ni_dvp.
37249738Smckusick * The pathname buffer is saved so that the name
37349738Smckusick * can be obtained later.
37437739Smckusick *
37537739Smckusick * NB - if the directory is unlocked, then this
37637739Smckusick * information cannot be used.
3775972Swnj */
37854529Smckusick cnp->cn_flags |= SAVENAME;
37937739Smckusick if (!lockparent)
38056803Smckusick VOP_UNLOCK(vdp);
38152799Smckusick return (EJUSTRETURN);
3827534Sroot }
38338770Smckusick /*
38438770Smckusick * Insert name into cache (as non-existent) if appropriate.
38538770Smckusick */
38655182Smckusick if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
38754529Smckusick cache_enter(vdp, *vpp, cnp);
38837739Smckusick return (ENOENT);
38937739Smckusick
3907534Sroot found:
39115798Smckusick if (numdirpasses == 2)
39215798Smckusick nchstats.ncs_pass2++;
3937534Sroot /*
3947534Sroot * Check that directory length properly reflects presence
3957534Sroot * of this entry.
3967534Sroot */
39754607Smckusick if (entryoffsetinblock + DIRSIZ(FSFMT(vdp), ep) > dp->i_size) {
39852337Smckusick ufs_dirbad(dp, dp->i_offset, "i_size too small");
39954607Smckusick dp->i_size = entryoffsetinblock + DIRSIZ(FSFMT(vdp), ep);
40064599Sbostic dp->i_flag |= IN_CHANGE | IN_UPDATE;
4017534Sroot }
4027534Sroot
4037534Sroot /*
40415660Smckusick * Found component in pathname.
40515798Smckusick * If the final component of path name, save information
40615660Smckusick * in the cache as to where the entry was found.
4077534Sroot */
40854529Smckusick if ((flags & ISLASTCN) && nameiop == LOOKUP)
40952337Smckusick dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1);
4107534Sroot
4117534Sroot /*
4127534Sroot * If deleting, and at end of pathname, return
4137534Sroot * parameters which can be used to remove file.
41437739Smckusick * If the wantparent flag isn't set, we return only
41537739Smckusick * the directory (in ndp->ni_dvp), otherwise we go
4169166Ssam * on and lock the inode, being careful with ".".
4177534Sroot */
41854529Smckusick if (nameiop == DELETE && (flags & ISLASTCN)) {
4197534Sroot /*
4207534Sroot * Write access to directory required to delete files.
4217534Sroot */
42254529Smckusick if (error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc))
42337739Smckusick return (error);
4247534Sroot /*
42552337Smckusick * Return pointer to current entry in dp->i_offset,
4267534Sroot * and distance past previous entry (if there
42752337Smckusick * is a previous entry in this block) in dp->i_count.
42838770Smckusick * Save directory inode pointer in ndp->ni_dvp for dirremove().
4297534Sroot */
43052337Smckusick if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
43152337Smckusick dp->i_count = 0;
4327534Sroot else
43352337Smckusick dp->i_count = dp->i_offset - prevoff;
43452337Smckusick if (dp->i_number == dp->i_ino) {
43552337Smckusick VREF(vdp);
43654529Smckusick *vpp = vdp;
43741308Smckusick return (0);
4389166Ssam }
43954649Smckusick if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp))
44041308Smckusick return (error);
44141308Smckusick /*
44241308Smckusick * If directory is "sticky", then user must own
44341308Smckusick * the directory, or the file in it, else she
44441308Smckusick * may not delete it (unless she's root). This
44541308Smckusick * implements append-only directories.
44641308Smckusick */
44741308Smckusick if ((dp->i_mode & ISVTX) &&
44854529Smckusick cred->cr_uid != 0 &&
44954529Smckusick cred->cr_uid != dp->i_uid &&
45054529Smckusick VTOI(tdp)->i_uid != cred->cr_uid) {
45151549Smckusick vput(tdp);
45241308Smckusick return (EPERM);
45341308Smckusick }
45454529Smckusick *vpp = tdp;
45541308Smckusick if (!lockparent)
45656803Smckusick VOP_UNLOCK(vdp);
45737739Smckusick return (0);
4587534Sroot }
4597534Sroot
4607534Sroot /*
46137739Smckusick * If rewriting (RENAME), return the inode and the
4629166Ssam * information required to rewrite the present directory
4639166Ssam * Must get inode of directory entry to verify it's a
46437739Smckusick * regular file, or empty directory.
4659166Ssam */
46654529Smckusick if (nameiop == RENAME && wantparent &&
46754529Smckusick (flags & ISLASTCN)) {
46854529Smckusick if (error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc))
46937739Smckusick return (error);
4709166Ssam /*
47137739Smckusick * Careful about locking second inode.
47237739Smckusick * This can only occur if the target is ".".
4739166Ssam */
47452337Smckusick if (dp->i_number == dp->i_ino)
47537739Smckusick return (EISDIR);
47654649Smckusick if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp))
47737739Smckusick return (error);
47854529Smckusick *vpp = tdp;
47954529Smckusick cnp->cn_flags |= SAVENAME;
48037739Smckusick if (!lockparent)
48156803Smckusick VOP_UNLOCK(vdp);
48237739Smckusick return (0);
4839166Ssam }
4849166Ssam
4859166Ssam /*
48656803Smckusick * Step through the translation in the name. We do not `vput' the
48737739Smckusick * directory because we may need it again if a symbolic link
48812011Smckusick * is relative to the current directory. Instead we save it
48912011Smckusick * unlocked as "pdp". We must get the target inode before unlocking
49012011Smckusick * the directory to insure that the inode will not be removed
49112011Smckusick * before we get it. We prevent deadlock by always fetching
49212011Smckusick * inodes from the root, moving down the directory tree. Thus
49312011Smckusick * when following backward pointers ".." we must unlock the
49412011Smckusick * parent directory before getting the requested directory.
49512011Smckusick * There is a potential race condition here if both the current
49666595Sbostic * and parent directories are removed before the VFS_VGET for the
49712011Smckusick * inode associated with ".." returns. We hope that this occurs
49812011Smckusick * infrequently since we cannot avoid this race condition without
49912492Ssam * implementing a sophisticated deadlock detection algorithm.
50012011Smckusick * Note also that this simple deadlock detection scheme will not
50112011Smckusick * work if the file system has any hard links other than ".."
50212011Smckusick * that point backwards in the directory structure.
5037534Sroot */
50456803Smckusick pdp = vdp;
50554529Smckusick if (flags & ISDOTDOT) {
50656803Smckusick VOP_UNLOCK(pdp); /* race to get the inode */
50754649Smckusick if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) {
50856803Smckusick VOP_LOCK(pdp);
50937739Smckusick return (error);
51037739Smckusick }
51156803Smckusick if (lockparent && (flags & ISLASTCN) &&
51256803Smckusick (error = VOP_LOCK(pdp))) {
51356803Smckusick vput(tdp);
51456803Smckusick return (error);
51556803Smckusick }
51654529Smckusick *vpp = tdp;
51752337Smckusick } else if (dp->i_number == dp->i_ino) {
51854529Smckusick VREF(vdp); /* we want ourself, ie "." */
51954529Smckusick *vpp = vdp;
52012011Smckusick } else {
52154649Smckusick if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp))
52237739Smckusick return (error);
52354529Smckusick if (!lockparent || !(flags & ISLASTCN))
52456803Smckusick VOP_UNLOCK(pdp);
52554529Smckusick *vpp = tdp;
52612011Smckusick }
52715798Smckusick
52815798Smckusick /*
52926273Skarels * Insert name into cache if appropriate.
53015798Smckusick */
53155182Smckusick if (cnp->cn_flags & MAKEENTRY)
53254529Smckusick cache_enter(vdp, *vpp, cnp);
53337739Smckusick return (0);
53430Sbill }
53530Sbill
53651511Sbostic void
ufs_dirbad(ip,offset,how)53751511Sbostic ufs_dirbad(ip, offset, how)
5387534Sroot struct inode *ip;
53953061Smckusick doff_t offset;
5407534Sroot char *how;
5417534Sroot {
54251511Sbostic struct mount *mp;
5437534Sroot
54451511Sbostic mp = ITOV(ip)->v_mount;
54551511Sbostic (void)printf("%s: bad dir ino %d at offset %d: %s\n",
54651511Sbostic mp->mnt_stat.f_mntonname, ip->i_number, offset, how);
54751511Sbostic if ((mp->mnt_stat.f_flags & MNT_RDONLY) == 0)
54848058Smckusick panic("bad dir");
5497534Sroot }
5507534Sroot
55116657Smckusick /*
55216657Smckusick * Do consistency checking on a directory entry:
55316657Smckusick * record length must be multiple of 4
55416657Smckusick * entry must fit in rest of its DIRBLKSIZ block
55516657Smckusick * record must be large enough to contain entry
55616657Smckusick * name is not longer than MAXNAMLEN
55716657Smckusick * name must be as long as advertised, and null terminated
55816657Smckusick */
55951511Sbostic int
ufs_dirbadentry(dp,ep,entryoffsetinblock)56054607Smckusick ufs_dirbadentry(dp, ep, entryoffsetinblock)
56154607Smckusick struct vnode *dp;
5627534Sroot register struct direct *ep;
56316657Smckusick int entryoffsetinblock;
5647534Sroot {
5657534Sroot register int i;
56654607Smckusick int namlen;
5677534Sroot
56854607Smckusick # if (BYTE_ORDER == LITTLE_ENDIAN)
56954607Smckusick if (dp->v_mount->mnt_maxsymlinklen > 0)
57054607Smckusick namlen = ep->d_namlen;
57154607Smckusick else
57254607Smckusick namlen = ep->d_type;
57354607Smckusick # else
57454607Smckusick namlen = ep->d_namlen;
57554607Smckusick # endif
57626273Skarels if ((ep->d_reclen & 0x3) != 0 ||
57716657Smckusick ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) ||
57854607Smckusick ep->d_reclen < DIRSIZ(FSFMT(dp), ep) || namlen > MAXNAMLEN) {
57951511Sbostic /*return (1); */
58051511Sbostic printf("First bad\n");
58151511Sbostic goto bad;
58251511Sbostic }
583*67266Smckusick if (ep->d_ino == 0)
584*67266Smckusick return (0);
58554607Smckusick for (i = 0; i < namlen; i++)
58651511Sbostic if (ep->d_name[i] == '\0') {
58751511Sbostic /*return (1); */
58851511Sbostic printf("Second bad\n");
58951511Sbostic goto bad;
59051511Sbostic }
59151511Sbostic if (ep->d_name[i])
59251511Sbostic goto bad;
5937534Sroot return (ep->d_name[i]);
59451511Sbostic bad:
59551511Sbostic return(1);
5967534Sroot }
5977534Sroot
59830Sbill /*
5997534Sroot * Write a directory entry after a call to namei, using the parameters
60049738Smckusick * that it left in nameidata. The argument ip is the inode which the new
60152337Smckusick * directory entry will refer to. Dvp is a pointer to the directory to
60252337Smckusick * be written, which was left locked by namei. Remaining parameters
60352337Smckusick * (dp->i_offset, dp->i_count) indicate how the space for the new
60452337Smckusick * entry is to be obtained.
6057534Sroot */
60651511Sbostic int
ufs_direnter(ip,dvp,cnp)60752292Sheideman ufs_direnter(ip, dvp, cnp)
6087534Sroot struct inode *ip;
60952232Sheideman struct vnode *dvp;
61052232Sheideman register struct componentname *cnp;
6115972Swnj {
6127534Sroot register struct direct *ep, *nep;
61351511Sbostic register struct inode *dp;
6147534Sroot struct buf *bp;
61551511Sbostic struct direct newdir;
61651511Sbostic struct iovec aiov;
61751511Sbostic struct uio auio;
6188631Sroot u_int dsize;
61951511Sbostic int error, loc, newentrysize, spacefree;
6207534Sroot char *dirbuf;
6215972Swnj
62249738Smckusick #ifdef DIAGNOSTIC
62352232Sheideman if ((cnp->cn_flags & SAVENAME) == 0)
62449738Smckusick panic("direnter: missing name");
62549738Smckusick #endif
62651549Smckusick dp = VTOI(dvp);
62749738Smckusick newdir.d_ino = ip->i_number;
62852232Sheideman newdir.d_namlen = cnp->cn_namelen;
62952232Sheideman bcopy(cnp->cn_nameptr, newdir.d_name, (unsigned)cnp->cn_namelen + 1);
63054607Smckusick if (dvp->v_mount->mnt_maxsymlinklen > 0)
63154607Smckusick newdir.d_type = IFTODT(ip->i_mode);
63254607Smckusick else {
63354607Smckusick newdir.d_type = 0;
63454607Smckusick # if (BYTE_ORDER == LITTLE_ENDIAN)
63554607Smckusick { u_char tmp = newdir.d_namlen;
63654607Smckusick newdir.d_namlen = newdir.d_type;
63754607Smckusick newdir.d_type = tmp; }
63854607Smckusick # endif
63954607Smckusick }
64054607Smckusick newentrysize = DIRSIZ(FSFMT(dvp), &newdir);
64152337Smckusick if (dp->i_count == 0) {
6427534Sroot /*
64352337Smckusick * If dp->i_count is 0, then namei could find no
64452337Smckusick * space in the directory. Here, dp->i_offset will
64549738Smckusick * be on a directory block boundary and we will write the
64649738Smckusick * new entry into a fresh block.
6477534Sroot */
64852337Smckusick if (dp->i_offset & (DIRBLKSIZ - 1))
64964599Sbostic panic("ufs_direnter: newblk");
65052337Smckusick auio.uio_offset = dp->i_offset;
65149738Smckusick newdir.d_reclen = DIRBLKSIZ;
65249738Smckusick auio.uio_resid = newentrysize;
65349738Smckusick aiov.iov_len = newentrysize;
65449738Smckusick aiov.iov_base = (caddr_t)&newdir;
65549738Smckusick auio.uio_iov = &aiov;
65649738Smckusick auio.uio_iovcnt = 1;
65749738Smckusick auio.uio_rw = UIO_WRITE;
65849738Smckusick auio.uio_segflg = UIO_SYSSPACE;
65949738Smckusick auio.uio_procp = (struct proc *)0;
66052232Sheideman error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
66151549Smckusick if (DIRBLKSIZ >
66251944Smckusick VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
66351511Sbostic /* XXX should grow with balloc() */
66451511Sbostic panic("ufs_direnter: frag size");
66551511Sbostic else if (!error) {
66618104Smckusick dp->i_size = roundup(dp->i_size, DIRBLKSIZ);
66764599Sbostic dp->i_flag |= IN_CHANGE;
66843288Smckusick }
66910849Ssam return (error);
6707534Sroot }
6717534Sroot
6727534Sroot /*
67352337Smckusick * If dp->i_count is non-zero, then namei found space
67452337Smckusick * for the new entry in the range dp->i_offset to
67552337Smckusick * dp->i_offset + dp->i_count in the directory.
67649738Smckusick * To use this space, we may have to compact the entries located
67749738Smckusick * there, by copying them together towards the beginning of the
67849738Smckusick * block, leaving the free space in one usable chunk at the end.
6797534Sroot */
6807534Sroot
6817534Sroot /*
6827534Sroot * Increase size of directory if entry eats into new space.
6837534Sroot * This should never push the size past a new multiple of
6847534Sroot * DIRBLKSIZE.
68518104Smckusick *
68618104Smckusick * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
6877534Sroot */
68852337Smckusick if (dp->i_offset + dp->i_count > dp->i_size)
68952337Smckusick dp->i_size = dp->i_offset + dp->i_count;
6907534Sroot /*
69137739Smckusick * Get the block containing the space for the new directory entry.
6927534Sroot */
69353061Smckusick if (error = VOP_BLKATOFF(dvp, (off_t)dp->i_offset, &dirbuf, &bp))
69437739Smckusick return (error);
6957534Sroot /*
69649738Smckusick * Find space for the new entry. In the simple case, the entry at
69749738Smckusick * offset base will have the space. If it does not, then namei
69852337Smckusick * arranged that compacting the region dp->i_offset to
69952337Smckusick * dp->i_offset + dp->i_count would yield the
70049738Smckusick * space.
7017534Sroot */
7027534Sroot ep = (struct direct *)dirbuf;
70354607Smckusick dsize = DIRSIZ(FSFMT(dvp), ep);
70411639Ssam spacefree = ep->d_reclen - dsize;
70552337Smckusick for (loc = ep->d_reclen; loc < dp->i_count; ) {
7067534Sroot nep = (struct direct *)(dirbuf + loc);
7077534Sroot if (ep->d_ino) {
7087534Sroot /* trim the existing slot */
7097534Sroot ep->d_reclen = dsize;
7107534Sroot ep = (struct direct *)((char *)ep + dsize);
7117534Sroot } else {
7127534Sroot /* overwrite; nothing there; header is ours */
71337739Smckusick spacefree += dsize;
7147534Sroot }
71554607Smckusick dsize = DIRSIZ(FSFMT(dvp), nep);
71611639Ssam spacefree += nep->d_reclen - dsize;
7177534Sroot loc += nep->d_reclen;
7187825Sroot bcopy((caddr_t)nep, (caddr_t)ep, dsize);
7197534Sroot }
7207534Sroot /*
7217534Sroot * Update the pointer fields in the previous entry (if any),
7227534Sroot * copy in the new entry, and write out the block.
7237534Sroot */
7247534Sroot if (ep->d_ino == 0) {
72511639Ssam if (spacefree + dsize < newentrysize)
72664599Sbostic panic("ufs_direnter: compact1");
72749738Smckusick newdir.d_reclen = spacefree + dsize;
7287534Sroot } else {
72911639Ssam if (spacefree < newentrysize)
73064599Sbostic panic("ufs_direnter: compact2");
73149738Smckusick newdir.d_reclen = spacefree;
7327534Sroot ep->d_reclen = dsize;
7337534Sroot ep = (struct direct *)((char *)ep + dsize);
7347534Sroot }
73549738Smckusick bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize);
73651549Smckusick error = VOP_BWRITE(bp);
73764599Sbostic dp->i_flag |= IN_CHANGE | IN_UPDATE;
73852337Smckusick if (!error && dp->i_endoff && dp->i_endoff < dp->i_size)
73954453Smckusick error = VOP_TRUNCATE(dvp, (off_t)dp->i_endoff, IO_SYNC,
74054453Smckusick cnp->cn_cred, cnp->cn_proc);
74110849Ssam return (error);
7425972Swnj }
7436571Smckusic
7449166Ssam /*
74538252Smckusick * Remove a directory entry after a call to namei, using
74638252Smckusick * the parameters which it left in nameidata. The entry
74752337Smckusick * dp->i_offset contains the offset into the directory of the
74852337Smckusick * entry to be eliminated. The dp->i_count field contains the
7499166Ssam * size of the previous record in the directory. If this
7509166Ssam * is 0, the first entry is being deleted, so we need only
7519166Ssam * zero the inode number to mark the entry as free. If the
75249738Smckusick * entry is not the first in the directory, we must reclaim
7539166Ssam * the space of the now empty record by adding the record size
7549166Ssam * to the size of the previous entry.
7559166Ssam */
75651511Sbostic int
ufs_dirremove(dvp,cnp)75752292Sheideman ufs_dirremove(dvp, cnp)
75852232Sheideman struct vnode *dvp;
75952232Sheideman struct componentname *cnp;
7606571Smckusic {
76151511Sbostic register struct inode *dp;
7627534Sroot struct direct *ep;
76337739Smckusick struct buf *bp;
76437739Smckusick int error;
7656571Smckusic
76652232Sheideman dp = VTOI(dvp);
76752337Smckusick if (dp->i_count == 0) {
7687534Sroot /*
7697534Sroot * First entry in block: set d_ino to zero.
7707534Sroot */
77153061Smckusick if (error =
77253061Smckusick VOP_BLKATOFF(dvp, (off_t)dp->i_offset, (char **)&ep, &bp))
77337739Smckusick return (error);
77449738Smckusick ep->d_ino = 0;
77551549Smckusick error = VOP_BWRITE(bp);
77664599Sbostic dp->i_flag |= IN_CHANGE | IN_UPDATE;
77749738Smckusick return (error);
7787534Sroot }
77949738Smckusick /*
78049738Smckusick * Collapse new free space into previous entry.
78149738Smckusick */
78253061Smckusick if (error = VOP_BLKATOFF(dvp, (off_t)(dp->i_offset - dp->i_count),
78353061Smckusick (char **)&ep, &bp))
78449738Smckusick return (error);
78552337Smckusick ep->d_reclen += dp->i_reclen;
78651549Smckusick error = VOP_BWRITE(bp);
78764599Sbostic dp->i_flag |= IN_CHANGE | IN_UPDATE;
78837739Smckusick return (error);
7896571Smckusic }
7907534Sroot
7917605Ssam /*
7929166Ssam * Rewrite an existing directory entry to point at the inode
7939166Ssam * supplied. The parameters describing the directory entry are
7949166Ssam * set up by a call to namei.
7959166Ssam */
79651511Sbostic int
ufs_dirrewrite(dp,ip,cnp)79752232Sheideman ufs_dirrewrite(dp, ip, cnp)
7989166Ssam struct inode *dp, *ip;
79952232Sheideman struct componentname *cnp;
8009166Ssam {
80151511Sbostic struct buf *bp;
80249738Smckusick struct direct *ep;
80354607Smckusick struct vnode *vdp = ITOV(dp);
80449738Smckusick int error;
8059166Ssam
80654607Smckusick if (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, (char **)&ep, &bp))
80749738Smckusick return (error);
80849738Smckusick ep->d_ino = ip->i_number;
80954607Smckusick if (vdp->v_mount->mnt_maxsymlinklen > 0)
81054607Smckusick ep->d_type = IFTODT(ip->i_mode);
81151549Smckusick error = VOP_BWRITE(bp);
81264599Sbostic dp->i_flag |= IN_CHANGE | IN_UPDATE;
81349738Smckusick return (error);
8149166Ssam }
8159166Ssam
8169166Ssam /*
8179166Ssam * Check if a directory is empty or not.
8189166Ssam * Inode supplied must be locked.
81912817Ssam *
82012817Ssam * Using a struct dirtemplate here is not precisely
82112817Ssam * what we want, but better than using a struct direct.
82212817Ssam *
82312817Ssam * NB: does not handle corrupted directories.
8249166Ssam */
82551511Sbostic int
ufs_dirempty(ip,parentino,cred)82651511Sbostic ufs_dirempty(ip, parentino, cred)
8279863Ssam register struct inode *ip;
82816777Smckusick ino_t parentino;
82937739Smckusick struct ucred *cred;
8309166Ssam {
8319166Ssam register off_t off;
83212817Ssam struct dirtemplate dbuf;
83312817Ssam register struct direct *dp = (struct direct *)&dbuf;
83454607Smckusick int error, count, namlen;
83512817Ssam #define MINDIRSIZ (sizeof (struct dirtemplate) / 2)
8369166Ssam
8379166Ssam for (off = 0; off < ip->i_size; off += dp->d_reclen) {
83848035Smckusick error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off,
83948035Smckusick UIO_SYSSPACE, IO_NODELOCKED, cred, &count, (struct proc *)0);
84012817Ssam /*
84112817Ssam * Since we read MINDIRSIZ, residual must
84212817Ssam * be 0 unless we're at end of file.
84312817Ssam */
84412817Ssam if (error || count != 0)
8459166Ssam return (0);
84624534Smckusick /* avoid infinite loops */
84726273Skarels if (dp->d_reclen == 0)
84824534Smckusick return (0);
84912817Ssam /* skip empty entries */
8509166Ssam if (dp->d_ino == 0)
8519166Ssam continue;
85212817Ssam /* accept only "." and ".." */
85354607Smckusick # if (BYTE_ORDER == LITTLE_ENDIAN)
85454607Smckusick if (ITOV(ip)->v_mount->mnt_maxsymlinklen > 0)
85554607Smckusick namlen = dp->d_namlen;
85654607Smckusick else
85754607Smckusick namlen = dp->d_type;
85854607Smckusick # else
85954607Smckusick namlen = dp->d_namlen;
86054607Smckusick # endif
86154607Smckusick if (namlen > 2)
86212817Ssam return (0);
8639166Ssam if (dp->d_name[0] != '.')
8649166Ssam return (0);
86512817Ssam /*
86654607Smckusick * At this point namlen must be 1 or 2.
86712817Ssam * 1 implies ".", 2 implies ".." if second
86812817Ssam * char is also "."
86912817Ssam */
87054607Smckusick if (namlen == 1)
8719166Ssam continue;
87216777Smckusick if (dp->d_name[1] == '.' && dp->d_ino == parentino)
87316777Smckusick continue;
8749166Ssam return (0);
8759166Ssam }
8769166Ssam return (1);
8779166Ssam }
87812815Smckusick
87912815Smckusick /*
88012815Smckusick * Check if source directory is in the path of the target directory.
88112815Smckusick * Target is supplied locked, source is unlocked.
88256803Smckusick * The target is always vput before returning.
88312815Smckusick */
88451511Sbostic int
ufs_checkpath(source,target,cred)88551511Sbostic ufs_checkpath(source, target, cred)
88612815Smckusick struct inode *source, *target;
88737739Smckusick struct ucred *cred;
88812815Smckusick {
88951549Smckusick struct vnode *vp;
89054607Smckusick int error, rootino, namlen;
89156803Smckusick struct dirtemplate dirbuf;
89212815Smckusick
89356803Smckusick vp = ITOV(target);
89456803Smckusick if (target->i_number == source->i_number) {
89512815Smckusick error = EEXIST;
89612815Smckusick goto out;
89712815Smckusick }
89851511Sbostic rootino = ROOTINO;
89951511Sbostic error = 0;
90056803Smckusick if (target->i_number == rootino)
90112815Smckusick goto out;
90212815Smckusick
90312815Smckusick for (;;) {
90456803Smckusick if (vp->v_type != VDIR) {
90512815Smckusick error = ENOTDIR;
90612815Smckusick break;
90712815Smckusick }
90851549Smckusick error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
90937739Smckusick sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
91048035Smckusick IO_NODELOCKED, cred, (int *)0, (struct proc *)0);
91112815Smckusick if (error != 0)
91212815Smckusick break;
91354607Smckusick # if (BYTE_ORDER == LITTLE_ENDIAN)
91454607Smckusick if (vp->v_mount->mnt_maxsymlinklen > 0)
91554607Smckusick namlen = dirbuf.dotdot_namlen;
91654607Smckusick else
91754607Smckusick namlen = dirbuf.dotdot_type;
91854607Smckusick # else
91954607Smckusick namlen = dirbuf.dotdot_namlen;
92054607Smckusick # endif
92154607Smckusick if (namlen != 2 ||
92216658Smckusick dirbuf.dotdot_name[0] != '.' ||
92316658Smckusick dirbuf.dotdot_name[1] != '.') {
92412815Smckusick error = ENOTDIR;
92512815Smckusick break;
92612815Smckusick }
92712815Smckusick if (dirbuf.dotdot_ino == source->i_number) {
92812815Smckusick error = EINVAL;
92912815Smckusick break;
93012815Smckusick }
93151511Sbostic if (dirbuf.dotdot_ino == rootino)
93212815Smckusick break;
93356803Smckusick vput(vp);
93456803Smckusick if (error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, &vp)) {
93556803Smckusick vp = NULL;
93612815Smckusick break;
93756803Smckusick }
93812815Smckusick }
93912815Smckusick
94012815Smckusick out:
94112815Smckusick if (error == ENOTDIR)
94212815Smckusick printf("checkpath: .. not a directory\n");
94356803Smckusick if (vp != NULL)
94456803Smckusick vput(vp);
94512815Smckusick return (error);
94612815Smckusick }
947