cg_free is (u_char)
[unix-history] / usr / src / sys / ufs / ffs / ufs_lookup.c
CommitLineData
954ce9b1 1/* ufs_lookup.c 4.25 82/10/17 */
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 }
399 bcopy(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
BJ
479 int loc, freespace;
480 u_int dsize, newentrysize;
6bd0bb92 481 char *dirbuf;
f5039631 482
6bd0bb92
BJ
483 u.u_dent.d_ino = ip->i_number;
484 u.u_segflg = 1;
485 newentrysize = DIRSIZ(&u.u_dent);
486 if (u.u_count == 0) {
487 /*
488 * If u.u_count is 0, then namei could find no space in the
489 * directory. In this case u.u_offset will be on a directory
490 * block boundary and we will write the new entry into a fresh
491 * block.
492 */
493 if (u.u_offset&(DIRBLKSIZ-1))
494 panic("wdir: newblk");
495 u.u_dent.d_reclen = DIRBLKSIZ;
9f024ffc
BJ
496 (void) rdwri(UIO_WRITE, u.u_pdir, (caddr_t)&u.u_dent, newentrysize,
497 u.u_offset, 1, (int *)0);
6bd0bb92
BJ
498 iput(u.u_pdir);
499 return;
500 }
501
502 /*
503 * If u.u_count is non-zero, then namei found space for the
504 * new entry in the range u.u_offset to u.u_offset+u.u_count.
505 * in the directory. To use this space, we may have to compact
506 * the entries located there, by copying them together towards
507 * the beginning of the block, leaving the free space in
508 * one usable chunk at the end.
509 */
510
511 /*
512 * Increase size of directory if entry eats into new space.
513 * This should never push the size past a new multiple of
514 * DIRBLKSIZE.
515 */
516 if (u.u_offset+u.u_count > u.u_pdir->i_size) {
4a0415d6
SL
517/*ZZ*/ if (((u.u_offset+u.u_count-1)&~(DIRBLKSIZ-1)) !=
518/*ZZ*/ ((u.u_pdir->i_size-1)&~(DIRBLKSIZ-1))) {
519/*ZZ*/ panic("wdir: span");
520/*ZZ*/ }
6bd0bb92
BJ
521 u.u_pdir->i_size = u.u_offset + u.u_count;
522 }
523
524 /*
525 * Get the block containing the space for the new directory
526 * entry.
527 */
4a0415d6 528 bp = blkatoff(u.u_pdir, u.u_offset, (char **)&dirbuf);
6bd0bb92
BJ
529 if (bp == 0)
530 return;
6bd0bb92
BJ
531
532 /*
533 * Find space for the new entry. In the simple case, the
534 * entry at offset base will have the space. If it does
535 * not, then namei arranged that compacting the region
536 * u.u_offset to u.u_offset+u.u_count would yield the space.
537 */
538 ep = (struct direct *)dirbuf;
539 dsize = DIRSIZ(ep);
540 freespace = ep->d_reclen - dsize;
541 for (loc = ep->d_reclen; loc < u.u_count; ) {
542 nep = (struct direct *)(dirbuf + loc);
543 if (ep->d_ino) {
544 /* trim the existing slot */
545 ep->d_reclen = dsize;
546 ep = (struct direct *)((char *)ep + dsize);
547 } else {
548 /* overwrite; nothing there; header is ours */
549 freespace += dsize;
550 }
551 dsize = DIRSIZ(nep);
552 freespace += nep->d_reclen - dsize;
553 loc += nep->d_reclen;
554/*ZZ*/if((loc&~0x1ff)!=(loc+nep->d_reclen-1&~0x1ff))
555/*ZZ*/printf("wdir: compact loc %d reclen %d (dir %s/%d)\n",loc,nep->d_reclen,
556/*ZZ*/u.u_pdir->i_fs->fs_fsmnt,u.u_pdir->i_number);
9f024ffc 557 bcopy((caddr_t)nep, (caddr_t)ep, dsize);
6bd0bb92
BJ
558 }
559 /*
560 * Update the pointer fields in the previous entry (if any),
561 * copy in the new entry, and write out the block.
562 */
563 if (ep->d_ino == 0) {
564 if (freespace + dsize < newentrysize)
565 panic("wdir: compact1");
566/*ZZ*/if(freespace+dsize>512)panic("wdir: compact screwup");
567 u.u_dent.d_reclen = freespace + dsize;
568 } else {
569 if (freespace < newentrysize)
570 panic("wdir: compact2");
571 u.u_dent.d_reclen = freespace;
6bd0bb92
BJ
572 ep->d_reclen = dsize;
573 ep = (struct direct *)((char *)ep + dsize);
574 }
954ce9b1 575 bcopy((caddr_t)&u.u_dent, (caddr_t)ep, newentrysize);
6bd0bb92
BJ
576 bwrite(bp);
577 u.u_pdir->i_flag |= IUPD|ICHG;
578 iput(u.u_pdir);
579}
580
581dirremove()
582{
583 register struct inode *dp = u.u_pdir;
6bd0bb92
BJ
584 register struct buf *bp;
585 struct direct *ep;
586
6bd0bb92
BJ
587 if (u.u_count == 0) {
588 /*
589 * First entry in block: set d_ino to zero.
590 */
6bd0bb92 591 u.u_dent.d_ino = 0;
954ce9b1
BJ
592 (void) rdwri(UIO_WRITE, dp, (caddr_t)&u.u_dent,
593 DIRSIZ(&u.u_dent), u.u_offset, 1, (int *)0);
6bd0bb92
BJ
594 } else {
595 /*
596 * Collapse new free space into previous entry.
597 */
9f024ffc 598 bp = blkatoff(dp, (int)(u.u_offset - u.u_count), (char **)&ep);
6bd0bb92
BJ
599 if (bp == 0)
600 return (0);
601 ep->d_reclen += u.u_dent.d_reclen;
6bd0bb92
BJ
602 bwrite(bp);
603 dp->i_flag |= IUPD|ICHG;
604 }
605 return (1);
10873320 606}
6459ebe0 607
4a0415d6
SL
608/*
609 * Return buffer with contents of block "offset"
610 * from the beginning of directory "ip". If "res"
611 * is non-zero, fill it in with a pointer to the
612 * remaining space in the directory.
613 */
6bd0bb92 614struct buf *
4a0415d6 615blkatoff(ip, offset, res)
6bd0bb92
BJ
616 struct inode *ip;
617 off_t offset;
618 char **res;
6459ebe0 619{
6bd0bb92
BJ
620 register struct fs *fs = ip->i_fs;
621 int lbn = lblkno(fs, offset);
622 int base = blkoff(fs, offset);
623 int bsize = blksize(fs, ip, lbn);
624 int bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, base, bsize));
625 register struct buf *bp;
6459ebe0 626
6bd0bb92
BJ
627 if (u.u_error)
628 return (0);
629 bp = bread(ip->i_dev, bn, bsize);
630 if (bp->b_flags & B_ERROR) {
631 brelse(bp);
632 return (0);
633 }
634 if (res)
635 *res = bp->b_un.b_addr + base;
6bd0bb92 636 return (bp);
6459ebe0 637}