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