/* ufs_lookup.c 4.29 82/10/31 */
* Convert a pathname into a pointer to a locked inode,
* with side effects usable in creating and removing files.
* This is a very central and rather complicated routine.
* The func argument gives the routine which returns successive
* characters of the name to be translated. The flag
* argument is (0, 1, 2) depending on whether the name is to be
* (looked up, created, deleted). The follow argument is 1 when
* symbolic links are to be followed when they occur at the end of
* the name translation process.
* check accessibility of directory
* copy next component of name to u.u_dent
* handle degenerate case where name is null string
* search for name in directory, to found or notfound
* if creating, return locked inode, leaving information on avail. slots
* if at end of path and deleting, return information to allow delete
* if .. and on mounted filesys, look in mount table for parent
* if symbolic link, massage name in buffer and continue at dirloop
* if more components of name, do next level at dirloop
* return the answer as locked inode
namei(func
, flag
, follow
)
int (*func
)(), flag
, follow
;
register char *cp
; /* pointer into pathname argument */
/* these variables refer to things which must be freed or unlocked */
register struct inode
*dp
= 0; /* the directory we are searching */
register struct fs
*fs
; /* file system that directory is in */
register struct buf
*bp
= 0; /* a buffer of directory entries */
register struct direct
*ep
; /* the current directory entry */
int entryoffsetinblock
; /* offset of ep in bp's buffer */
register struct buf
*nbp
; /* buffer storing path name argument */
/* these variables hold information about the search for a slot */
enum {NONE
, COMPACT
, FOUND
} slotstatus
;
int slotoffset
= -1; /* offset of area with free space */
int slotsize
; /* size of area at slotoffset */
int slotfreespace
; /* amount of space free in slot */
int slotneeded
; /* size of the entry we're seeking */
int prevoff
; /* u.u_offset of previous entry */
int nlink
= 0; /* number of symbolic links taken */
struct inode
*pdp
; /* saved dp during symlink work */
* Get a buffer for the name to be translated, and copy the
nbp
= geteblk(MAXPATHLEN
);
for (cp
= nbp
->b_un
.b_addr
; *cp
= (*func
)(); ) {
if ((*cp
&0377) == ('/'|0200) || (*cp
&0200) && flag
!= 2) {
if (cp
>= nbp
->b_un
.b_addr
+ MAXPATHLEN
) {
* Get starting directory.
if ((dp
= u
.u_rdir
) == NULL
)
u
.u_pdir
= (struct inode
*)0xc0000000; /* illegal */
* We come to dirloop to search a new directory.
* The directory must be locked so that it can be
* iput, and fs must be already set to dp->i_fs.
* Check accessiblity of directory.
if ((dp
->i_mode
&IFMT
) != IFDIR
) {
* Copy next component of name to u.u_dent.
for (i
= 0; *cp
!= 0 && *cp
!= '/'; cp
++) {
u
.u_dent
.d_name
[i
++] = *cp
;
* Check for degenerate name (e.g. / or "")
* which is a way of talking about a directory,
if (u
.u_dent
.d_name
[0] == 0) {
* Suppress search for slots unless creating
* file and at end of pathname, in which case
* we watch for a place to put the new file in
* case it doesn't already exist.
if (flag
== 1 && *cp
== 0) {
slotneeded
= DIRSIZ(&u
.u_dent
);
dirsize
= roundup(dp
->i_size
, DIRBLKSIZ
);
while (u
.u_offset
< dirsize
) {
* If offset is on a block boundary,
* read the next directory block.
* Release previous if it exists.
if (blkoff(fs
, u
.u_offset
) == 0) {
bp
= blkatoff(dp
, u
.u_offset
, (char **)0);
* If still looking for a slot, and at a DIRBLKSIZE
* boundary, have to start looking for free space
if (slotstatus
== NONE
&&
(entryoffsetinblock
&(DIRBLKSIZ
-1)) == 0) {
* Get pointer to next entry, and do consistency checking:
* record length must be multiple of 4
* record length must not be zero
* entry must fit in rest of this DIRBLKSIZ block
* record must be large enough to contain name
* When dirchk is set we also check:
* name is not longer than MAXNAMLEN
* name must be as long as advertised, and null terminated
* Checking last two conditions is done only when dirchk is
ep
= (struct direct
*)(bp
->b_un
.b_addr
+ entryoffsetinblock
);
i
= DIRBLKSIZ
- (entryoffsetinblock
& (DIRBLKSIZ
- 1));
if ((ep
->d_reclen
& 0x3) || ep
->d_reclen
== 0 ||
ep
->d_reclen
> i
|| DIRSIZ(ep
) > ep
->d_reclen
||
dirchk
&& (ep
->d_namlen
> MAXNAMLEN
|| dirbadname(ep
))) {
dirbad(dp
, "mangled entry");
* If an appropriate sized slot has not yet been found,
* check to see if one is available. Also accumulate space
* in the current block so that we can determine if
if (slotstatus
!= FOUND
) {
if (size
>= slotneeded
) {
} else if (slotstatus
== NONE
) {
if (slotfreespace
>= slotneeded
) {
u
.u_offset
+ep
->d_reclen
-
* Check for a name match.
if (ep
->d_namlen
== u
.u_dent
.d_namlen
&&
!bcmp(u
.u_dent
.d_name
, ep
->d_name
, ep
->d_namlen
))
u
.u_offset
+= ep
->d_reclen
;
entryoffsetinblock
+= ep
->d_reclen
;
* If creating, and at end of pathname and current
* directory has not been removed, then can consider allowing
if (flag
== 1 && *cp
== 0 && dp
->i_nlink
!= 0) {
* Access for write is interpreted as allowing
* creation of files in the directory.
* Return an indication of where the new directory
* entry should be put. If we didn't find a slot,
* then set u.u_count to 0 indicating that the
* new slot belongs at the end of the directory.
* If we found a slot, then the new entry can be
* put in the range [u.u_offset..u.u_offset+u.u_count)
* We return with the directory locked, so that
* the parameters we set up above will still be
* valid if we actually decide to do a direnter().
* We return NULL to indicate that the entry doesn't
* currently exist, leaving a pointer to the (locked)
* directory inode in u.u_pdir.
* Check that directory length properly reflects presence
if (entryoffsetinblock
+ DIRSIZ(ep
) > dp
->i_size
) {
dirbad(dp
, "i_size too small");
dp
->i_size
= entryoffsetinblock
+ DIRSIZ(ep
);
* Found component in pathname; save directory
* entry in u.u_dent, and release directory buffer.
bcopy((caddr_t
)ep
, (caddr_t
)&u
.u_dent
, (u_int
)DIRSIZ(ep
));
* If deleting, and at end of pathname, return
* parameters which can be used to remove file.
* Note that in this case we return the directory
* inode, not the inode of the file being deleted.
if (flag
== 2 && *cp
== 0) {
* Write access to directory required to delete files.
* Return pointer to current entry in u.u_offset,
* and distance past previous entry (if there
* is a previous entry in this block) in u.u_count.
* Save directory inode pointer in u.u_pdir for dirremove().
if ((u
.u_offset
&(DIRBLKSIZ
-1)) == 0)
u
.u_count
= u
.u_offset
- prevoff
;
u
.u_pdir
= dp
; /* for dirremove() */
* Special handling for ".." allowing chdir out of mounted
* file system: indirect .. in root inode to reevaluate
* in directory file system was mounted on.
if (u
.u_dent
.d_name
[0] == '.' && u
.u_dent
.d_name
[1] == '.' &&
u
.u_dent
.d_name
[2] == '\0') {
u
.u_dent
.d_ino
= dp
->i_number
;
else if (u
.u_dent
.d_ino
== ROOTINO
&&
dp
->i_number
== ROOTINO
) {
for (i
= 1; i
< NMOUNT
; i
++)
if (mount
[i
].m_bufp
!= NULL
&&
mount
[i
].m_dev
== dp
->i_dev
) {
cp
-= 2; /* back over .. */
* Check for symbolic link, which may require us
* to massage the name before we continue translation.
* To avoid deadlock have to unlock the current directory,
* but don't iput it because we may need it again (if
* the symbolic link is relative to .). Instead save
dp
= iget(dp
->i_dev
, fs
, u
.u_dent
.d_ino
);
* Check for symbolic link
if ((dp
->i_mode
& IFMT
) == IFLNK
&& (follow
|| *cp
== '/')) {
u_int pathlen
= strlen(cp
) + 1;
if (dp
->i_size
+ pathlen
>= MAXPATHLEN
- 1 ||
ovbcopy(cp
, nbp
->b_un
.b_addr
+ dp
->i_size
, pathlen
);
rdwri(UIO_READ
, dp
, nbp
->b_un
.b_addr
, dp
->i_size
,
if ((dp
= u
.u_rdir
) == NULL
)
* Not a symbolic link. If more pathname,
* continue at next component, else return.
printf("%s: bad dir ino %d at offset %d: %s\n",
ip
->i_fs
->fs_fsmnt
, ip
->i_number
, u
.u_offset
, how
);
register struct direct
*ep
;
for (i
= 0; i
< ep
->d_namlen
; i
++)
* Write a directory entry after a call to namei, using the parameters
* which it left in the u. area. The argument ip is the inode which
* the new directory entry will refer to. The u. area field u.u_pdir is
* a pointer to the directory to be written, which was left locked by
* namei. Remaining parameters (u.u_offset, u.u_count) indicate
* how the space for the new entry is to be gotten.
register struct direct
*ep
, *nep
;
u
.u_dent
.d_ino
= ip
->i_number
;
newentrysize
= DIRSIZ(&u
.u_dent
);
* If u.u_count is 0, then namei could find no space in the
* directory. In this case u.u_offset will be on a directory
* block boundary and we will write the new entry into a fresh
if (u
.u_offset
&(DIRBLKSIZ
-1))
u
.u_dent
.d_reclen
= DIRBLKSIZ
;
(void) rdwri(UIO_WRITE
, u
.u_pdir
, (caddr_t
)&u
.u_dent
,
newentrysize
, u
.u_offset
, 1, (int *)0);
* If u.u_count is non-zero, then namei found space for the
* new entry in the range u.u_offset to u.u_offset+u.u_count.
* in the directory. To use this space, we may have to compact
* the entries located there, by copying them together towards
* the beginning of the block, leaving the free space in
* one usable chunk at the end.
* Increase size of directory if entry eats into new space.
* This should never push the size past a new multiple of
if (u
.u_offset
+u
.u_count
> u
.u_pdir
->i_size
)
u
.u_pdir
->i_size
= u
.u_offset
+ u
.u_count
;
* Get the block containing the space for the new directory
bp
= blkatoff(u
.u_pdir
, u
.u_offset
, (char **)&dirbuf
);
* Find space for the new entry. In the simple case, the
* entry at offset base will have the space. If it does
* not, then namei arranged that compacting the region
* u.u_offset to u.u_offset+u.u_count would yield the space.
ep
= (struct direct
*)dirbuf
;
freespace
= ep
->d_reclen
- dsize
;
for (loc
= ep
->d_reclen
; loc
< u
.u_count
; ) {
nep
= (struct direct
*)(dirbuf
+ loc
);
/* trim the existing slot */
ep
= (struct direct
*)((char *)ep
+ dsize
);
/* overwrite; nothing there; header is ours */
freespace
+= nep
->d_reclen
- dsize
;
bcopy((caddr_t
)nep
, (caddr_t
)ep
, dsize
);
* Update the pointer fields in the previous entry (if any),
* copy in the new entry, and write out the block.
if (freespace
+ dsize
< newentrysize
)
u
.u_dent
.d_reclen
= freespace
+ dsize
;
if (freespace
< newentrysize
)
u
.u_dent
.d_reclen
= freespace
;
ep
= (struct direct
*)((char *)ep
+ dsize
);
bcopy((caddr_t
)&u
.u_dent
, (caddr_t
)ep
, (u_int
)newentrysize
);
u
.u_pdir
->i_flag
|= IUPD
|ICHG
;
register struct inode
*dp
= u
.u_pdir
;
* First entry in block: set d_ino to zero.
(void) rdwri(UIO_WRITE
, dp
, (caddr_t
)&u
.u_dent
,
(int)DIRSIZ(&u
.u_dent
), u
.u_offset
, 1, (int *)0);
* Collapse new free space into previous entry.
bp
= blkatoff(dp
, (int)(u
.u_offset
- u
.u_count
), (char **)&ep
);
ep
->d_reclen
+= u
.u_dent
.d_reclen
;
* Return buffer with contents of block "offset"
* from the beginning of directory "ip". If "res"
* is non-zero, fill it in with a pointer to the
* remaining space in the directory.
blkatoff(ip
, offset
, res
)
register struct fs
*fs
= ip
->i_fs
;
daddr_t lbn
= lblkno(fs
, offset
);
int base
= blkoff(fs
, offset
);
int bsize
= blksize(fs
, ip
, lbn
);
daddr_t bn
= fsbtodb(fs
, bmap(ip
, lbn
, B_WRITE
, base
, bsize
));
bp
= bread(ip
->i_dev
, bn
, bsize
);
if (bp
->b_flags
& B_ERROR
) {
*res
= bp
->b_un
.b_addr
+ base
;