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