LOCKPARENT flag means lock parent even on lookup
[unix-history] / usr / src / sys / ufs / ffs / ufs_lookup.c
CommitLineData
da7c5cc6 1/*
7604aef4
KM
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
da7c5cc6 4 *
7604aef4
KM
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 *
a6ad75d0 17 * @(#)ufs_lookup.c 7.8 (Berkeley) %G%
da7c5cc6 18 */
10873320 19
94368568 20#include "param.h"
94368568
JB
21#include "user.h"
22#include "buf.h"
7604aef4
KM
23#include "file.h"
24#include "vnode.h"
25#include "../ufs/inode.h"
26#include "../ufs/fs.h"
10873320 27
7604aef4
KM
28struct vnode *cache_lookup();
29struct nchstats nchstats;
658f5fdc 30int dirchk = 1;
f93197fc
KM
31
32/*
7604aef4 33 * Convert a component of a pathname into a pointer to a locked inode.
6bd0bb92 34 * This is a very central and rather complicated routine.
4f083fd7 35 * If the file system is not maintained in a strict tree hierarchy,
7f69b0e6
KM
36 * this can result in a deadlock situation (see comments in code below).
37 *
7604aef4
KM
38 * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
39 * whether the name is to be looked up, created, renamed, or deleted.
40 * When CREATE, RENAME, or DELETE is specified, information usable in
41 * creating, renaming, or deleting a directory entry may be calculated.
42 * If flag has LOCKPARENT or'ed into it and the target of the pathname
43 * exists, lookup returns both the target and its parent directory locked.
44 * When creating or renaming and LOCKPARENT is specified, the target may
45 * not be ".". When deleting and LOCKPARENT is specified, the target may
46 * be "."., but the caller must check to ensure it does an vrele and iput
47 * instead of two iputs.
f93197fc 48 *
7604aef4 49 * Overall outline of ufs_lookup:
f93197fc 50 *
6bd0bb92 51 * check accessibility of directory
f93197fc 52 * look for name in cache, if found, then if at end of path
7604aef4 53 * and deleting or creating, drop it, else return name
6bd0bb92
BJ
54 * search for name in directory, to found or notfound
55 * notfound:
7604aef4 56 * if creating, return locked directory, leaving info on available slots
6bd0bb92
BJ
57 * else return error
58 * found:
59 * if at end of path and deleting, return information to allow delete
7604aef4 60 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
4f083fd7 61 * inode and return info to allow rewrite
7f69b0e6
KM
62 * if not at end, add name to cache; if at end and neither creating
63 * nor deleting, add name to cache
4f083fd7 64 *
7604aef4 65 * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode unlocked.
10873320 66 */
7604aef4
KM
67ufs_lookup(vp, ndp)
68 struct vnode *vp;
d870be74 69 register struct nameidata *ndp;
10873320 70{
7604aef4 71 register struct vnode *vdp; /* vnode copy of dp */
6bd0bb92
BJ
72 register struct inode *dp = 0; /* the directory we are searching */
73 register struct fs *fs; /* file system that directory is in */
7604aef4 74 struct buf *bp = 0; /* a buffer of directory entries */
6bd0bb92
BJ
75 register struct direct *ep; /* the current directory entry */
76 int entryoffsetinblock; /* offset of ep in bp's buffer */
6bd0bb92
BJ
77 enum {NONE, COMPACT, FOUND} slotstatus;
78 int slotoffset = -1; /* offset of area with free space */
79 int slotsize; /* size of area at slotoffset */
80 int slotfreespace; /* amount of space free in slot */
81 int slotneeded; /* size of the entry we're seeking */
84ef30ec
KM
82 int numdirpasses; /* strategy for directory search */
83 int endsearch; /* offset to end directory search */
d870be74 84 int prevoff; /* ndp->ni_offset of previous entry */
6bd0bb92 85 struct inode *pdp; /* saved dp during symlink work */
7604aef4 86 struct inode *tdp; /* returned by iget */
9e7c949b 87 off_t enduseful; /* pointer past last used dir slot */
7604aef4
KM
88 int flag; /* LOOKUP, CREATE, RENAME, or DELETE */
89 int lockparent; /* 1 => lockparent flag is set */
90 int wantparent; /* 1 => wantparent or lockparent flag */
91 int error;
92
93 ndp->ni_dvp = vp;
94 ndp->ni_vp = NULL;
95 dp = VTOI(vp);
6bd0bb92 96 fs = dp->i_fs;
7604aef4
KM
97 lockparent = ndp->ni_nameiop & LOCKPARENT;
98 flag = ndp->ni_nameiop & OPFLAG;
99 wantparent = ndp->ni_nameiop & (LOCKPARENT|WANTPARENT);
6bd0bb92 100
a0aead5a 101 /*
6bd0bb92 102 * Check accessiblity of directory.
a0aead5a 103 */
7604aef4
KM
104 if ((dp->i_mode&IFMT) != IFDIR)
105 return (ENOTDIR);
106 if (error = iaccess(dp, IEXEC, ndp->ni_cred))
107 return (error);
e47da406 108
f93197fc
KM
109 /*
110 * We now have a segment name to search for, and a directory to search.
111 *
112 * Before tediously performing a linear scan of the directory,
113 * check the name cache to see if the directory/name pair
7604aef4 114 * we are looking for is known already.
f93197fc 115 */
7604aef4
KM
116 if (vdp = cache_lookup(ndp)) {
117 /*
118 * Get the next vnode in the path.
119 * See comment above `IUNLOCK' code for
120 * an explaination of the locking protocol.
121 */
122 pdp = dp;
123 dp = VTOI(vdp);
124 if (pdp == dp) {
125 vdp->v_count++;
126 } else if (ndp->ni_isdotdot) {
127 IUNLOCK(pdp);
128 igrab(dp);
f93197fc 129 } else {
7604aef4
KM
130 igrab(dp);
131 IUNLOCK(pdp);
f93197fc 132 }
7604aef4
KM
133 ndp->ni_vp = vdp;
134 return (0);
f93197fc
KM
135 }
136
6459ebe0 137 /*
6bd0bb92
BJ
138 * Suppress search for slots unless creating
139 * file and at end of pathname, in which case
140 * we watch for a place to put the new file in
141 * case it doesn't already exist.
6459ebe0 142 */
6bd0bb92 143 slotstatus = FOUND;
7604aef4 144 if ((flag == CREATE || flag == RENAME) && *ndp->ni_next == 0) {
6bd0bb92
BJ
145 slotstatus = NONE;
146 slotfreespace = 0;
d870be74 147 slotneeded = DIRSIZ(&ndp->ni_dent);
6bd0bb92 148 }
7604aef4 149
84ef30ec 150 /*
7604aef4
KM
151 * If there is cached information on a previous search of
152 * this directory, pick up where we last left off.
f93197fc 153 * We cache only lookups as these are the most common
84ef30ec
KM
154 * and have the greatest payoff. Caching CREATE has little
155 * benefit as it usually must search the entire directory
156 * to determine that the entry does not exist. Caching the
7604aef4
KM
157 * location of the last DELETE or RENAME has not reduced
158 * profiling time and hence has been removed in the interest
159 * of simplicity.
84ef30ec 160 */
7604aef4 161 if (flag != LOOKUP || dp->i_diroff == 0 || dp->i_diroff > dp->i_size) {
d870be74 162 ndp->ni_offset = 0;
84ef30ec
KM
163 numdirpasses = 1;
164 } else {
7604aef4 165 ndp->ni_offset = dp->i_diroff;
d870be74 166 entryoffsetinblock = blkoff(fs, ndp->ni_offset);
84ef30ec 167 if (entryoffsetinblock != 0) {
7604aef4
KM
168 error = blkatoff(dp, ndp->ni_offset, (char **)0, &bp);
169 if (error)
170 return (error);
84ef30ec
KM
171 }
172 numdirpasses = 2;
f93197fc 173 nchstats.ncs_2passes++;
84ef30ec
KM
174 }
175 endsearch = roundup(dp->i_size, DIRBLKSIZ);
9e7c949b 176 enduseful = 0;
6bd0bb92 177
84ef30ec 178searchloop:
d870be74 179 while (ndp->ni_offset < endsearch) {
f5039631
BJ
180 /*
181 * If offset is on a block boundary,
182 * read the next directory block.
183 * Release previous if it exists.
184 */
d870be74 185 if (blkoff(fs, ndp->ni_offset) == 0) {
f5039631
BJ
186 if (bp != NULL)
187 brelse(bp);
7604aef4
KM
188 error = blkatoff(dp, ndp->ni_offset, (char **)0, &bp);
189 if (error)
190 return (error);
6bd0bb92 191 entryoffsetinblock = 0;
6459ebe0
KM
192 }
193 /*
6bd0bb92 194 * If still looking for a slot, and at a DIRBLKSIZE
d82d5deb 195 * boundary, have to start looking for free space again.
6459ebe0 196 */
6bd0bb92 197 if (slotstatus == NONE &&
7604aef4 198 (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
6bd0bb92
BJ
199 slotoffset = -1;
200 slotfreespace = 0;
201 }
6bd0bb92 202 /*
d82d5deb
KM
203 * Get pointer to next entry.
204 * Full validation checks are slow, so we only check
205 * enough to insure forward progress through the
206 * directory. Complete checks can be run by patching
207 * "dirchk" to be true.
6bd0bb92
BJ
208 */
209 ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock);
a5e62f37 210 if (ep->d_reclen == 0 ||
d82d5deb 211 dirchk && dirbadentry(ep, entryoffsetinblock)) {
7604aef4
KM
212 int i;
213
d870be74 214 dirbad(dp, ndp->ni_offset, "mangled entry");
d82d5deb 215 i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
d870be74 216 ndp->ni_offset += i;
6bd0bb92 217 entryoffsetinblock += i;
6459ebe0
KM
218 continue;
219 }
6bd0bb92 220
6459ebe0 221 /*
6bd0bb92 222 * If an appropriate sized slot has not yet been found,
6459ebe0
KM
223 * check to see if one is available. Also accumulate space
224 * in the current block so that we can determine if
225 * compaction is viable.
226 */
6bd0bb92
BJ
227 if (slotstatus != FOUND) {
228 int size = ep->d_reclen;
229
6459ebe0
KM
230 if (ep->d_ino != 0)
231 size -= DIRSIZ(ep);
232 if (size > 0) {
6bd0bb92
BJ
233 if (size >= slotneeded) {
234 slotstatus = FOUND;
d870be74 235 slotoffset = ndp->ni_offset;
6bd0bb92
BJ
236 slotsize = ep->d_reclen;
237 } else if (slotstatus == NONE) {
238 slotfreespace += size;
239 if (slotoffset == -1)
d870be74 240 slotoffset = ndp->ni_offset;
6bd0bb92
BJ
241 if (slotfreespace >= slotneeded) {
242 slotstatus = COMPACT;
d870be74
KM
243 slotsize = ndp->ni_offset +
244 ep->d_reclen - slotoffset;
6bd0bb92 245 }
6459ebe0 246 }
f5039631 247 }
f5039631 248 }
6bd0bb92 249
f5039631 250 /*
6bd0bb92 251 * Check for a name match.
f5039631 252 */
6bd0bb92 253 if (ep->d_ino) {
d870be74 254 if (ep->d_namlen == ndp->ni_dent.d_namlen &&
7604aef4
KM
255 !bcmp(ndp->ni_ptr, ep->d_name,
256 (unsigned)ep->d_namlen)) {
257 /*
258 * Save directory entry's inode number and
259 * reclen in ndp->ni_dent, and release
260 * directory buffer.
261 */
262 ndp->ni_dent.d_ino = ep->d_ino;
263 ndp->ni_dent.d_reclen = ep->d_reclen;
264 brelse(bp);
6bd0bb92 265 goto found;
7604aef4 266 }
6bd0bb92 267 }
d870be74
KM
268 prevoff = ndp->ni_offset;
269 ndp->ni_offset += ep->d_reclen;
6bd0bb92 270 entryoffsetinblock += ep->d_reclen;
9e7c949b
KM
271 if (ep->d_ino)
272 enduseful = ndp->ni_offset;
6bd0bb92 273 }
f93197fc 274/* notfound: */
84ef30ec 275 /*
f93197fc 276 * If we started in the middle of the directory and failed
84ef30ec
KM
277 * to find our target, we must check the beginning as well.
278 */
279 if (numdirpasses == 2) {
280 numdirpasses--;
d870be74 281 ndp->ni_offset = 0;
7604aef4 282 endsearch = dp->i_diroff;
84ef30ec
KM
283 goto searchloop;
284 }
7604aef4
KM
285 if (bp != NULL)
286 brelse(bp);
6bd0bb92
BJ
287 /*
288 * If creating, and at end of pathname and current
4f083fd7
SL
289 * directory has not been removed, then can consider
290 * allowing file to be created.
6bd0bb92 291 */
7604aef4
KM
292 if ((flag == CREATE || flag == RENAME) &&
293 *ndp->ni_next == 0 && dp->i_nlink != 0) {
f5039631 294 /*
6bd0bb92
BJ
295 * Access for write is interpreted as allowing
296 * creation of files in the directory.
f5039631 297 */
7604aef4
KM
298 if (error = iaccess(dp, IWRITE, ndp->ni_cred))
299 return (error);
f5039631 300 /*
6bd0bb92
BJ
301 * Return an indication of where the new directory
302 * entry should be put. If we didn't find a slot,
d870be74
KM
303 * then set ndp->ni_count to 0 indicating that the new
304 * slot belongs at the end of the directory. If we found
305 * a slot, then the new entry can be put in the range
306 * [ndp->ni_offset .. ndp->ni_offset + ndp->ni_count)
f5039631 307 */
84ef30ec 308 if (slotstatus == NONE) {
d870be74
KM
309 ndp->ni_offset = roundup(dp->i_size, DIRBLKSIZ);
310 ndp->ni_count = 0;
9e7c949b 311 enduseful = ndp->ni_offset;
84ef30ec 312 } else {
d870be74
KM
313 ndp->ni_offset = slotoffset;
314 ndp->ni_count = slotsize;
9e7c949b
KM
315 if (enduseful < slotoffset + slotsize)
316 enduseful = slotoffset + slotsize;
5485e062 317 }
9e7c949b 318 ndp->ni_endoff = roundup(enduseful, DIRBLKSIZ);
6bd0bb92 319 dp->i_flag |= IUPD|ICHG;
f5039631 320 /*
6bd0bb92
BJ
321 * We return with the directory locked, so that
322 * the parameters we set up above will still be
323 * valid if we actually decide to do a direnter().
7604aef4
KM
324 * We return ni_vp == NULL to indicate that the entry
325 * does not currently exist; we leave a pointer to
326 * the (locked) directory inode in ndp->ni_dvp.
327 *
328 * NB - if the directory is unlocked, then this
329 * information cannot be used.
f5039631 330 */
7604aef4
KM
331 if (!lockparent)
332 IUNLOCK(dp);
6bd0bb92 333 }
7604aef4
KM
334 return (ENOENT);
335
6bd0bb92 336found:
f93197fc
KM
337 if (numdirpasses == 2)
338 nchstats.ncs_pass2++;
6bd0bb92
BJ
339 /*
340 * Check that directory length properly reflects presence
341 * of this entry.
342 */
4a0415d6 343 if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) {
d870be74 344 dirbad(dp, ndp->ni_offset, "i_size too small");
4a0415d6 345 dp->i_size = entryoffsetinblock + DIRSIZ(ep);
6bd0bb92
BJ
346 dp->i_flag |= IUPD|ICHG;
347 }
348
349 /*
84ef30ec 350 * Found component in pathname.
f93197fc 351 * If the final component of path name, save information
84ef30ec
KM
352 * in the cache as to where the entry was found.
353 */
7604aef4
KM
354 if (*ndp->ni_next == '\0' && flag == LOOKUP)
355 dp->i_diroff = ndp->ni_offset &~ (DIRBLKSIZ - 1);
6bd0bb92
BJ
356
357 /*
358 * If deleting, and at end of pathname, return
359 * parameters which can be used to remove file.
7604aef4
KM
360 * If the wantparent flag isn't set, we return only
361 * the directory (in ndp->ni_dvp), otherwise we go
4f083fd7 362 * on and lock the inode, being careful with ".".
6bd0bb92 363 */
7604aef4 364 if (flag == DELETE && *ndp->ni_next == 0) {
6bd0bb92
BJ
365 /*
366 * Write access to directory required to delete files.
367 */
7604aef4
KM
368 if (error = iaccess(dp, IWRITE, ndp->ni_cred))
369 return (error);
6bd0bb92 370 /*
d870be74 371 * Return pointer to current entry in ndp->ni_offset,
6bd0bb92 372 * and distance past previous entry (if there
d870be74
KM
373 * is a previous entry in this block) in ndp->ni_count.
374 * Save directory inode pointer in ndp->ni_pdir for dirremove().
6bd0bb92 375 */
d870be74
KM
376 if ((ndp->ni_offset&(DIRBLKSIZ-1)) == 0)
377 ndp->ni_count = 0;
6bd0bb92 378 else
d870be74 379 ndp->ni_count = ndp->ni_offset - prevoff;
7604aef4
KM
380 vdp = ITOV(dp);
381 if (dp->i_number == ndp->ni_dent.d_ino) {
382 vdp->v_count++;
383 } else {
384 pdp = dp;
385 if (error = iget(dp, ndp->ni_dent.d_ino, &tdp))
386 return (error);
387 vdp = ITOV(tdp);
388 /*
389 * If directory is "sticky", then user must own
390 * the directory, or the file in it, else he
391 * may not delete it (unless he's root). This
392 * implements append-only directories.
393 */
394 if ((pdp->i_mode & ISVTX) &&
395 ndp->ni_cred->cr_uid != 0 &&
396 ndp->ni_cred->cr_uid != pdp->i_uid &&
397 tdp->i_uid != ndp->ni_cred->cr_uid) {
398 iput(tdp);
399 return (EPERM);
4f083fd7
SL
400 }
401 }
7604aef4
KM
402 ndp->ni_vp = vdp;
403 if (!lockparent)
404 IUNLOCK(pdp);
405 return (0);
6bd0bb92
BJ
406 }
407
4f083fd7 408 /*
7604aef4 409 * If rewriting (RENAME), return the inode and the
4f083fd7
SL
410 * information required to rewrite the present directory
411 * Must get inode of directory entry to verify it's a
7604aef4 412 * regular file, or empty directory.
4f083fd7 413 */
7604aef4
KM
414 if (flag == RENAME && wantparent && *ndp->ni_next == 0) {
415 if (error = iaccess(dp, IWRITE, ndp->ni_cred))
416 return (error);
4f083fd7 417 /*
7604aef4
KM
418 * Careful about locking second inode.
419 * This can only occur if the target is ".".
4f083fd7 420 */
7604aef4
KM
421 if (dp->i_number == ndp->ni_dent.d_ino)
422 return (EISDIR);
423 if (error = iget(dp, ndp->ni_dent.d_ino, &tdp))
424 return (error);
425 ndp->ni_vp = ITOV(tdp);
426 if (!lockparent)
427 IUNLOCK(dp);
428 return (0);
4f083fd7
SL
429 }
430
6bd0bb92 431 /*
7604aef4
KM
432 * Step through the translation in the name. We do not `iput' the
433 * directory because we may need it again if a symbolic link
bde63aa5
KM
434 * is relative to the current directory. Instead we save it
435 * unlocked as "pdp". We must get the target inode before unlocking
436 * the directory to insure that the inode will not be removed
437 * before we get it. We prevent deadlock by always fetching
438 * inodes from the root, moving down the directory tree. Thus
439 * when following backward pointers ".." we must unlock the
440 * parent directory before getting the requested directory.
441 * There is a potential race condition here if both the current
442 * and parent directories are removed before the `iget' for the
443 * inode associated with ".." returns. We hope that this occurs
444 * infrequently since we cannot avoid this race condition without
a4358a25 445 * implementing a sophisticated deadlock detection algorithm.
bde63aa5
KM
446 * Note also that this simple deadlock detection scheme will not
447 * work if the file system has any hard links other than ".."
448 * that point backwards in the directory structure.
6bd0bb92
BJ
449 */
450 pdp = dp;
7604aef4 451 if (ndp->ni_isdotdot) {
56700195 452 IUNLOCK(pdp); /* race to get the inode */
7604aef4
KM
453 if (error = iget(dp, ndp->ni_dent.d_ino, &tdp)) {
454 ILOCK(pdp);
455 return (error);
456 }
a6ad75d0
KM
457 if (lockparent && *ndp->ni_next == '\0')
458 ILOCK(pdp);
7604aef4 459 ndp->ni_vp = ITOV(tdp);
d870be74 460 } else if (dp->i_number == ndp->ni_dent.d_ino) {
7604aef4
KM
461 vdp = ITOV(dp);
462 vdp->v_count++; /* we want ourself, ie "." */
463 ndp->ni_vp = vdp;
bde63aa5 464 } else {
7604aef4
KM
465 if (error = iget(dp, ndp->ni_dent.d_ino, &tdp))
466 return (error);
a6ad75d0
KM
467 if (!lockparent || *ndp->ni_next != '\0')
468 IUNLOCK(pdp);
7604aef4 469 ndp->ni_vp = ITOV(tdp);
bde63aa5 470 }
f93197fc
KM
471
472 /*
a5e62f37 473 * Insert name into cache if appropriate.
f93197fc 474 */
7604aef4
KM
475 if (ndp->ni_makeentry)
476 cache_enter(ndp);
477 return (0);
10873320
BJ
478}
479
f93197fc 480
d870be74 481dirbad(ip, offset, how)
6bd0bb92 482 struct inode *ip;
d870be74 483 off_t offset;
6bd0bb92
BJ
484 char *how;
485{
486
487 printf("%s: bad dir ino %d at offset %d: %s\n",
d870be74 488 ip->i_fs->fs_fsmnt, ip->i_number, offset, how);
6bd0bb92
BJ
489}
490
d82d5deb
KM
491/*
492 * Do consistency checking on a directory entry:
493 * record length must be multiple of 4
d82d5deb
KM
494 * entry must fit in rest of its DIRBLKSIZ block
495 * record must be large enough to contain entry
496 * name is not longer than MAXNAMLEN
497 * name must be as long as advertised, and null terminated
498 */
499dirbadentry(ep, entryoffsetinblock)
6bd0bb92 500 register struct direct *ep;
d82d5deb 501 int entryoffsetinblock;
6bd0bb92 502{
6bd0bb92
BJ
503 register int i;
504
a5e62f37 505 if ((ep->d_reclen & 0x3) != 0 ||
d82d5deb
KM
506 ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) ||
507 ep->d_reclen < DIRSIZ(ep) || ep->d_namlen > MAXNAMLEN)
508 return (1);
6bd0bb92 509 for (i = 0; i < ep->d_namlen; i++)
d870be74 510 if (ep->d_name[i] == '\0')
6bd0bb92
BJ
511 return (1);
512 return (ep->d_name[i]);
513}
514
6bd0bb92
BJ
515/*
516 * Write a directory entry after a call to namei, using the parameters
7604aef4
KM
517 * which it left in nameidata. The argument ip is the inode which the
518 * new directory entry will refer to. The nameidata field ndp->ni_dvp
519 * is a pointer to the directory to be written, which was left locked by
d870be74 520 * namei. Remaining parameters (ndp->ni_offset, ndp->ni_count) indicate
6bd0bb92
BJ
521 * how the space for the new entry is to be gotten.
522 */
d870be74 523direnter(ip, ndp)
6bd0bb92 524 struct inode *ip;
d870be74 525 register struct nameidata *ndp;
f5039631 526{
6bd0bb92 527 register struct direct *ep, *nep;
7604aef4 528 register struct inode *dp = VTOI(ndp->ni_dvp);
6bd0bb92 529 struct buf *bp;
efa3a91c 530 int loc, spacefree, error = 0;
b32450f4
BJ
531 u_int dsize;
532 int newentrysize;
6bd0bb92 533 char *dirbuf;
f5039631 534
d870be74
KM
535 ndp->ni_dent.d_ino = ip->i_number;
536 newentrysize = DIRSIZ(&ndp->ni_dent);
537 if (ndp->ni_count == 0) {
6bd0bb92 538 /*
d870be74
KM
539 * If ndp->ni_count is 0, then namei could find no space in the
540 * directory. In this case ndp->ni_offset will be on a directory
6bd0bb92
BJ
541 * block boundary and we will write the new entry into a fresh
542 * block.
543 */
d870be74 544 if (ndp->ni_offset&(DIRBLKSIZ-1))
6bd0bb92 545 panic("wdir: newblk");
d870be74 546 ndp->ni_dent.d_reclen = DIRBLKSIZ;
9e7c949b 547 error = rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent,
7604aef4
KM
548 newentrysize, ndp->ni_offset, UIO_SYSSPACE, ndp->ni_cred,
549 (int *)0);
23de9f20 550 if (DIRBLKSIZ > dp->i_fs->fs_fsize)
7604aef4 551 panic("wdir: blksize"); /* XXX - should grow w/balloc */
23de9f20
KM
552 else
553 dp->i_size = roundup(dp->i_size, DIRBLKSIZ);
9e7c949b 554 iput(dp);
f2a5ad78 555 return (error);
6bd0bb92
BJ
556 }
557
558 /*
d870be74
KM
559 * If ndp->ni_count is non-zero, then namei found space for the new
560 * entry in the range ndp->ni_offset to ndp->ni_offset + ndp->ni_count.
6bd0bb92
BJ
561 * in the directory. To use this space, we may have to compact
562 * the entries located there, by copying them together towards
563 * the beginning of the block, leaving the free space in
564 * one usable chunk at the end.
565 */
566
567 /*
568 * Increase size of directory if entry eats into new space.
569 * This should never push the size past a new multiple of
570 * DIRBLKSIZE.
23de9f20
KM
571 *
572 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
6bd0bb92 573 */
9e7c949b
KM
574 if (ndp->ni_offset + ndp->ni_count > dp->i_size)
575 dp->i_size = ndp->ni_offset + ndp->ni_count;
6bd0bb92 576 /*
7604aef4 577 * Get the block containing the space for the new directory entry.
6bd0bb92 578 */
7604aef4 579 if (error = blkatoff(dp, ndp->ni_offset, (char **)&dirbuf, &bp)) {
9e7c949b 580 iput(dp);
7604aef4 581 return (error);
4f083fd7 582 }
6bd0bb92
BJ
583 /*
584 * Find space for the new entry. In the simple case, the
585 * entry at offset base will have the space. If it does
586 * not, then namei arranged that compacting the region
d870be74 587 * ndp->ni_offset to ndp->ni_offset+ndp->ni_count would yield the space.
6bd0bb92
BJ
588 */
589 ep = (struct direct *)dirbuf;
590 dsize = DIRSIZ(ep);
efa3a91c 591 spacefree = ep->d_reclen - dsize;
d870be74 592 for (loc = ep->d_reclen; loc < ndp->ni_count; ) {
6bd0bb92
BJ
593 nep = (struct direct *)(dirbuf + loc);
594 if (ep->d_ino) {
595 /* trim the existing slot */
596 ep->d_reclen = dsize;
597 ep = (struct direct *)((char *)ep + dsize);
598 } else {
599 /* overwrite; nothing there; header is ours */
7604aef4 600 spacefree += dsize;
6bd0bb92
BJ
601 }
602 dsize = DIRSIZ(nep);
efa3a91c 603 spacefree += nep->d_reclen - dsize;
6bd0bb92 604 loc += nep->d_reclen;
9f024ffc 605 bcopy((caddr_t)nep, (caddr_t)ep, dsize);
6bd0bb92
BJ
606 }
607 /*
608 * Update the pointer fields in the previous entry (if any),
609 * copy in the new entry, and write out the block.
610 */
611 if (ep->d_ino == 0) {
efa3a91c 612 if (spacefree + dsize < newentrysize)
6bd0bb92 613 panic("wdir: compact1");
d870be74 614 ndp->ni_dent.d_reclen = spacefree + dsize;
6bd0bb92 615 } else {
efa3a91c 616 if (spacefree < newentrysize)
6bd0bb92 617 panic("wdir: compact2");
d870be74 618 ndp->ni_dent.d_reclen = spacefree;
6bd0bb92
BJ
619 ep->d_reclen = dsize;
620 ep = (struct direct *)((char *)ep + dsize);
621 }
d870be74 622 bcopy((caddr_t)&ndp->ni_dent, (caddr_t)ep, (u_int)newentrysize);
7604aef4 623 error = bwrite(bp);
9e7c949b
KM
624 dp->i_flag |= IUPD|ICHG;
625 if (ndp->ni_endoff && ndp->ni_endoff < dp->i_size)
7604aef4 626 error = itrunc(dp, (u_long)ndp->ni_endoff);
9e7c949b 627 iput(dp);
f2a5ad78 628 return (error);
6bd0bb92
BJ
629}
630
4f083fd7
SL
631/*
632 * Remove a directory entry after a call to namei, using the
633 * parameters which it left in the u. area. The u. entry
d870be74
KM
634 * ni_offset contains the offset into the directory of the
635 * entry to be eliminated. The ni_count field contains the
4f083fd7
SL
636 * size of the previous record in the directory. If this
637 * is 0, the first entry is being deleted, so we need only
638 * zero the inode number to mark the entry as free. If the
639 * entry isn't the first in the directory, we must reclaim
640 * the space of the now empty record by adding the record size
641 * to the size of the previous entry.
642 */
d870be74
KM
643dirremove(ndp)
644 register struct nameidata *ndp;
6bd0bb92 645{
7604aef4 646 register struct inode *dp = VTOI(ndp->ni_dvp);
6bd0bb92 647 struct direct *ep;
7604aef4
KM
648 struct buf *bp;
649 int error;
6bd0bb92 650
d870be74 651 if (ndp->ni_count == 0) {
6bd0bb92
BJ
652 /*
653 * First entry in block: set d_ino to zero.
654 */
d870be74 655 ndp->ni_dent.d_ino = 0;
7604aef4
KM
656 error = rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent,
657 (int)DIRSIZ(&ndp->ni_dent), ndp->ni_offset, UIO_SYSSPACE,
658 ndp->ni_cred, (int *)0);
36d4ed87 659 } else {
6bd0bb92
BJ
660 /*
661 * Collapse new free space into previous entry.
662 */
7604aef4
KM
663 if (error = blkatoff(dp, ndp->ni_offset - ndp->ni_count,
664 (char **)&ep, &bp)) {
665 return (error);
666 }
d870be74 667 ep->d_reclen += ndp->ni_dent.d_reclen;
7604aef4 668 error = bwrite(bp);
6bd0bb92
BJ
669 dp->i_flag |= IUPD|ICHG;
670 }
7604aef4 671 return (error);
10873320 672}
6459ebe0 673
4f083fd7
SL
674/*
675 * Rewrite an existing directory entry to point at the inode
676 * supplied. The parameters describing the directory entry are
677 * set up by a call to namei.
678 */
d870be74 679dirrewrite(dp, ip, ndp)
4f083fd7 680 struct inode *dp, *ip;
d870be74 681 struct nameidata *ndp;
4f083fd7
SL
682{
683
d870be74 684 ndp->ni_dent.d_ino = ip->i_number;
7604aef4
KM
685 return (rdwri(UIO_WRITE, dp, (caddr_t)&ndp->ni_dent,
686 (int)DIRSIZ(&ndp->ni_dent), ndp->ni_offset, UIO_SYSSPACE,
687 ndp->ni_cred, (int *)0));
4f083fd7
SL
688}
689
4a0415d6
SL
690/*
691 * Return buffer with contents of block "offset"
692 * from the beginning of directory "ip". If "res"
693 * is non-zero, fill it in with a pointer to the
694 * remaining space in the directory.
695 */
7604aef4 696blkatoff(ip, offset, res, bpp)
6bd0bb92
BJ
697 struct inode *ip;
698 off_t offset;
699 char **res;
7604aef4 700 struct buf **bpp;
6459ebe0 701{
6bd0bb92 702 register struct fs *fs = ip->i_fs;
3fd23f5c 703 daddr_t lbn = lblkno(fs, offset);
6bd0bb92 704 int bsize = blksize(fs, ip, lbn);
7604aef4 705 struct buf *bp;
7e4f159c 706 daddr_t bn;
7604aef4 707 int error;
6459ebe0 708
7604aef4
KM
709 *bpp = 0;
710 if (error = bmap(ip, lbn, &bn, (daddr_t *)0, (int *)0))
711 return (error);
7e4f159c
KM
712 if (bn == (daddr_t)-1) {
713 dirbad(ip, offset, "hole in dir");
7604aef4 714 return (EIO);
7e4f159c 715 }
ec67a3ce
MK
716#ifdef SECSIZE
717 bp = bread(ip->i_dev, fsbtodb(fs, bn), bsize, fs->fs_dbsize);
718#else SECSIZE
7604aef4
KM
719 error = bread(ip->i_devvp, bn, bsize, &bp);
720 if (error) {
6bd0bb92 721 brelse(bp);
7604aef4 722 return (error);
6bd0bb92
BJ
723 }
724 if (res)
7e4f159c 725 *res = bp->b_un.b_addr + blkoff(fs, offset);
7604aef4
KM
726 *bpp = bp;
727 return (0);
6459ebe0 728}
4f083fd7
SL
729
730/*
731 * Check if a directory is empty or not.
732 * Inode supplied must be locked.
17d11193
SL
733 *
734 * Using a struct dirtemplate here is not precisely
735 * what we want, but better than using a struct direct.
736 *
737 * NB: does not handle corrupted directories.
4f083fd7 738 */
7604aef4 739dirempty(ip, parentino, cred)
05d2bbce 740 register struct inode *ip;
68f21562 741 ino_t parentino;
7604aef4 742 struct ucred *cred;
4f083fd7
SL
743{
744 register off_t off;
17d11193
SL
745 struct dirtemplate dbuf;
746 register struct direct *dp = (struct direct *)&dbuf;
05d2bbce 747 int error, count;
17d11193 748#define MINDIRSIZ (sizeof (struct dirtemplate) / 2)
4f083fd7
SL
749
750 for (off = 0; off < ip->i_size; off += dp->d_reclen) {
7604aef4
KM
751 error = rdwri(UIO_READ, ip, (caddr_t)dp, MINDIRSIZ, off,
752 UIO_SYSSPACE, cred, &count);
17d11193
SL
753 /*
754 * Since we read MINDIRSIZ, residual must
755 * be 0 unless we're at end of file.
756 */
757 if (error || count != 0)
4f083fd7 758 return (0);
d14b14c3 759 /* avoid infinite loops */
a5e62f37 760 if (dp->d_reclen == 0)
d14b14c3 761 return (0);
17d11193 762 /* skip empty entries */
4f083fd7
SL
763 if (dp->d_ino == 0)
764 continue;
17d11193
SL
765 /* accept only "." and ".." */
766 if (dp->d_namlen > 2)
767 return (0);
4f083fd7
SL
768 if (dp->d_name[0] != '.')
769 return (0);
17d11193
SL
770 /*
771 * At this point d_namlen must be 1 or 2.
772 * 1 implies ".", 2 implies ".." if second
773 * char is also "."
774 */
68f21562
KM
775 if (dp->d_namlen == 1)
776 continue;
777 if (dp->d_name[1] == '.' && dp->d_ino == parentino)
4f083fd7
SL
778 continue;
779 return (0);
780 }
781 return (1);
782}
b1aa93b9
KM
783
784/*
785 * Check if source directory is in the path of the target directory.
786 * Target is supplied locked, source is unlocked.
787 * The target is always iput() before returning.
788 */
7604aef4 789checkpath(source, target, cred)
b1aa93b9 790 struct inode *source, *target;
7604aef4 791 struct ucred *cred;
b1aa93b9
KM
792{
793 struct dirtemplate dirbuf;
7604aef4 794 struct inode *ip;
b1aa93b9
KM
795 int error = 0;
796
797 ip = target;
798 if (ip->i_number == source->i_number) {
799 error = EEXIST;
800 goto out;
801 }
802 if (ip->i_number == ROOTINO)
803 goto out;
804
805 for (;;) {
806 if ((ip->i_mode&IFMT) != IFDIR) {
807 error = ENOTDIR;
808 break;
809 }
810 error = rdwri(UIO_READ, ip, (caddr_t)&dirbuf,
7604aef4
KM
811 sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
812 cred, (int *)0);
b1aa93b9
KM
813 if (error != 0)
814 break;
815 if (dirbuf.dotdot_namlen != 2 ||
4a287f19
KM
816 dirbuf.dotdot_name[0] != '.' ||
817 dirbuf.dotdot_name[1] != '.') {
b1aa93b9
KM
818 error = ENOTDIR;
819 break;
820 }
821 if (dirbuf.dotdot_ino == source->i_number) {
822 error = EINVAL;
823 break;
824 }
825 if (dirbuf.dotdot_ino == ROOTINO)
826 break;
827 iput(ip);
7604aef4 828 if (error = iget(ip, dirbuf.dotdot_ino, &ip))
b1aa93b9 829 break;
b1aa93b9
KM
830 }
831
832out:
833 if (error == ENOTDIR)
834 printf("checkpath: .. not a directory\n");
835 if (ip != NULL)
836 iput(ip);
837 return (error);
838}