* Written by Paul Popelka (paulp@uts.amdahl.com)
* You can do anything you want with this software,
* just don't say you wrote it,
* and don't remove this notice.
* This software is provided "as is".
* The author supplies this software to be publicly
* redistributed on the understanding that the author
* is not responsible for the correct functioning of
* this software in any circumstances and is not liable
* for any damages caused by this software.
* $Header: /usr/src/CVS/sys/pcfs/pcfs_lookup.c,v 1.1.2.2 1993/02/07 21:57:22 friedl Exp $
* When we search a directory the blocks containing directory
* entries are read and examined. The directory entries
* contain information that would normally be in the inode
* of a unix filesystem. This means that some of a directory's
* contents may also be in memory resident denodes (sort of
* an inode). This can cause problems if we are searching
* while some other process is modifying a directory. To
* prevent one process from accessing incompletely modified
* directory information we depend upon being the soul owner
* of a directory block. bread/brelse provide this service.
* This being the case, when a process modifies a directory
* it must first acquire the disk block that contains the
* directory entry to be modified. Then update the disk
* block and the denode, and then write the disk block out
* to disk. This way disk blocks containing directory
* entries and in memory denode's will be in synch.
struct vnode
*vdp
; /* vnode of directory to search */
int isadir
; /* ~0 if found direntry is a directory */
u_long scn
; /* starting cluster number */
printf("pcfs_lookup(): looking for %s\n", ndp
->ni_ptr
);
#endif /* defined(PCFSDEBUG) */
lockparent
= ndp
->ni_nameiop
& LOCKPARENT
;
flag
= ndp
->ni_nameiop
& OPMASK
;
wantparent
= ndp
->ni_nameiop
& (LOCKPARENT
| WANTPARENT
);
printf("pcfs_lookup(): vdp %08x, dp %08x, Attr %02x\n",
vdp
, dp
, dp
->de_Attributes
);
#endif /* defined(PCFSDEBUG) */
* Be sure vdp is a directory. Since dos filesystems
* don't have the concept of execute permission anybody
* can search a directory.
if ((dp
->de_Attributes
& ATTR_DIRECTORY
) == 0)
* See if the component of the pathname we are looking for
* is in the directory cache. If so then do a few things
if (error
= cache_lookup(ndp
)) {
if (vdp
== ndp
->ni_rdir
&& ndp
->ni_isdotdot
)
panic("pcfs_lookup: .. thru root");
} else if (ndp
->ni_isdotdot
) {
if (!error
&& lockparent
&& *ndp
->ni_next
== '\0')
if (!lockparent
|| error
|| *ndp
->ni_next
!= '\0')
printf("pcfs_lookup(): cache hit, vnode %08x, file %s\n", vdp
, dp
->de_Name
);
#endif /* defined(PCFSDEBUG) */
if (lockparent
&& pdp
!= dp
&& *ndp
->ni_next
== '\0')
* If they are going after the . or .. entry in the
* root directory, they won't find it. DOS filesystems
* don't have them in the root directory. So, we fake it.
* deget() is in on this scam too.
if ((vdp
->v_flag
& VROOT
) && ndp
->ni_ptr
[0] == '.' &&
(ndp
->ni_namelen
== 2 && ndp
->ni_ptr
[1] == '.'))) {
printf("pcfs_lookup(): looking for . or .. in root directory\n");
#endif /* defined(PCFSDEBUG) */
* Don't search for free slots unless we are creating
* a filename and we are at the end of the pathname.
if ((flag
== CREATE
|| flag
== RENAME
) && *ndp
->ni_next
== '\0') {
unix2dosfn((u_char
*)ndp
->ni_ptr
, dosfilename
, ndp
->ni_namelen
);
printf("pcfs_lookup(): dos version of filename %s, length %d\n",
dosfilename
, ndp
->ni_namelen
);
#endif /* defined(PCFSDEBUG) */
* Search the directory pointed at by vdp for the
* name pointed at by ndp->ni_ptr.
* The outer loop ranges over the clusters that make
* up the directory. Note that the root directory is
* different from all other directories. It has a
* fixed number of blocks that are not part of the
* pool of allocatable clusters. So, we treat it a
* The root directory starts at "cluster" 0.
for (frcn
= 0; ; frcn
++) {
if (error
= pcbmap(dp
, frcn
, &bn
, &cluster
)) {
if (error
= bread(pmp
->pm_devvp
, bn
, pmp
->pm_bpcluster
, NOCRED
, &bp
))
for (diroff
= 0; diroff
< pmp
->pm_depclust
; diroff
++) {
dep
= (struct direntry
*)bp
->b_un
.b_addr
+ diroff
;
* If the slot is empty and we are still looking for
* an empty then remember this one. If the slot is
* not empty then check to see if it matches what we
* are looking for. If the slot has never been filled
* with anything, then the remainder of the directory
* has never been used, so there is no point in searching
if (dep
->deName
[0] == SLOT_EMPTY
||
dep
->deName
[0] == SLOT_DELETED
) {
if (slotstatus
!= FOUND
) {
if (dep
->deName
[0] == SLOT_EMPTY
) {
/* Ignore volume labels (anywhere, not just
* the root directory). */
if ((dep
->deAttributes
& ATTR_VOLUME
) == 0 &&
bcmp(dosfilename
, dep
->deName
, 11) == 0) {
printf("pcfs_lookup(): match diroff %d, rootreloff %d\n", diroff
, rootreloff
);
#endif /* defined(PCFSDEBUG) */
* Remember where this directory entry came from
* for whoever did this lookup.
* If this is the root directory we are interested
* in the offset relative to the beginning of the
* directory (not the beginning of the cluster).
ndp
->ni_pcfs
.pcfs_offset
= diroff
;
ndp
->ni_pcfs
.pcfs_cluster
= cluster
;
} /* for (diroff = 0; .... */
* Release the buffer holding the directory cluster
} /* for (frcn = 0; ; frcn++) */
* We hold no disk buffers at this point.
* If we get here we didn't find the entry we were looking
* for. But that's ok if we are creating or renaming and
* are at the end of the pathname and the directory hasn't
printf("pcfs_lookup(): flag %d, refcnt %d, slotstatus %d\n",
flag
, dp
->de_refcnt
, slotstatus
);
printf(" slotoffset %d, slotcluster %d\n",
slotoffset
, slotcluster
);
#endif /* defined(PCFSDEBUG) */
if ((flag
== CREATE
|| flag
== RENAME
) &&
*ndp
->ni_next
== '\0' && dp
->de_refcnt
!= 0) {
if (slotstatus
== NONE
) {
ndp
->ni_pcfs
.pcfs_offset
= 0;
ndp
->ni_pcfs
.pcfs_cluster
= 0;
ndp
->ni_pcfs
.pcfs_count
= 0;
printf("pcfs_lookup(): saving empty slot location\n");
#endif /* defined(PCFSDEBUG) */
ndp
->ni_pcfs
.pcfs_offset
= slotoffset
;
ndp
->ni_pcfs
.pcfs_cluster
= slotcluster
;
ndp
->ni_pcfs
.pcfs_count
= 1;
/* dp->de_flag |= DEUPD; /* never update dos directories */
ndp
->ni_nameiop
|= SAVENAME
;
if (!lockparent
) /* leave searched dir locked? */
* Insert name in cache as non-existant if not
if (ndp
->ni_makeentry
&& flag
!= CREATE
)
* NOTE: We still have the buffer with matched
* directory entry at this point.
isadir
= dep
->deAttributes
& ATTR_DIRECTORY
;
scn
= dep
->deStartCluster
;
* If we entered at foundroot, then we are looking
* for the . or .. entry of the filesystems root
* directory. isadir and scn were setup before
* jumping here. And, bp is null. There is no buf header.
* If deleting and at the end of the path, then
* if we matched on "." then don't deget() we would
* probably panic(). Otherwise deget() the directory
if (flag
== DELETE
&& ndp
->ni_next
== '\0') {
if (dp
->de_StartCluster
== scn
&&
error
= deget(pmp
, cluster
, diroff
, dep
, &tdp
);
if (flag
== RENAME
&& wantparent
&& *ndp
->ni_next
== '\0') {
if (dp
->de_StartCluster
== scn
&&
error
= deget(pmp
, cluster
, diroff
, dep
, &tdp
);
ndp
->ni_nameiop
|= SAVENAME
;
error
= deget(pmp
, cluster
, diroff
, dep
, &tdp
);
if (lockparent
&& *ndp
->ni_next
== '\0')
} else if (dp
->de_StartCluster
== scn
&&
error
= deget(pmp
, cluster
, diroff
, dep
, &tdp
);
if (!lockparent
|| *ndp
->ni_next
!= '\0')
* Insert name in cache if wanted.
* dep - directory to copy into the directory
* ndp - nameidata structure containing info on
* where to put the directory entry in the directory.
* depp - return the address of the denode for the
* created directory entry if depp != 0
u_long dirclust
, diroffset
;
struct denode
*ddep
= VTODE(ndp
->ni_dvp
); /* directory to add to */
struct pcfsmount
*pmp
= dep
->de_pmp
;
printf("createde(dep %08x, ndp %08x, depp %08x)\n", dep
, ndp
, depp
);
#endif /* defined(PCFSDEBUG) */
* If no space left in the directory then allocate
* another cluster and chain it onto the end of the
* file. There is one exception to this. That is,
* if the root directory has no more space it can NOT
* be expanded. extendfile() checks for and fails attempts to
* extend the root directory. We just return an error
if (ndp
->ni_pcfs
.pcfs_count
== 0) {
if (error
= extendfile(ddep
, &bp
, &dirclust
))
ndep
= (struct direntry
*)bp
->b_un
.b_addr
;
* Let caller know where we put the directory entry.
ndp
->ni_pcfs
.pcfs_cluster
= dirclust
;
ndp
->ni_pcfs
.pcfs_offset
= diroffset
= 0;
* There is space in the existing directory. So,
* we just read in the cluster with space. Copy
* the new directory entry in. Then write it to
* NOTE: DOS directories do not get smaller as
dirclust
= ndp
->ni_pcfs
.pcfs_cluster
;
diroffset
= ndp
->ni_pcfs
.pcfs_offset
;
error
= readep(pmp
, dirclust
, diroffset
, &bp
, &ndep
);
* If they want us to return with the denode gotten.
error
= deget(pmp
, dirclust
, diroffset
, ndep
, depp
);
* Read in a directory entry and mark it as being deleted.
markdeleted(pmp
, dirclust
, diroffset
)
error
= readep(pmp
, dirclust
, diroffset
, &bp
, &ep
);
ep
->deName
[0] = SLOT_DELETED
;
* Remove a directory entry.
* At this point the file represented by the directory
* entry to be removed is still full length until no
* one has it open. When the file no longer being
* used pcfs_inactive() is called and will truncate
* the file to 0 length. When the vnode containing
* the denode is needed for some other purpose by
* VFS it will call pcfs_reclaim() which will remove
* the denode from the denode cache.
struct denode
*dep
= VTODE(ndp
->ni_vp
); /* the file being removed */
struct pcfsmount
*pmp
= dep
->de_pmp
;
/*printf("removede(): filename %s\n", dep->de_Name);
printf("rmde(): dep %08x, ndpcluster %d, ndpoffset %d\n",
dep, ndp->ni_pcfs.pcfs_cluster, ndp->ni_pcfs.pcfs_offset);*/
#endif /* defined(PCFSDEBUG) */
* Read the directory block containing the directory
* entry we are to make free. The nameidata structure
* holds the cluster number and directory entry index
* number of the entry to free.
error
= markdeleted(pmp
, ndp
->ni_pcfs
.pcfs_cluster
,
ndp
->ni_pcfs
.pcfs_offset
);
* Be sure a directory is empty except for "." and "..".
* Return 1 if empty, return 0 if not empty or error.
struct pcfsmount
*pmp
= dep
->de_pmp
;
* Since the filesize field in directory entries for a directory
* is zero, we just have to feel our way through the directory
* until we hit end of file.
error
= pcbmap(dep
, cn
, &bn
, 0);
return 1; /* it's empty */
error
= bread(pmp
->pm_devvp
, bn
, pmp
->pm_bpcluster
, NOCRED
,
dentp
= (struct direntry
*)bp
->b_un
.b_addr
;
for (dei
= 0; dei
< pmp
->pm_depclust
; dei
++) {
if (dentp
->deName
[0] != SLOT_DELETED
) {
* In dos directories an entry whose name starts with SLOT_EMPTY (0)
* starts the beginning of the unused part of the directory, so we
* can just return that it is empty.
if (dentp
->deName
[0] == SLOT_EMPTY
) {
* Any names other than "." and ".." in a directory mean
if (bcmp(dentp
->deName
, ". ", 11) &&
bcmp(dentp
->deName
, ".. ", 11)) {
printf("dosdirempty(): entry %d found %02x, %02x\n", dei
, dentp
->deName
[0],
#endif /* defined(PCFSDEBUG) */
return 0; /* not empty */
* Check to see if the directory described by target is
* in some subdirectory of source. This prevents something
* like the following from succeeding and leaving a bunch
* or files and directories orphaned.
* Where c and f are directories.
* source - the inode for /a/b/c
* target - the inode for /a/b/c/d/e/f
* Returns 0 if target is NOT a subdirectory of source.
* Otherwise returns a non-zero error number.
* The target inode is always unlocked on return.
doscheckpath(source
, target
)
if ((target
->de_Attributes
& ATTR_DIRECTORY
) == 0 ||
(source
->de_Attributes
& ATTR_DIRECTORY
) == 0) {
if (dep
->de_StartCluster
== source
->de_StartCluster
) {
if (dep
->de_StartCluster
== PCFSROOT
)
if ((dep
->de_Attributes
& ATTR_DIRECTORY
) == 0) {
scn
= dep
->de_StartCluster
;
error
= bread(pmp
->pm_devvp
, cntobn(pmp
, scn
),
pmp
->pm_bpcluster
, NOCRED
, &bp
);
ep
= (struct direntry
*)bp
->b_un
.b_addr
+ 1;
if ((ep
->deAttributes
& ATTR_DIRECTORY
) == 0 ||
bcmp(ep
->deName
, ".. ", 11) != 0) {
if (ep
->deStartCluster
== source
->de_StartCluster
) {
if (ep
->deStartCluster
== PCFSROOT
)
/* NOTE: deget() clears dep on error */
error
= deget(pmp
, ep
->deStartCluster
, 0, ep
, &dep
);
printf("doscheckpath(): .. not a directory?\n");
* Read in the disk block containing the directory entry
* (dirclu, dirofs) and return the address of the buf header,
* and the address of the directory entry within the block.
readep(pmp
, dirclu
, dirofs
, bpp
, epp
)
bn
= detobn(pmp
, dirclu
, dirofs
);
if (error
= bread(pmp
->pm_devvp
, bn
, pmp
->pm_bpcluster
, NOCRED
, bpp
)) {
*epp
= bptoep(pmp
, *bpp
, dirofs
);
* Read in the disk block containing the directory entry
* dep came from and return the address of the buf header,
* and the address of the directory entry within the block.
return readep(dep
->de_pmp
, dep
->de_dirclust
, dep
->de_diroffset
,