Commit | Line | Data |
---|---|---|
da7c5cc6 | 1 | /* |
ad0f93d2 KB |
2 | * Copyright (c) 1989, 1993 |
3 | * The Regents of the University of California. All rights reserved. | |
adb35f79 KB |
4 | * (c) UNIX System Laboratories, Inc. |
5 | * All or some portions of this file are derived from material licensed | |
6 | * to the University of California by American Telephone and Telegraph | |
7 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with | |
8 | * the permission of UNIX System Laboratories, Inc. | |
da7c5cc6 | 9 | * |
b702c21d | 10 | * %sccs.include.redist.c% |
7604aef4 | 11 | * |
adb35f79 | 12 | * @(#)ufs_lookup.c 8.5 (Berkeley) %G% |
da7c5cc6 | 13 | */ |
10873320 | 14 | |
8f7ab13a KB |
15 | #include <sys/param.h> |
16 | #include <sys/namei.h> | |
17 | #include <sys/buf.h> | |
18 | #include <sys/file.h> | |
19 | #include <sys/mount.h> | |
20 | #include <sys/vnode.h> | |
c6f5111d | 21 | |
8f7ab13a KB |
22 | #include <ufs/ufs/quota.h> |
23 | #include <ufs/ufs/inode.h> | |
24 | #include <ufs/ufs/dir.h> | |
25 | #include <ufs/ufs/ufsmount.h> | |
26 | #include <ufs/ufs/ufs_extern.h> | |
10873320 | 27 | |
7604aef4 | 28 | struct nchstats nchstats; |
06406258 | 29 | #ifdef DIAGNOSTIC |
658f5fdc | 30 | int dirchk = 1; |
06406258 KM |
31 | #else |
32 | int dirchk = 0; | |
33 | #endif | |
f93197fc | 34 | |
7657a77a KM |
35 | #define FSFMT(vp) ((vp)->v_mount->mnt_maxsymlinklen <= 0) |
36 | ||
f93197fc | 37 | /* |
7604aef4 | 38 | * Convert a component of a pathname into a pointer to a locked inode. |
6bd0bb92 | 39 | * This is a very central and rather complicated routine. |
4f083fd7 | 40 | * If the file system is not maintained in a strict tree hierarchy, |
7f69b0e6 KM |
41 | * this can result in a deadlock situation (see comments in code below). |
42 | * | |
7166190c KM |
43 | * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending |
44 | * on whether the name is to be looked up, created, renamed, or deleted. | |
7604aef4 KM |
45 | * When CREATE, RENAME, or DELETE is specified, information usable in |
46 | * creating, renaming, or deleting a directory entry may be calculated. | |
47 | * If flag has LOCKPARENT or'ed into it and the target of the pathname | |
48 | * exists, lookup returns both the target and its parent directory locked. | |
49 | * When creating or renaming and LOCKPARENT is specified, the target may | |
50 | * not be ".". When deleting and LOCKPARENT is specified, the target may | |
2c3a3d05 KM |
51 | * be "."., but the caller must check to ensure it does an vrele and vput |
52 | * instead of two vputs. | |
f93197fc | 53 | * |
7604aef4 | 54 | * Overall outline of ufs_lookup: |
f93197fc | 55 | * |
6bd0bb92 | 56 | * check accessibility of directory |
f93197fc | 57 | * look for name in cache, if found, then if at end of path |
7604aef4 | 58 | * and deleting or creating, drop it, else return name |
6bd0bb92 BJ |
59 | * search for name in directory, to found or notfound |
60 | * notfound: | |
7604aef4 | 61 | * if creating, return locked directory, leaving info on available slots |
6bd0bb92 BJ |
62 | * else return error |
63 | * found: | |
64 | * if at end of path and deleting, return information to allow delete | |
7604aef4 | 65 | * if at end of path and rewriting (RENAME and LOCKPARENT), lock target |
4f083fd7 | 66 | * inode and return info to allow rewrite |
7f69b0e6 KM |
67 | * if not at end, add name to cache; if at end and neither creating |
68 | * nor deleting, add name to cache | |
10873320 | 69 | */ |
8f7ab13a | 70 | int |
66090215 | 71 | ufs_lookup(ap) |
5bad4469 | 72 | struct vop_lookup_args /* { |
2b3ef074 KM |
73 | struct vnode *a_dvp; |
74 | struct vnode **a_vpp; | |
75 | struct componentname *a_cnp; | |
5bad4469 | 76 | } */ *ap; |
10873320 | 77 | { |
2b3ef074 KM |
78 | register struct vnode *vdp; /* vnode for directory being searched */ |
79 | register struct inode *dp; /* inode for directory being searched */ | |
8f7ab13a | 80 | struct buf *bp; /* a buffer of directory entries */ |
6bd0bb92 BJ |
81 | register struct direct *ep; /* the current directory entry */ |
82 | int entryoffsetinblock; /* offset of ep in bp's buffer */ | |
6bd0bb92 | 83 | enum {NONE, COMPACT, FOUND} slotstatus; |
7166190c | 84 | doff_t slotoffset; /* offset of area with free space */ |
6bd0bb92 BJ |
85 | int slotsize; /* size of area at slotoffset */ |
86 | int slotfreespace; /* amount of space free in slot */ | |
87 | int slotneeded; /* size of the entry we're seeking */ | |
84ef30ec | 88 | int numdirpasses; /* strategy for directory search */ |
7166190c KM |
89 | doff_t endsearch; /* offset to end directory search */ |
90 | doff_t prevoff; /* prev entry dp->i_offset */ | |
2c3a3d05 | 91 | struct vnode *pdp; /* saved dp during symlink work */ |
66090215 | 92 | struct vnode *tdp; /* returned by VFS_VGET */ |
7166190c | 93 | doff_t enduseful; /* pointer past last used dir slot */ |
8f7ab13a | 94 | u_long bmask; /* block offset mask */ |
7604aef4 KM |
95 | int lockparent; /* 1 => lockparent flag is set */ |
96 | int wantparent; /* 1 => wantparent or lockparent flag */ | |
7657a77a | 97 | int namlen, error; |
2b3ef074 KM |
98 | struct vnode **vpp = ap->a_vpp; |
99 | struct componentname *cnp = ap->a_cnp; | |
100 | struct ucred *cred = cnp->cn_cred; | |
101 | int flags = cnp->cn_flags; | |
102 | int nameiop = cnp->cn_nameiop; | |
7604aef4 | 103 | |
8f7ab13a KB |
104 | bp = NULL; |
105 | slotoffset = -1; | |
2b3ef074 KM |
106 | *vpp = NULL; |
107 | vdp = ap->a_dvp; | |
108 | dp = VTOI(vdp); | |
109 | lockparent = flags & LOCKPARENT; | |
110 | wantparent = flags & (LOCKPARENT|WANTPARENT); | |
6bd0bb92 | 111 | |
a0aead5a | 112 | /* |
6bd0bb92 | 113 | * Check accessiblity of directory. |
a0aead5a | 114 | */ |
f1500ee7 | 115 | if ((dp->i_mode & IFMT) != IFDIR) |
7604aef4 | 116 | return (ENOTDIR); |
2b3ef074 | 117 | if (error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc)) |
7604aef4 | 118 | return (error); |
e47da406 | 119 | |
f93197fc KM |
120 | /* |
121 | * We now have a segment name to search for, and a directory to search. | |
122 | * | |
123 | * Before tediously performing a linear scan of the directory, | |
124 | * check the name cache to see if the directory/name pair | |
7604aef4 | 125 | * we are looking for is known already. |
f93197fc | 126 | */ |
2b3ef074 | 127 | if (error = cache_lookup(vdp, vpp, cnp)) { |
2bd0d28d KM |
128 | int vpid; /* capability number of vnode */ |
129 | ||
130 | if (error == ENOENT) | |
131 | return (error); | |
7604aef4 KM |
132 | /* |
133 | * Get the next vnode in the path. | |
2bd0d28d | 134 | * See comment below starting `Step through' for |
7604aef4 KM |
135 | * an explaination of the locking protocol. |
136 | */ | |
2c3a3d05 | 137 | pdp = vdp; |
2b3ef074 KM |
138 | dp = VTOI(*vpp); |
139 | vdp = *vpp; | |
140 | vpid = vdp->v_id; | |
2c3a3d05 | 141 | if (pdp == vdp) { /* lookup on "." */ |
2b3ef074 | 142 | VREF(vdp); |
27c192da | 143 | error = 0; |
2b3ef074 | 144 | } else if (flags & ISDOTDOT) { |
2c3a3d05 | 145 | VOP_UNLOCK(pdp); |
97104ed5 | 146 | error = vget(vdp, 1); |
2b3ef074 | 147 | if (!error && lockparent && (flags & ISLASTCN)) |
2c3a3d05 | 148 | error = VOP_LOCK(pdp); |
f93197fc | 149 | } else { |
97104ed5 | 150 | error = vget(vdp, 1); |
2b3ef074 | 151 | if (!lockparent || error || !(flags & ISLASTCN)) |
2c3a3d05 | 152 | VOP_UNLOCK(pdp); |
f93197fc | 153 | } |
2bd0d28d KM |
154 | /* |
155 | * Check that the capability number did not change | |
156 | * while we were waiting for the lock. | |
157 | */ | |
27c192da | 158 | if (!error) { |
2b3ef074 | 159 | if (vpid == vdp->v_id) |
27c192da | 160 | return (0); |
2c3a3d05 KM |
161 | vput(vdp); |
162 | if (lockparent && pdp != vdp && (flags & ISLASTCN)) | |
163 | VOP_UNLOCK(pdp); | |
27c192da | 164 | } |
2c3a3d05 KM |
165 | if (error = VOP_LOCK(pdp)) |
166 | return (error); | |
167 | vdp = pdp; | |
168 | dp = VTOI(pdp); | |
2b3ef074 | 169 | *vpp = NULL; |
f93197fc KM |
170 | } |
171 | ||
6459ebe0 | 172 | /* |
6bd0bb92 BJ |
173 | * Suppress search for slots unless creating |
174 | * file and at end of pathname, in which case | |
175 | * we watch for a place to put the new file in | |
176 | * case it doesn't already exist. | |
6459ebe0 | 177 | */ |
6bd0bb92 | 178 | slotstatus = FOUND; |
0fd9d45b | 179 | slotfreespace = slotsize = slotneeded = 0; |
2b3ef074 KM |
180 | if ((nameiop == CREATE || nameiop == RENAME) && |
181 | (flags & ISLASTCN)) { | |
6bd0bb92 | 182 | slotstatus = NONE; |
7166190c | 183 | slotneeded = (sizeof(struct direct) - MAXNAMLEN + |
2b3ef074 | 184 | cnp->cn_namelen + 3) &~ 3; |
6bd0bb92 | 185 | } |
7604aef4 | 186 | |
84ef30ec | 187 | /* |
7604aef4 KM |
188 | * If there is cached information on a previous search of |
189 | * this directory, pick up where we last left off. | |
f93197fc | 190 | * We cache only lookups as these are the most common |
84ef30ec KM |
191 | * and have the greatest payoff. Caching CREATE has little |
192 | * benefit as it usually must search the entire directory | |
193 | * to determine that the entry does not exist. Caching the | |
7604aef4 KM |
194 | * location of the last DELETE or RENAME has not reduced |
195 | * profiling time and hence has been removed in the interest | |
196 | * of simplicity. | |
84ef30ec | 197 | */ |
2b3ef074 KM |
198 | bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; |
199 | if (nameiop != LOOKUP || dp->i_diroff == 0 || | |
f1500ee7 | 200 | dp->i_diroff > dp->i_size) { |
7166190c KM |
201 | entryoffsetinblock = 0; |
202 | dp->i_offset = 0; | |
84ef30ec KM |
203 | numdirpasses = 1; |
204 | } else { | |
f1500ee7 KM |
205 | dp->i_offset = dp->i_diroff; |
206 | if ((entryoffsetinblock = dp->i_offset & bmask) && | |
2b3ef074 | 207 | (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp))) |
8f7ab13a | 208 | return (error); |
84ef30ec | 209 | numdirpasses = 2; |
f93197fc | 210 | nchstats.ncs_2passes++; |
84ef30ec | 211 | } |
0fd9d45b | 212 | prevoff = dp->i_offset; |
84ef30ec | 213 | endsearch = roundup(dp->i_size, DIRBLKSIZ); |
9e7c949b | 214 | enduseful = 0; |
6bd0bb92 | 215 | |
84ef30ec | 216 | searchloop: |
f1500ee7 | 217 | while (dp->i_offset < endsearch) { |
f5039631 | 218 | /* |
b63c6f8d | 219 | * If necessary, get the next directory block. |
f5039631 | 220 | */ |
f1500ee7 | 221 | if ((dp->i_offset & bmask) == 0) { |
f5039631 BJ |
222 | if (bp != NULL) |
223 | brelse(bp); | |
7166190c | 224 | if (error = |
2b3ef074 | 225 | VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)) |
7604aef4 | 226 | return (error); |
6bd0bb92 | 227 | entryoffsetinblock = 0; |
6459ebe0 KM |
228 | } |
229 | /* | |
6bd0bb92 | 230 | * If still looking for a slot, and at a DIRBLKSIZE |
d82d5deb | 231 | * boundary, have to start looking for free space again. |
6459ebe0 | 232 | */ |
6bd0bb92 | 233 | if (slotstatus == NONE && |
7604aef4 | 234 | (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) { |
6bd0bb92 BJ |
235 | slotoffset = -1; |
236 | slotfreespace = 0; | |
237 | } | |
6bd0bb92 | 238 | /* |
d82d5deb KM |
239 | * Get pointer to next entry. |
240 | * Full validation checks are slow, so we only check | |
241 | * enough to insure forward progress through the | |
242 | * directory. Complete checks can be run by patching | |
243 | * "dirchk" to be true. | |
6bd0bb92 | 244 | */ |
cb84e0ab | 245 | ep = (struct direct *)((char *)bp->b_data + entryoffsetinblock); |
a5e62f37 | 246 | if (ep->d_reclen == 0 || |
7657a77a | 247 | dirchk && ufs_dirbadentry(vdp, ep, entryoffsetinblock)) { |
7604aef4 KM |
248 | int i; |
249 | ||
f1500ee7 | 250 | ufs_dirbad(dp, dp->i_offset, "mangled entry"); |
d82d5deb | 251 | i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); |
f1500ee7 | 252 | dp->i_offset += i; |
6bd0bb92 | 253 | entryoffsetinblock += i; |
6459ebe0 KM |
254 | continue; |
255 | } | |
6bd0bb92 | 256 | |
6459ebe0 | 257 | /* |
6bd0bb92 | 258 | * If an appropriate sized slot has not yet been found, |
6459ebe0 KM |
259 | * check to see if one is available. Also accumulate space |
260 | * in the current block so that we can determine if | |
261 | * compaction is viable. | |
262 | */ | |
6bd0bb92 BJ |
263 | if (slotstatus != FOUND) { |
264 | int size = ep->d_reclen; | |
265 | ||
6459ebe0 | 266 | if (ep->d_ino != 0) |
7657a77a | 267 | size -= DIRSIZ(FSFMT(vdp), ep); |
6459ebe0 | 268 | if (size > 0) { |
6bd0bb92 BJ |
269 | if (size >= slotneeded) { |
270 | slotstatus = FOUND; | |
f1500ee7 | 271 | slotoffset = dp->i_offset; |
6bd0bb92 BJ |
272 | slotsize = ep->d_reclen; |
273 | } else if (slotstatus == NONE) { | |
274 | slotfreespace += size; | |
275 | if (slotoffset == -1) | |
f1500ee7 | 276 | slotoffset = dp->i_offset; |
6bd0bb92 BJ |
277 | if (slotfreespace >= slotneeded) { |
278 | slotstatus = COMPACT; | |
f1500ee7 | 279 | slotsize = dp->i_offset + |
d870be74 | 280 | ep->d_reclen - slotoffset; |
6bd0bb92 | 281 | } |
6459ebe0 | 282 | } |
f5039631 | 283 | } |
f5039631 | 284 | } |
6bd0bb92 | 285 | |
f5039631 | 286 | /* |
6bd0bb92 | 287 | * Check for a name match. |
f5039631 | 288 | */ |
6bd0bb92 | 289 | if (ep->d_ino) { |
7657a77a KM |
290 | # if (BYTE_ORDER == LITTLE_ENDIAN) |
291 | if (vdp->v_mount->mnt_maxsymlinklen > 0) | |
292 | namlen = ep->d_namlen; | |
293 | else | |
294 | namlen = ep->d_type; | |
295 | # else | |
296 | namlen = ep->d_namlen; | |
297 | # endif | |
298 | if (namlen == cnp->cn_namelen && | |
2b3ef074 | 299 | !bcmp(cnp->cn_nameptr, ep->d_name, |
7657a77a | 300 | (unsigned)namlen)) { |
7604aef4 KM |
301 | /* |
302 | * Save directory entry's inode number and | |
75a31db8 | 303 | * reclen in ndp->ni_ufs area, and release |
7604aef4 KM |
304 | * directory buffer. |
305 | */ | |
f1500ee7 KM |
306 | dp->i_ino = ep->d_ino; |
307 | dp->i_reclen = ep->d_reclen; | |
7604aef4 | 308 | brelse(bp); |
6bd0bb92 | 309 | goto found; |
7604aef4 | 310 | } |
6bd0bb92 | 311 | } |
f1500ee7 KM |
312 | prevoff = dp->i_offset; |
313 | dp->i_offset += ep->d_reclen; | |
6bd0bb92 | 314 | entryoffsetinblock += ep->d_reclen; |
9e7c949b | 315 | if (ep->d_ino) |
f1500ee7 | 316 | enduseful = dp->i_offset; |
6bd0bb92 | 317 | } |
f93197fc | 318 | /* notfound: */ |
84ef30ec | 319 | /* |
f93197fc | 320 | * If we started in the middle of the directory and failed |
84ef30ec KM |
321 | * to find our target, we must check the beginning as well. |
322 | */ | |
323 | if (numdirpasses == 2) { | |
324 | numdirpasses--; | |
f1500ee7 | 325 | dp->i_offset = 0; |
7604aef4 | 326 | endsearch = dp->i_diroff; |
84ef30ec KM |
327 | goto searchloop; |
328 | } | |
7604aef4 KM |
329 | if (bp != NULL) |
330 | brelse(bp); | |
6bd0bb92 BJ |
331 | /* |
332 | * If creating, and at end of pathname and current | |
4f083fd7 SL |
333 | * directory has not been removed, then can consider |
334 | * allowing file to be created. | |
6bd0bb92 | 335 | */ |
2b3ef074 KM |
336 | if ((nameiop == CREATE || nameiop == RENAME) && |
337 | (flags & ISLASTCN) && dp->i_nlink != 0) { | |
f5039631 | 338 | /* |
6bd0bb92 BJ |
339 | * Access for write is interpreted as allowing |
340 | * creation of files in the directory. | |
f5039631 | 341 | */ |
2b3ef074 | 342 | if (error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) |
7604aef4 | 343 | return (error); |
f5039631 | 344 | /* |
6bd0bb92 BJ |
345 | * Return an indication of where the new directory |
346 | * entry should be put. If we didn't find a slot, | |
f1500ee7 | 347 | * then set dp->i_count to 0 indicating |
75a31db8 KM |
348 | * that the new slot belongs at the end of the |
349 | * directory. If we found a slot, then the new entry | |
f1500ee7 KM |
350 | * can be put in the range from dp->i_offset to |
351 | * dp->i_offset + dp->i_count. | |
f5039631 | 352 | */ |
84ef30ec | 353 | if (slotstatus == NONE) { |
f1500ee7 KM |
354 | dp->i_offset = roundup(dp->i_size, DIRBLKSIZ); |
355 | dp->i_count = 0; | |
356 | enduseful = dp->i_offset; | |
84ef30ec | 357 | } else { |
f1500ee7 KM |
358 | dp->i_offset = slotoffset; |
359 | dp->i_count = slotsize; | |
9e7c949b KM |
360 | if (enduseful < slotoffset + slotsize) |
361 | enduseful = slotoffset + slotsize; | |
5485e062 | 362 | } |
f1500ee7 | 363 | dp->i_endoff = roundup(enduseful, DIRBLKSIZ); |
cf5ef508 | 364 | dp->i_flag |= IN_CHANGE | IN_UPDATE; |
f5039631 | 365 | /* |
6bd0bb92 BJ |
366 | * We return with the directory locked, so that |
367 | * the parameters we set up above will still be | |
368 | * valid if we actually decide to do a direnter(). | |
7604aef4 KM |
369 | * We return ni_vp == NULL to indicate that the entry |
370 | * does not currently exist; we leave a pointer to | |
371 | * the (locked) directory inode in ndp->ni_dvp. | |
75a31db8 KM |
372 | * The pathname buffer is saved so that the name |
373 | * can be obtained later. | |
7604aef4 KM |
374 | * |
375 | * NB - if the directory is unlocked, then this | |
376 | * information cannot be used. | |
f5039631 | 377 | */ |
2b3ef074 | 378 | cnp->cn_flags |= SAVENAME; |
7604aef4 | 379 | if (!lockparent) |
2c3a3d05 | 380 | VOP_UNLOCK(vdp); |
39a892ba | 381 | return (EJUSTRETURN); |
6bd0bb92 | 382 | } |
2bd0d28d KM |
383 | /* |
384 | * Insert name into cache (as non-existent) if appropriate. | |
385 | */ | |
4166c1bf | 386 | if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) |
2b3ef074 | 387 | cache_enter(vdp, *vpp, cnp); |
7604aef4 KM |
388 | return (ENOENT); |
389 | ||
6bd0bb92 | 390 | found: |
f93197fc KM |
391 | if (numdirpasses == 2) |
392 | nchstats.ncs_pass2++; | |
6bd0bb92 BJ |
393 | /* |
394 | * Check that directory length properly reflects presence | |
395 | * of this entry. | |
396 | */ | |
7657a77a | 397 | if (entryoffsetinblock + DIRSIZ(FSFMT(vdp), ep) > dp->i_size) { |
f1500ee7 | 398 | ufs_dirbad(dp, dp->i_offset, "i_size too small"); |
7657a77a | 399 | dp->i_size = entryoffsetinblock + DIRSIZ(FSFMT(vdp), ep); |
cf5ef508 | 400 | dp->i_flag |= IN_CHANGE | IN_UPDATE; |
6bd0bb92 BJ |
401 | } |
402 | ||
403 | /* | |
84ef30ec | 404 | * Found component in pathname. |
f93197fc | 405 | * If the final component of path name, save information |
84ef30ec KM |
406 | * in the cache as to where the entry was found. |
407 | */ | |
2b3ef074 | 408 | if ((flags & ISLASTCN) && nameiop == LOOKUP) |
f1500ee7 | 409 | dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1); |
6bd0bb92 BJ |
410 | |
411 | /* | |
412 | * If deleting, and at end of pathname, return | |
413 | * parameters which can be used to remove file. | |
7604aef4 KM |
414 | * If the wantparent flag isn't set, we return only |
415 | * the directory (in ndp->ni_dvp), otherwise we go | |
4f083fd7 | 416 | * on and lock the inode, being careful with ".". |
6bd0bb92 | 417 | */ |
2b3ef074 | 418 | if (nameiop == DELETE && (flags & ISLASTCN)) { |
6bd0bb92 BJ |
419 | /* |
420 | * Write access to directory required to delete files. | |
421 | */ | |
2b3ef074 | 422 | if (error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) |
7604aef4 | 423 | return (error); |
6bd0bb92 | 424 | /* |
f1500ee7 | 425 | * Return pointer to current entry in dp->i_offset, |
6bd0bb92 | 426 | * and distance past previous entry (if there |
f1500ee7 | 427 | * is a previous entry in this block) in dp->i_count. |
2bd0d28d | 428 | * Save directory inode pointer in ndp->ni_dvp for dirremove(). |
6bd0bb92 | 429 | */ |
f1500ee7 KM |
430 | if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0) |
431 | dp->i_count = 0; | |
6bd0bb92 | 432 | else |
f1500ee7 KM |
433 | dp->i_count = dp->i_offset - prevoff; |
434 | if (dp->i_number == dp->i_ino) { | |
435 | VREF(vdp); | |
2b3ef074 | 436 | *vpp = vdp; |
a48aecd3 | 437 | return (0); |
4f083fd7 | 438 | } |
66090215 | 439 | if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) |
a48aecd3 KM |
440 | return (error); |
441 | /* | |
442 | * If directory is "sticky", then user must own | |
443 | * the directory, or the file in it, else she | |
444 | * may not delete it (unless she's root). This | |
445 | * implements append-only directories. | |
446 | */ | |
447 | if ((dp->i_mode & ISVTX) && | |
2b3ef074 KM |
448 | cred->cr_uid != 0 && |
449 | cred->cr_uid != dp->i_uid && | |
450 | VTOI(tdp)->i_uid != cred->cr_uid) { | |
15298a02 | 451 | vput(tdp); |
a48aecd3 KM |
452 | return (EPERM); |
453 | } | |
2b3ef074 | 454 | *vpp = tdp; |
a48aecd3 | 455 | if (!lockparent) |
2c3a3d05 | 456 | VOP_UNLOCK(vdp); |
7604aef4 | 457 | return (0); |
6bd0bb92 BJ |
458 | } |
459 | ||
4f083fd7 | 460 | /* |
7604aef4 | 461 | * If rewriting (RENAME), return the inode and the |
4f083fd7 SL |
462 | * information required to rewrite the present directory |
463 | * Must get inode of directory entry to verify it's a | |
7604aef4 | 464 | * regular file, or empty directory. |
4f083fd7 | 465 | */ |
2b3ef074 KM |
466 | if (nameiop == RENAME && wantparent && |
467 | (flags & ISLASTCN)) { | |
468 | if (error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) | |
7604aef4 | 469 | return (error); |
4f083fd7 | 470 | /* |
7604aef4 KM |
471 | * Careful about locking second inode. |
472 | * This can only occur if the target is ".". | |
4f083fd7 | 473 | */ |
f1500ee7 | 474 | if (dp->i_number == dp->i_ino) |
7604aef4 | 475 | return (EISDIR); |
66090215 | 476 | if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) |
7604aef4 | 477 | return (error); |
2b3ef074 KM |
478 | *vpp = tdp; |
479 | cnp->cn_flags |= SAVENAME; | |
7604aef4 | 480 | if (!lockparent) |
2c3a3d05 | 481 | VOP_UNLOCK(vdp); |
7604aef4 | 482 | return (0); |
4f083fd7 SL |
483 | } |
484 | ||
6bd0bb92 | 485 | /* |
2c3a3d05 | 486 | * Step through the translation in the name. We do not `vput' the |
7604aef4 | 487 | * directory because we may need it again if a symbolic link |
bde63aa5 KM |
488 | * is relative to the current directory. Instead we save it |
489 | * unlocked as "pdp". We must get the target inode before unlocking | |
490 | * the directory to insure that the inode will not be removed | |
491 | * before we get it. We prevent deadlock by always fetching | |
492 | * inodes from the root, moving down the directory tree. Thus | |
493 | * when following backward pointers ".." we must unlock the | |
494 | * parent directory before getting the requested directory. | |
495 | * There is a potential race condition here if both the current | |
496 | * and parent directories are removed before the `iget' for the | |
497 | * inode associated with ".." returns. We hope that this occurs | |
498 | * infrequently since we cannot avoid this race condition without | |
a4358a25 | 499 | * implementing a sophisticated deadlock detection algorithm. |
bde63aa5 KM |
500 | * Note also that this simple deadlock detection scheme will not |
501 | * work if the file system has any hard links other than ".." | |
502 | * that point backwards in the directory structure. | |
6bd0bb92 | 503 | */ |
2c3a3d05 | 504 | pdp = vdp; |
2b3ef074 | 505 | if (flags & ISDOTDOT) { |
2c3a3d05 | 506 | VOP_UNLOCK(pdp); /* race to get the inode */ |
66090215 | 507 | if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) { |
2c3a3d05 KM |
508 | VOP_LOCK(pdp); |
509 | return (error); | |
510 | } | |
511 | if (lockparent && (flags & ISLASTCN) && | |
512 | (error = VOP_LOCK(pdp))) { | |
513 | vput(tdp); | |
7604aef4 KM |
514 | return (error); |
515 | } | |
2b3ef074 | 516 | *vpp = tdp; |
f1500ee7 | 517 | } else if (dp->i_number == dp->i_ino) { |
2b3ef074 KM |
518 | VREF(vdp); /* we want ourself, ie "." */ |
519 | *vpp = vdp; | |
bde63aa5 | 520 | } else { |
66090215 | 521 | if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) |
7604aef4 | 522 | return (error); |
2b3ef074 | 523 | if (!lockparent || !(flags & ISLASTCN)) |
2c3a3d05 | 524 | VOP_UNLOCK(pdp); |
2b3ef074 | 525 | *vpp = tdp; |
bde63aa5 | 526 | } |
f93197fc KM |
527 | |
528 | /* | |
a5e62f37 | 529 | * Insert name into cache if appropriate. |
f93197fc | 530 | */ |
4166c1bf | 531 | if (cnp->cn_flags & MAKEENTRY) |
2b3ef074 | 532 | cache_enter(vdp, *vpp, cnp); |
7604aef4 | 533 | return (0); |
10873320 BJ |
534 | } |
535 | ||
8f7ab13a KB |
536 | void |
537 | ufs_dirbad(ip, offset, how) | |
6bd0bb92 | 538 | struct inode *ip; |
7166190c | 539 | doff_t offset; |
6bd0bb92 BJ |
540 | char *how; |
541 | { | |
8f7ab13a | 542 | struct mount *mp; |
6bd0bb92 | 543 | |
8f7ab13a KB |
544 | mp = ITOV(ip)->v_mount; |
545 | (void)printf("%s: bad dir ino %d at offset %d: %s\n", | |
546 | mp->mnt_stat.f_mntonname, ip->i_number, offset, how); | |
547 | if ((mp->mnt_stat.f_flags & MNT_RDONLY) == 0) | |
0f3242db | 548 | panic("bad dir"); |
6bd0bb92 BJ |
549 | } |
550 | ||
d82d5deb KM |
551 | /* |
552 | * Do consistency checking on a directory entry: | |
553 | * record length must be multiple of 4 | |
d82d5deb KM |
554 | * entry must fit in rest of its DIRBLKSIZ block |
555 | * record must be large enough to contain entry | |
556 | * name is not longer than MAXNAMLEN | |
557 | * name must be as long as advertised, and null terminated | |
558 | */ | |
8f7ab13a | 559 | int |
7657a77a KM |
560 | ufs_dirbadentry(dp, ep, entryoffsetinblock) |
561 | struct vnode *dp; | |
6bd0bb92 | 562 | register struct direct *ep; |
d82d5deb | 563 | int entryoffsetinblock; |
6bd0bb92 | 564 | { |
6bd0bb92 | 565 | register int i; |
7657a77a | 566 | int namlen; |
6bd0bb92 | 567 | |
7657a77a KM |
568 | # if (BYTE_ORDER == LITTLE_ENDIAN) |
569 | if (dp->v_mount->mnt_maxsymlinklen > 0) | |
570 | namlen = ep->d_namlen; | |
571 | else | |
572 | namlen = ep->d_type; | |
573 | # else | |
574 | namlen = ep->d_namlen; | |
575 | # endif | |
a5e62f37 | 576 | if ((ep->d_reclen & 0x3) != 0 || |
d82d5deb | 577 | ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) || |
7657a77a | 578 | ep->d_reclen < DIRSIZ(FSFMT(dp), ep) || namlen > MAXNAMLEN) { |
8f7ab13a KB |
579 | /*return (1); */ |
580 | printf("First bad\n"); | |
581 | goto bad; | |
582 | } | |
7657a77a | 583 | for (i = 0; i < namlen; i++) |
8f7ab13a KB |
584 | if (ep->d_name[i] == '\0') { |
585 | /*return (1); */ | |
586 | printf("Second bad\n"); | |
587 | goto bad; | |
588 | } | |
589 | if (ep->d_name[i]) | |
590 | goto bad; | |
6bd0bb92 | 591 | return (ep->d_name[i]); |
8f7ab13a | 592 | bad: |
8f7ab13a | 593 | return(1); |
6bd0bb92 BJ |
594 | } |
595 | ||
6bd0bb92 BJ |
596 | /* |
597 | * Write a directory entry after a call to namei, using the parameters | |
75a31db8 | 598 | * that it left in nameidata. The argument ip is the inode which the new |
f1500ee7 KM |
599 | * directory entry will refer to. Dvp is a pointer to the directory to |
600 | * be written, which was left locked by namei. Remaining parameters | |
601 | * (dp->i_offset, dp->i_count) indicate how the space for the new | |
602 | * entry is to be obtained. | |
6bd0bb92 | 603 | */ |
8f7ab13a | 604 | int |
d5a78f43 | 605 | ufs_direnter(ip, dvp, cnp) |
6bd0bb92 | 606 | struct inode *ip; |
cfef4373 JH |
607 | struct vnode *dvp; |
608 | register struct componentname *cnp; | |
f5039631 | 609 | { |
6bd0bb92 | 610 | register struct direct *ep, *nep; |
8f7ab13a | 611 | register struct inode *dp; |
6bd0bb92 | 612 | struct buf *bp; |
8f7ab13a KB |
613 | struct direct newdir; |
614 | struct iovec aiov; | |
8f7ab13a | 615 | struct uio auio; |
b32450f4 | 616 | u_int dsize; |
8f7ab13a | 617 | int error, loc, newentrysize, spacefree; |
6bd0bb92 | 618 | char *dirbuf; |
f5039631 | 619 | |
75a31db8 | 620 | #ifdef DIAGNOSTIC |
cfef4373 | 621 | if ((cnp->cn_flags & SAVENAME) == 0) |
75a31db8 KM |
622 | panic("direnter: missing name"); |
623 | #endif | |
15298a02 | 624 | dp = VTOI(dvp); |
75a31db8 | 625 | newdir.d_ino = ip->i_number; |
cfef4373 JH |
626 | newdir.d_namlen = cnp->cn_namelen; |
627 | bcopy(cnp->cn_nameptr, newdir.d_name, (unsigned)cnp->cn_namelen + 1); | |
7657a77a KM |
628 | if (dvp->v_mount->mnt_maxsymlinklen > 0) |
629 | newdir.d_type = IFTODT(ip->i_mode); | |
630 | else { | |
631 | newdir.d_type = 0; | |
632 | # if (BYTE_ORDER == LITTLE_ENDIAN) | |
633 | { u_char tmp = newdir.d_namlen; | |
634 | newdir.d_namlen = newdir.d_type; | |
635 | newdir.d_type = tmp; } | |
636 | # endif | |
637 | } | |
638 | newentrysize = DIRSIZ(FSFMT(dvp), &newdir); | |
f1500ee7 | 639 | if (dp->i_count == 0) { |
6bd0bb92 | 640 | /* |
f1500ee7 KM |
641 | * If dp->i_count is 0, then namei could find no |
642 | * space in the directory. Here, dp->i_offset will | |
75a31db8 KM |
643 | * be on a directory block boundary and we will write the |
644 | * new entry into a fresh block. | |
6bd0bb92 | 645 | */ |
f1500ee7 | 646 | if (dp->i_offset & (DIRBLKSIZ - 1)) |
cf5ef508 | 647 | panic("ufs_direnter: newblk"); |
f1500ee7 | 648 | auio.uio_offset = dp->i_offset; |
75a31db8 KM |
649 | newdir.d_reclen = DIRBLKSIZ; |
650 | auio.uio_resid = newentrysize; | |
651 | aiov.iov_len = newentrysize; | |
652 | aiov.iov_base = (caddr_t)&newdir; | |
653 | auio.uio_iov = &aiov; | |
654 | auio.uio_iovcnt = 1; | |
655 | auio.uio_rw = UIO_WRITE; | |
656 | auio.uio_segflg = UIO_SYSSPACE; | |
657 | auio.uio_procp = (struct proc *)0; | |
cfef4373 | 658 | error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred); |
15298a02 | 659 | if (DIRBLKSIZ > |
cc173077 | 660 | VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize) |
8f7ab13a KB |
661 | /* XXX should grow with balloc() */ |
662 | panic("ufs_direnter: frag size"); | |
663 | else if (!error) { | |
23de9f20 | 664 | dp->i_size = roundup(dp->i_size, DIRBLKSIZ); |
cf5ef508 | 665 | dp->i_flag |= IN_CHANGE; |
15365e07 | 666 | } |
f2a5ad78 | 667 | return (error); |
6bd0bb92 BJ |
668 | } |
669 | ||
670 | /* | |
f1500ee7 KM |
671 | * If dp->i_count is non-zero, then namei found space |
672 | * for the new entry in the range dp->i_offset to | |
673 | * dp->i_offset + dp->i_count in the directory. | |
75a31db8 KM |
674 | * To use this space, we may have to compact the entries located |
675 | * there, by copying them together towards the beginning of the | |
676 | * block, leaving the free space in one usable chunk at the end. | |
6bd0bb92 BJ |
677 | */ |
678 | ||
679 | /* | |
680 | * Increase size of directory if entry eats into new space. | |
681 | * This should never push the size past a new multiple of | |
682 | * DIRBLKSIZE. | |
23de9f20 KM |
683 | * |
684 | * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. | |
6bd0bb92 | 685 | */ |
f1500ee7 KM |
686 | if (dp->i_offset + dp->i_count > dp->i_size) |
687 | dp->i_size = dp->i_offset + dp->i_count; | |
6bd0bb92 | 688 | /* |
7604aef4 | 689 | * Get the block containing the space for the new directory entry. |
6bd0bb92 | 690 | */ |
7166190c | 691 | if (error = VOP_BLKATOFF(dvp, (off_t)dp->i_offset, &dirbuf, &bp)) |
7604aef4 | 692 | return (error); |
6bd0bb92 | 693 | /* |
75a31db8 KM |
694 | * Find space for the new entry. In the simple case, the entry at |
695 | * offset base will have the space. If it does not, then namei | |
f1500ee7 KM |
696 | * arranged that compacting the region dp->i_offset to |
697 | * dp->i_offset + dp->i_count would yield the | |
75a31db8 | 698 | * space. |
6bd0bb92 BJ |
699 | */ |
700 | ep = (struct direct *)dirbuf; | |
7657a77a | 701 | dsize = DIRSIZ(FSFMT(dvp), ep); |
efa3a91c | 702 | spacefree = ep->d_reclen - dsize; |
f1500ee7 | 703 | for (loc = ep->d_reclen; loc < dp->i_count; ) { |
6bd0bb92 BJ |
704 | nep = (struct direct *)(dirbuf + loc); |
705 | if (ep->d_ino) { | |
706 | /* trim the existing slot */ | |
707 | ep->d_reclen = dsize; | |
708 | ep = (struct direct *)((char *)ep + dsize); | |
709 | } else { | |
710 | /* overwrite; nothing there; header is ours */ | |
7604aef4 | 711 | spacefree += dsize; |
6bd0bb92 | 712 | } |
7657a77a | 713 | dsize = DIRSIZ(FSFMT(dvp), nep); |
efa3a91c | 714 | spacefree += nep->d_reclen - dsize; |
6bd0bb92 | 715 | loc += nep->d_reclen; |
9f024ffc | 716 | bcopy((caddr_t)nep, (caddr_t)ep, dsize); |
6bd0bb92 BJ |
717 | } |
718 | /* | |
719 | * Update the pointer fields in the previous entry (if any), | |
720 | * copy in the new entry, and write out the block. | |
721 | */ | |
722 | if (ep->d_ino == 0) { | |
efa3a91c | 723 | if (spacefree + dsize < newentrysize) |
cf5ef508 | 724 | panic("ufs_direnter: compact1"); |
75a31db8 | 725 | newdir.d_reclen = spacefree + dsize; |
6bd0bb92 | 726 | } else { |
efa3a91c | 727 | if (spacefree < newentrysize) |
cf5ef508 | 728 | panic("ufs_direnter: compact2"); |
75a31db8 | 729 | newdir.d_reclen = spacefree; |
6bd0bb92 BJ |
730 | ep->d_reclen = dsize; |
731 | ep = (struct direct *)((char *)ep + dsize); | |
732 | } | |
75a31db8 | 733 | bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize); |
15298a02 | 734 | error = VOP_BWRITE(bp); |
cf5ef508 | 735 | dp->i_flag |= IN_CHANGE | IN_UPDATE; |
f1500ee7 | 736 | if (!error && dp->i_endoff && dp->i_endoff < dp->i_size) |
5bad4469 KM |
737 | error = VOP_TRUNCATE(dvp, (off_t)dp->i_endoff, IO_SYNC, |
738 | cnp->cn_cred, cnp->cn_proc); | |
f2a5ad78 | 739 | return (error); |
6bd0bb92 BJ |
740 | } |
741 | ||
4f083fd7 | 742 | /* |
5c299d0e KM |
743 | * Remove a directory entry after a call to namei, using |
744 | * the parameters which it left in nameidata. The entry | |
f1500ee7 KM |
745 | * dp->i_offset contains the offset into the directory of the |
746 | * entry to be eliminated. The dp->i_count field contains the | |
4f083fd7 SL |
747 | * size of the previous record in the directory. If this |
748 | * is 0, the first entry is being deleted, so we need only | |
749 | * zero the inode number to mark the entry as free. If the | |
75a31db8 | 750 | * entry is not the first in the directory, we must reclaim |
4f083fd7 SL |
751 | * the space of the now empty record by adding the record size |
752 | * to the size of the previous entry. | |
753 | */ | |
8f7ab13a | 754 | int |
d5a78f43 | 755 | ufs_dirremove(dvp, cnp) |
cfef4373 JH |
756 | struct vnode *dvp; |
757 | struct componentname *cnp; | |
6bd0bb92 | 758 | { |
8f7ab13a | 759 | register struct inode *dp; |
6bd0bb92 | 760 | struct direct *ep; |
7604aef4 KM |
761 | struct buf *bp; |
762 | int error; | |
6bd0bb92 | 763 | |
cfef4373 | 764 | dp = VTOI(dvp); |
f1500ee7 | 765 | if (dp->i_count == 0) { |
6bd0bb92 BJ |
766 | /* |
767 | * First entry in block: set d_ino to zero. | |
768 | */ | |
7166190c KM |
769 | if (error = |
770 | VOP_BLKATOFF(dvp, (off_t)dp->i_offset, (char **)&ep, &bp)) | |
7604aef4 | 771 | return (error); |
75a31db8 | 772 | ep->d_ino = 0; |
15298a02 | 773 | error = VOP_BWRITE(bp); |
cf5ef508 | 774 | dp->i_flag |= IN_CHANGE | IN_UPDATE; |
75a31db8 KM |
775 | return (error); |
776 | } | |
777 | /* | |
778 | * Collapse new free space into previous entry. | |
779 | */ | |
7166190c KM |
780 | if (error = VOP_BLKATOFF(dvp, (off_t)(dp->i_offset - dp->i_count), |
781 | (char **)&ep, &bp)) | |
75a31db8 | 782 | return (error); |
f1500ee7 | 783 | ep->d_reclen += dp->i_reclen; |
15298a02 | 784 | error = VOP_BWRITE(bp); |
cf5ef508 | 785 | dp->i_flag |= IN_CHANGE | IN_UPDATE; |
7604aef4 | 786 | return (error); |
10873320 | 787 | } |
6459ebe0 | 788 | |
4f083fd7 SL |
789 | /* |
790 | * Rewrite an existing directory entry to point at the inode | |
791 | * supplied. The parameters describing the directory entry are | |
792 | * set up by a call to namei. | |
793 | */ | |
8f7ab13a | 794 | int |
cfef4373 | 795 | ufs_dirrewrite(dp, ip, cnp) |
4f083fd7 | 796 | struct inode *dp, *ip; |
cfef4373 | 797 | struct componentname *cnp; |
4f083fd7 | 798 | { |
75a31db8 | 799 | struct buf *bp; |
8f7ab13a | 800 | struct direct *ep; |
7657a77a | 801 | struct vnode *vdp = ITOV(dp); |
75a31db8 | 802 | int error; |
4f083fd7 | 803 | |
7657a77a | 804 | if (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, (char **)&ep, &bp)) |
75a31db8 KM |
805 | return (error); |
806 | ep->d_ino = ip->i_number; | |
7657a77a KM |
807 | if (vdp->v_mount->mnt_maxsymlinklen > 0) |
808 | ep->d_type = IFTODT(ip->i_mode); | |
15298a02 | 809 | error = VOP_BWRITE(bp); |
cf5ef508 | 810 | dp->i_flag |= IN_CHANGE | IN_UPDATE; |
75a31db8 | 811 | return (error); |
4f083fd7 SL |
812 | } |
813 | ||
4f083fd7 SL |
814 | /* |
815 | * Check if a directory is empty or not. | |
816 | * Inode supplied must be locked. | |
17d11193 SL |
817 | * |
818 | * Using a struct dirtemplate here is not precisely | |
819 | * what we want, but better than using a struct direct. | |
820 | * | |
821 | * NB: does not handle corrupted directories. | |
4f083fd7 | 822 | */ |
8f7ab13a KB |
823 | int |
824 | ufs_dirempty(ip, parentino, cred) | |
05d2bbce | 825 | register struct inode *ip; |
68f21562 | 826 | ino_t parentino; |
7604aef4 | 827 | struct ucred *cred; |
4f083fd7 SL |
828 | { |
829 | register off_t off; | |
17d11193 SL |
830 | struct dirtemplate dbuf; |
831 | register struct direct *dp = (struct direct *)&dbuf; | |
7657a77a | 832 | int error, count, namlen; |
17d11193 | 833 | #define MINDIRSIZ (sizeof (struct dirtemplate) / 2) |
4f083fd7 SL |
834 | |
835 | for (off = 0; off < ip->i_size; off += dp->d_reclen) { | |
b7581321 KM |
836 | error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off, |
837 | UIO_SYSSPACE, IO_NODELOCKED, cred, &count, (struct proc *)0); | |
17d11193 SL |
838 | /* |
839 | * Since we read MINDIRSIZ, residual must | |
840 | * be 0 unless we're at end of file. | |
841 | */ | |
842 | if (error || count != 0) | |
4f083fd7 | 843 | return (0); |
d14b14c3 | 844 | /* avoid infinite loops */ |
a5e62f37 | 845 | if (dp->d_reclen == 0) |
d14b14c3 | 846 | return (0); |
17d11193 | 847 | /* skip empty entries */ |
4f083fd7 SL |
848 | if (dp->d_ino == 0) |
849 | continue; | |
17d11193 | 850 | /* accept only "." and ".." */ |
7657a77a KM |
851 | # if (BYTE_ORDER == LITTLE_ENDIAN) |
852 | if (ITOV(ip)->v_mount->mnt_maxsymlinklen > 0) | |
853 | namlen = dp->d_namlen; | |
854 | else | |
855 | namlen = dp->d_type; | |
856 | # else | |
857 | namlen = dp->d_namlen; | |
858 | # endif | |
859 | if (namlen > 2) | |
17d11193 | 860 | return (0); |
4f083fd7 SL |
861 | if (dp->d_name[0] != '.') |
862 | return (0); | |
17d11193 | 863 | /* |
7657a77a | 864 | * At this point namlen must be 1 or 2. |
17d11193 SL |
865 | * 1 implies ".", 2 implies ".." if second |
866 | * char is also "." | |
867 | */ | |
7657a77a | 868 | if (namlen == 1) |
68f21562 KM |
869 | continue; |
870 | if (dp->d_name[1] == '.' && dp->d_ino == parentino) | |
4f083fd7 SL |
871 | continue; |
872 | return (0); | |
873 | } | |
874 | return (1); | |
875 | } | |
b1aa93b9 KM |
876 | |
877 | /* | |
878 | * Check if source directory is in the path of the target directory. | |
879 | * Target is supplied locked, source is unlocked. | |
2c3a3d05 | 880 | * The target is always vput before returning. |
b1aa93b9 | 881 | */ |
8f7ab13a KB |
882 | int |
883 | ufs_checkpath(source, target, cred) | |
b1aa93b9 | 884 | struct inode *source, *target; |
7604aef4 | 885 | struct ucred *cred; |
b1aa93b9 | 886 | { |
15298a02 | 887 | struct vnode *vp; |
7657a77a | 888 | int error, rootino, namlen; |
2c3a3d05 | 889 | struct dirtemplate dirbuf; |
b1aa93b9 | 890 | |
2c3a3d05 KM |
891 | vp = ITOV(target); |
892 | if (target->i_number == source->i_number) { | |
b1aa93b9 KM |
893 | error = EEXIST; |
894 | goto out; | |
895 | } | |
8f7ab13a | 896 | rootino = ROOTINO; |
8f7ab13a | 897 | error = 0; |
2c3a3d05 | 898 | if (target->i_number == rootino) |
b1aa93b9 KM |
899 | goto out; |
900 | ||
901 | for (;;) { | |
2c3a3d05 | 902 | if (vp->v_type != VDIR) { |
b1aa93b9 KM |
903 | error = ENOTDIR; |
904 | break; | |
905 | } | |
15298a02 | 906 | error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, |
7604aef4 | 907 | sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, |
b7581321 | 908 | IO_NODELOCKED, cred, (int *)0, (struct proc *)0); |
b1aa93b9 KM |
909 | if (error != 0) |
910 | break; | |
7657a77a KM |
911 | # if (BYTE_ORDER == LITTLE_ENDIAN) |
912 | if (vp->v_mount->mnt_maxsymlinklen > 0) | |
913 | namlen = dirbuf.dotdot_namlen; | |
914 | else | |
915 | namlen = dirbuf.dotdot_type; | |
916 | # else | |
917 | namlen = dirbuf.dotdot_namlen; | |
918 | # endif | |
919 | if (namlen != 2 || | |
4a287f19 KM |
920 | dirbuf.dotdot_name[0] != '.' || |
921 | dirbuf.dotdot_name[1] != '.') { | |
b1aa93b9 KM |
922 | error = ENOTDIR; |
923 | break; | |
924 | } | |
925 | if (dirbuf.dotdot_ino == source->i_number) { | |
926 | error = EINVAL; | |
927 | break; | |
928 | } | |
8f7ab13a | 929 | if (dirbuf.dotdot_ino == rootino) |
b1aa93b9 | 930 | break; |
2c3a3d05 KM |
931 | vput(vp); |
932 | if (error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, &vp)) { | |
933 | vp = NULL; | |
b1aa93b9 | 934 | break; |
2c3a3d05 | 935 | } |
b1aa93b9 KM |
936 | } |
937 | ||
938 | out: | |
939 | if (error == ENOTDIR) | |
940 | printf("checkpath: .. not a directory\n"); | |
2c3a3d05 KM |
941 | if (vp != NULL) |
942 | vput(vp); | |
b1aa93b9 KM |
943 | return (error); |
944 | } |