merge of 4.1b and 4.1c; and missing check for 0 length write
[unix-history] / usr / src / sys / ufs / ffs / ufs_lookup.c
CommitLineData
d36096be 1/* ufs_lookup.c 4.29 82/10/31 */
10873320
BJ
2
3#include "../h/param.h"
4#include "../h/systm.h"
5#include "../h/inode.h"
6459ebe0 6#include "../h/fs.h"
10873320
BJ
7#include "../h/mount.h"
8#include "../h/dir.h"
9#include "../h/user.h"
10#include "../h/buf.h"
3cbbbe12 11#include "../h/conf.h"
9f024ffc 12#include "../h/uio.h"
10873320 13
4a0415d6 14struct buf *blkatoff();
6bd0bb92 15int dirchk = 1;
10873320 16/*
6bd0bb92
BJ
17 * Convert a pathname into a pointer to a locked inode,
18 * with side effects usable in creating and removing files.
19 * This is a very central and rather complicated routine.
20 *
21 * The func argument gives the routine which returns successive
22 * characters of the name to be translated. The flag
23 * argument is (0, 1, 2) depending on whether the name is to be
24 * (looked up, created, deleted). The follow argument is 1 when
25 * symbolic links are to be followed when they occur at the end of
26 * the name translation process.
10873320 27 *
6bd0bb92
BJ
28 * Overall outline:
29 *
30 * copy in name
31 * get starting directory
32 * dirloop:
33 * check accessibility of directory
34 * dirloop2:
35 * copy next component of name to u.u_dent
36 * handle degenerate case where name is null string
37 * search for name in directory, to found or notfound
38 * notfound:
39 * if creating, return locked inode, leaving information on avail. slots
40 * else return error
41 * found:
42 * if at end of path and deleting, return information to allow delete
43 * if .. and on mounted filesys, look in mount table for parent
44 * if symbolic link, massage name in buffer and continue at dirloop
45 * if more components of name, do next level at dirloop
46 * return the answer as locked inode
10873320
BJ
47 */
48struct inode *
f5039631
BJ
49namei(func, flag, follow)
50 int (*func)(), flag, follow;
10873320 51{
6bd0bb92
BJ
52 register char *cp; /* pointer into pathname argument */
53/* these variables refer to things which must be freed or unlocked */
54 register struct inode *dp = 0; /* the directory we are searching */
55 register struct fs *fs; /* file system that directory is in */
56 register struct buf *bp = 0; /* a buffer of directory entries */
57 register struct direct *ep; /* the current directory entry */
58 int entryoffsetinblock; /* offset of ep in bp's buffer */
59 register struct buf *nbp; /* buffer storing path name argument */
60/* these variables hold information about the search for a slot */
61 enum {NONE, COMPACT, FOUND} slotstatus;
62 int slotoffset = -1; /* offset of area with free space */
63 int slotsize; /* size of area at slotoffset */
64 int slotfreespace; /* amount of space free in slot */
65 int slotneeded; /* size of the entry we're seeking */
66/* */
67 int dirsize;
68 int prevoff; /* u.u_offset of previous entry */
69 int nlink = 0; /* number of symbolic links taken */
70 struct inode *pdp; /* saved dp during symlink work */
71 int i;
10873320 72
f5039631 73 /*
6bd0bb92
BJ
74 * Get a buffer for the name to be translated, and copy the
75 * name into the buffer.
f5039631 76 */
6459ebe0 77 nbp = geteblk(MAXPATHLEN);
6bd0bb92
BJ
78 for (cp = nbp->b_un.b_addr; *cp = (*func)(); ) {
79 if ((*cp&0377) == ('/'|0200) || (*cp&0200) && flag != 2) {
a21ae242 80 u.u_error = EPERM;
6bd0bb92 81 goto bad;
a21ae242 82 }
a21ae242 83 cp++;
6459ebe0 84 if (cp >= nbp->b_un.b_addr + MAXPATHLEN) {
f5039631 85 u.u_error = ENOENT;
6bd0bb92 86 goto bad;
f5039631
BJ
87 }
88 }
6bd0bb92
BJ
89 if (u.u_error)
90 goto bad;
91
10873320 92 /*
6bd0bb92 93 * Get starting directory.
10873320 94 */
6bd0bb92 95 cp = nbp->b_un.b_addr;
f5039631
BJ
96 if (*cp == '/') {
97 while (*cp == '/')
98 cp++;
10873320
BJ
99 if ((dp = u.u_rdir) == NULL)
100 dp = rootdir;
6bd0bb92
BJ
101 } else
102 dp = u.u_cdir;
103 fs = dp->i_fs;
f5039631
BJ
104 ilock(dp);
105 dp->i_count++;
6bd0bb92
BJ
106 u.u_pdir = (struct inode *)0xc0000000; /* illegal */
107
10873320 108 /*
6bd0bb92
BJ
109 * We come to dirloop to search a new directory.
110 * The directory must be locked so that it can be
111 * iput, and fs must be already set to dp->i_fs.
10873320 112 */
6bd0bb92 113dirloop:
a0aead5a 114 /*
6bd0bb92 115 * Check accessiblity of directory.
a0aead5a 116 */
6bd0bb92 117 if ((dp->i_mode&IFMT) != IFDIR) {
10873320 118 u.u_error = ENOTDIR;
6bd0bb92
BJ
119 goto bad;
120 }
121 if (access(dp, IEXEC))
122 goto bad;
123
6f004ab5 124dirloop2:
6bd0bb92
BJ
125 /*
126 * Copy next component of name to u.u_dent.
127 */
128 for (i = 0; *cp != 0 && *cp != '/'; cp++) {
6459ebe0 129 if (i >= MAXNAMLEN) {
f5039631 130 u.u_error = ENOENT;
6bd0bb92 131 goto bad;
6459ebe0 132 }
6bd0bb92 133 u.u_dent.d_name[i++] = *cp;
6459ebe0
KM
134 }
135 u.u_dent.d_namlen = i;
6bd0bb92
BJ
136 u.u_dent.d_name[i] = 0;
137
138 /*
139 * Check for degenerate name (e.g. / or "")
140 * which is a way of talking about a directory,
141 * e.g. like "/." or ".".
142 */
143 if (u.u_dent.d_name[0] == 0) {
144 if (flag) {
f5039631 145 u.u_error = ENOENT;
6bd0bb92 146 goto bad;
f5039631 147 }
6459ebe0
KM
148 brelse(nbp);
149 return (dp);
f5039631 150 }
6bd0bb92 151
6459ebe0 152 /*
6bd0bb92
BJ
153 * Suppress search for slots unless creating
154 * file and at end of pathname, in which case
155 * we watch for a place to put the new file in
156 * case it doesn't already exist.
6459ebe0 157 */
6bd0bb92
BJ
158 slotstatus = FOUND;
159 if (flag == 1 && *cp == 0) {
160 slotstatus = NONE;
161 slotfreespace = 0;
162 slotneeded = DIRSIZ(&u.u_dent);
163 }
164
165 dirsize = roundup(dp->i_size, DIRBLKSIZ);
6459ebe0 166 u.u_offset = 0;
6bd0bb92 167 while (u.u_offset < dirsize) {
f5039631
BJ
168 /*
169 * If offset is on a block boundary,
170 * read the next directory block.
171 * Release previous if it exists.
172 */
6459ebe0 173 if (blkoff(fs, u.u_offset) == 0) {
f5039631
BJ
174 if (bp != NULL)
175 brelse(bp);
4a0415d6 176 bp = blkatoff(dp, u.u_offset, (char **)0);
6bd0bb92
BJ
177 if (bp == 0)
178 goto bad;
179 entryoffsetinblock = 0;
6459ebe0 180 }
6bd0bb92 181
6459ebe0 182 /*
6bd0bb92
BJ
183 * If still looking for a slot, and at a DIRBLKSIZE
184 * boundary, have to start looking for free space
185 * again.
6459ebe0 186 */
6bd0bb92
BJ
187 if (slotstatus == NONE &&
188 (entryoffsetinblock&(DIRBLKSIZ-1)) == 0) {
189 slotoffset = -1;
190 slotfreespace = 0;
191 }
192
193 /*
194 * Get pointer to next entry, and do consistency checking:
195 * record length must be multiple of 4
4a0415d6 196 * record length must not be zero
6bd0bb92
BJ
197 * entry must fit in rest of this DIRBLKSIZ block
198 * record must be large enough to contain name
4a0415d6
SL
199 * When dirchk is set we also check:
200 * name is not longer than MAXNAMLEN
6bd0bb92 201 * name must be as long as advertised, and null terminated
4a0415d6
SL
202 * Checking last two conditions is done only when dirchk is
203 * set, to save time.
6bd0bb92
BJ
204 */
205 ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock);
206 i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
4a0415d6
SL
207 if ((ep->d_reclen & 0x3) || ep->d_reclen == 0 ||
208 ep->d_reclen > i || DIRSIZ(ep) > ep->d_reclen ||
209 dirchk && (ep->d_namlen > MAXNAMLEN || dirbadname(ep))) {
6bd0bb92 210 dirbad(dp, "mangled entry");
6459ebe0 211 u.u_offset += i;
6bd0bb92 212 entryoffsetinblock += i;
6459ebe0
KM
213 continue;
214 }
6bd0bb92 215
6459ebe0 216 /*
6bd0bb92 217 * If an appropriate sized slot has not yet been found,
6459ebe0
KM
218 * check to see if one is available. Also accumulate space
219 * in the current block so that we can determine if
220 * compaction is viable.
221 */
6bd0bb92
BJ
222 if (slotstatus != FOUND) {
223 int size = ep->d_reclen;
224
6459ebe0
KM
225 if (ep->d_ino != 0)
226 size -= DIRSIZ(ep);
227 if (size > 0) {
6bd0bb92
BJ
228 if (size >= slotneeded) {
229 slotstatus = FOUND;
230 slotoffset = u.u_offset;
231 slotsize = ep->d_reclen;
232 } else if (slotstatus == NONE) {
233 slotfreespace += size;
234 if (slotoffset == -1)
235 slotoffset = u.u_offset;
236 if (slotfreespace >= slotneeded) {
237 slotstatus = COMPACT;
238 slotsize =
239 u.u_offset+ep->d_reclen -
240 slotoffset;
241 }
6459ebe0 242 }
f5039631 243 }
f5039631 244 }
6bd0bb92 245
f5039631 246 /*
6bd0bb92 247 * Check for a name match.
f5039631 248 */
6bd0bb92
BJ
249 if (ep->d_ino) {
250 if (ep->d_namlen == u.u_dent.d_namlen &&
251 !bcmp(u.u_dent.d_name, ep->d_name, ep->d_namlen))
252 goto found;
253 }
254 prevoff = u.u_offset;
6459ebe0 255 u.u_offset += ep->d_reclen;
6bd0bb92
BJ
256 entryoffsetinblock += ep->d_reclen;
257 }
258/* notfound: */
259 /*
260 * If creating, and at end of pathname and current
261 * directory has not been removed, then can consider allowing
262 * file to be created.
263 */
264 if (flag == 1 && *cp == 0 && dp->i_nlink != 0) {
f5039631 265 /*
6bd0bb92
BJ
266 * Access for write is interpreted as allowing
267 * creation of files in the directory.
f5039631 268 */
6bd0bb92
BJ
269 if (access(dp, IWRITE))
270 goto bad;
f5039631 271 /*
6bd0bb92
BJ
272 * Return an indication of where the new directory
273 * entry should be put. If we didn't find a slot,
274 * then set u.u_count to 0 indicating that the
275 * new slot belongs at the end of the directory.
276 * If we found a slot, then the new entry can be
277 * put in the range [u.u_offset..u.u_offset+u.u_count)
f5039631 278 */
6bd0bb92
BJ
279 if (slotstatus == NONE)
280 u.u_count = 0;
281 else {
282 u.u_offset = slotoffset;
283 u.u_count = slotsize;
5485e062 284 }
6bd0bb92
BJ
285 dp->i_flag |= IUPD|ICHG;
286 if (bp)
287 brelse(bp);
288 brelse(nbp);
f5039631 289 /*
6bd0bb92
BJ
290 * We return with the directory locked, so that
291 * the parameters we set up above will still be
292 * valid if we actually decide to do a direnter().
293 * We return NULL to indicate that the entry doesn't
294 * currently exist, leaving a pointer to the (locked)
295 * directory inode in u.u_pdir.
f5039631 296 */
6bd0bb92
BJ
297 u.u_pdir = dp;
298 return (NULL);
299 }
300 u.u_error = ENOENT;
301 goto bad;
302found:
303 /*
304 * Check that directory length properly reflects presence
305 * of this entry.
306 */
4a0415d6 307 if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) {
6bd0bb92 308 dirbad(dp, "i_size too small");
4a0415d6 309 dp->i_size = entryoffsetinblock + DIRSIZ(ep);
6bd0bb92
BJ
310 dp->i_flag |= IUPD|ICHG;
311 }
312
313 /*
314 * Found component in pathname; save directory
315 * entry in u.u_dent, and release directory buffer.
316 */
9f024ffc 317 bcopy((caddr_t)ep, (caddr_t)&u.u_dent, (u_int)DIRSIZ(ep));
6bd0bb92
BJ
318 brelse(bp);
319 bp = NULL;
320
321 /*
322 * If deleting, and at end of pathname, return
323 * parameters which can be used to remove file.
324 * Note that in this case we return the directory
325 * inode, not the inode of the file being deleted.
326 */
327 if (flag == 2 && *cp == 0) {
328 /*
329 * Write access to directory required to delete files.
330 */
331 if (access(dp, IWRITE))
332 goto bad;
333 /*
334 * Return pointer to current entry in u.u_offset,
335 * and distance past previous entry (if there
336 * is a previous entry in this block) in u.u_count.
337 * Save directory inode pointer in u.u_pdir for dirremove().
338 */
339 if ((u.u_offset&(DIRBLKSIZ-1)) == 0)
340 u.u_count = 0;
341 else
342 u.u_count = u.u_offset - prevoff;
343 brelse(nbp);
344 u.u_pdir = dp; /* for dirremove() */
345 return (dp);
346 }
347
348 /*
349 * Special handling for ".." allowing chdir out of mounted
350 * file system: indirect .. in root inode to reevaluate
351 * in directory file system was mounted on.
352 */
353 if (u.u_dent.d_name[0] == '.' && u.u_dent.d_name[1] == '.' &&
354 u.u_dent.d_name[2] == '\0') {
355 if (dp == u.u_rdir)
356 u.u_dent.d_ino = dp->i_number;
357 else if (u.u_dent.d_ino == ROOTINO &&
358 dp->i_number == ROOTINO) {
359 for (i = 1; i < NMOUNT; i++)
360 if (mount[i].m_bufp != NULL &&
361 mount[i].m_dev == dp->i_dev) {
6459ebe0 362 iput(dp);
6bd0bb92 363 dp = mount[i].m_inodp;
f5039631
BJ
364 ilock(dp);
365 dp->i_count++;
6bd0bb92
BJ
366 fs = dp->i_fs;
367 cp -= 2; /* back over .. */
368 goto dirloop2;
f5039631 369 }
10873320 370 }
6bd0bb92
BJ
371 }
372
373 /*
374 * Check for symbolic link, which may require us
375 * to massage the name before we continue translation.
376 * To avoid deadlock have to unlock the current directory,
377 * but don't iput it because we may need it again (if
378 * the symbolic link is relative to .). Instead save
379 * it (unlocked) as pdp.
380 */
381 pdp = dp;
382 iunlock(pdp);
383 dp = iget(dp->i_dev, fs, u.u_dent.d_ino);
384 if (dp == NULL)
385 goto bad2;
386 fs = dp->i_fs;
387
388 /*
389 * Check for symbolic link
390 */
391 if ((dp->i_mode & IFMT) == IFLNK && (follow || *cp == '/')) {
9f024ffc 392 u_int pathlen = strlen(cp) + 1;
6bd0bb92
BJ
393
394 if (dp->i_size + pathlen >= MAXPATHLEN - 1 ||
395 ++nlink > MAXSYMLINKS) {
396 u.u_error = ELOOP;
397 goto bad2;
398 }
d36096be 399 ovbcopy(cp, nbp->b_un.b_addr + dp->i_size, pathlen);
a6b6f679 400 u.u_error =
9f024ffc
BJ
401 rdwri(UIO_READ, dp, nbp->b_un.b_addr, dp->i_size,
402 0, 1, (int *)0);
6bd0bb92
BJ
403 if (u.u_error)
404 goto bad2;
405 cp = nbp->b_un.b_addr;
406 iput(dp);
f5039631 407 if (*cp == '/') {
6bd0bb92 408 irele(pdp);
f5039631
BJ
409 while (*cp == '/')
410 cp++;
6bd0bb92
BJ
411 if ((dp = u.u_rdir) == NULL)
412 dp = rootdir;
413 ilock(dp);
414 dp->i_count++;
415 } else {
416 dp = pdp;
417 ilock(dp);
f5039631 418 }
6bd0bb92
BJ
419 fs = dp->i_fs;
420 goto dirloop;
10873320 421 }
6bd0bb92
BJ
422 irele(pdp);
423
10873320 424 /*
6bd0bb92
BJ
425 * Not a symbolic link. If more pathname,
426 * continue at next component, else return.
10873320 427 */
6bd0bb92
BJ
428 if (*cp == '/') {
429 while (*cp == '/')
430 cp++;
431 goto dirloop;
10873320 432 }
6bd0bb92
BJ
433 brelse(nbp);
434 return (dp);
435bad2:
436 irele(pdp);
437bad:
438 if (bp)
439 brelse(bp);
440 if (dp)
441 iput(dp);
f5039631 442 brelse(nbp);
6459ebe0 443 return (NULL);
10873320
BJ
444}
445
6bd0bb92
BJ
446dirbad(ip, how)
447 struct inode *ip;
448 char *how;
449{
450
451 printf("%s: bad dir ino %d at offset %d: %s\n",
452 ip->i_fs->fs_fsmnt, ip->i_number, u.u_offset, how);
453}
454
455dirbadname(ep)
456 register struct direct *ep;
457{
6bd0bb92
BJ
458 register int i;
459
460 for (i = 0; i < ep->d_namlen; i++)
461 if (ep->d_name[i] == 0)
462 return (1);
463 return (ep->d_name[i]);
464}
465
6bd0bb92
BJ
466/*
467 * Write a directory entry after a call to namei, using the parameters
468 * which it left in the u. area. The argument ip is the inode which
469 * the new directory entry will refer to. The u. area field u.u_pdir is
470 * a pointer to the directory to be written, which was left locked by
471 * namei. Remaining parameters (u.u_offset, u.u_count) indicate
472 * how the space for the new entry is to be gotten.
473 */
474direnter(ip)
475 struct inode *ip;
f5039631 476{
6bd0bb92 477 register struct direct *ep, *nep;
6bd0bb92 478 struct buf *bp;
9f024ffc 479 int loc, freespace;
b32450f4
BJ
480 u_int dsize;
481 int newentrysize;
6bd0bb92 482 char *dirbuf;
f5039631 483
6bd0bb92
BJ
484 u.u_dent.d_ino = ip->i_number;
485 u.u_segflg = 1;
486 newentrysize = DIRSIZ(&u.u_dent);
487 if (u.u_count == 0) {
488 /*
489 * If u.u_count is 0, then namei could find no space in the
490 * directory. In this case u.u_offset will be on a directory
491 * block boundary and we will write the new entry into a fresh
492 * block.
493 */
494 if (u.u_offset&(DIRBLKSIZ-1))
495 panic("wdir: newblk");
496 u.u_dent.d_reclen = DIRBLKSIZ;
b32450f4
BJ
497 (void) rdwri(UIO_WRITE, u.u_pdir, (caddr_t)&u.u_dent,
498 newentrysize, u.u_offset, 1, (int *)0);
6bd0bb92
BJ
499 iput(u.u_pdir);
500 return;
501 }
502
503 /*
504 * If u.u_count is non-zero, then namei found space for the
505 * new entry in the range u.u_offset to u.u_offset+u.u_count.
506 * in the directory. To use this space, we may have to compact
507 * the entries located there, by copying them together towards
508 * the beginning of the block, leaving the free space in
509 * one usable chunk at the end.
510 */
511
512 /*
513 * Increase size of directory if entry eats into new space.
514 * This should never push the size past a new multiple of
515 * DIRBLKSIZE.
516 */
b32450f4 517 if (u.u_offset+u.u_count > u.u_pdir->i_size)
6bd0bb92 518 u.u_pdir->i_size = u.u_offset + u.u_count;
6bd0bb92
BJ
519
520 /*
521 * Get the block containing the space for the new directory
522 * entry.
523 */
4a0415d6 524 bp = blkatoff(u.u_pdir, u.u_offset, (char **)&dirbuf);
6bd0bb92
BJ
525 if (bp == 0)
526 return;
6bd0bb92
BJ
527
528 /*
529 * Find space for the new entry. In the simple case, the
530 * entry at offset base will have the space. If it does
531 * not, then namei arranged that compacting the region
532 * u.u_offset to u.u_offset+u.u_count would yield the space.
533 */
534 ep = (struct direct *)dirbuf;
535 dsize = DIRSIZ(ep);
536 freespace = ep->d_reclen - dsize;
537 for (loc = ep->d_reclen; loc < u.u_count; ) {
538 nep = (struct direct *)(dirbuf + loc);
539 if (ep->d_ino) {
540 /* trim the existing slot */
541 ep->d_reclen = dsize;
542 ep = (struct direct *)((char *)ep + dsize);
543 } else {
544 /* overwrite; nothing there; header is ours */
545 freespace += dsize;
546 }
547 dsize = DIRSIZ(nep);
548 freespace += nep->d_reclen - dsize;
549 loc += nep->d_reclen;
9f024ffc 550 bcopy((caddr_t)nep, (caddr_t)ep, dsize);
6bd0bb92
BJ
551 }
552 /*
553 * Update the pointer fields in the previous entry (if any),
554 * copy in the new entry, and write out the block.
555 */
556 if (ep->d_ino == 0) {
557 if (freespace + dsize < newentrysize)
558 panic("wdir: compact1");
6bd0bb92
BJ
559 u.u_dent.d_reclen = freespace + dsize;
560 } else {
561 if (freespace < newentrysize)
562 panic("wdir: compact2");
563 u.u_dent.d_reclen = freespace;
6bd0bb92
BJ
564 ep->d_reclen = dsize;
565 ep = (struct direct *)((char *)ep + dsize);
566 }
3fd23f5c 567 bcopy((caddr_t)&u.u_dent, (caddr_t)ep, (u_int)newentrysize);
6bd0bb92
BJ
568 bwrite(bp);
569 u.u_pdir->i_flag |= IUPD|ICHG;
570 iput(u.u_pdir);
571}
572
573dirremove()
574{
575 register struct inode *dp = u.u_pdir;
6bd0bb92
BJ
576 register struct buf *bp;
577 struct direct *ep;
578
6bd0bb92
BJ
579 if (u.u_count == 0) {
580 /*
581 * First entry in block: set d_ino to zero.
582 */
6bd0bb92 583 u.u_dent.d_ino = 0;
954ce9b1 584 (void) rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent,
b32450f4 585 (int)DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0);
6bd0bb92
BJ
586 } else {
587 /*
588 * Collapse new free space into previous entry.
589 */
9f024ffc 590 bp = blkatoff(dp, (int)(u.u_offset - u.u_count), (char **)&ep);
6bd0bb92
BJ
591 if (bp == 0)
592 return (0);
593 ep->d_reclen += u.u_dent.d_reclen;
6bd0bb92
BJ
594 bwrite(bp);
595 dp->i_flag |= IUPD|ICHG;
596 }
597 return (1);
10873320 598}
6459ebe0 599
4a0415d6
SL
600/*
601 * Return buffer with contents of block "offset"
602 * from the beginning of directory "ip". If "res"
603 * is non-zero, fill it in with a pointer to the
604 * remaining space in the directory.
605 */
6bd0bb92 606struct buf *
4a0415d6 607blkatoff(ip, offset, res)
6bd0bb92
BJ
608 struct inode *ip;
609 off_t offset;
610 char **res;
6459ebe0 611{
6bd0bb92 612 register struct fs *fs = ip->i_fs;
3fd23f5c 613 daddr_t lbn = lblkno(fs, offset);
6bd0bb92
BJ
614 int base = blkoff(fs, offset);
615 int bsize = blksize(fs, ip, lbn);
3fd23f5c 616 daddr_t bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize));
6bd0bb92 617 register struct buf *bp;
6459ebe0 618
6bd0bb92
BJ
619 if (u.u_error)
620 return (0);
621 bp = bread(ip->i_dev, bn, bsize);
622 if (bp->b_flags & B_ERROR) {
623 brelse(bp);
624 return (0);
625 }
626 if (res)
627 *res = bp->b_un.b_addr + base;
6bd0bb92 628 return (bp);
6459ebe0 629}