* 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.
* from NetBSD: msdosfs_lookup.c,v 1.5 1993/12/18 00:51:09 mycroft Exp
* $Id: msdosfs_lookup.c,v 1.1 1994/01/24 06:04:55 rgrimes Exp $
#include <fs/msdosfs/bpb.h>
#include <fs/msdosfs/direntry.h>
#include <fs/msdosfs/denode.h>
#include <fs/msdosfs/msdosfsmount.h>
#include <fs/msdosfs/fat.h>
* 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.
msdosfs_lookup(vdp
, ndp
, p
)
struct vnode
*vdp
; /* vnode of directory to search */
int isadir
; /* ~0 if found direntry is a directory */
u_long scn
; /* starting cluster number */
struct msdosfsmount
*pmp
;
#if defined(MSDOSFSDEBUG)
printf("msdosfs_lookup(): looking for %s\n", ndp
->ni_ptr
);
#endif /* defined(MSDOSFSDEBUG) */
lockparent
= ndp
->ni_nameiop
& LOCKPARENT
;
flag
= ndp
->ni_nameiop
& OPMASK
;
wantparent
= ndp
->ni_nameiop
& (LOCKPARENT
| WANTPARENT
);
#if defined(MSDOSFSDEBUG)
printf("msdosfs_lookup(): vdp %08x, dp %08x, Attr %02x\n",
vdp
, dp
, dp
->de_Attributes
);
#endif /* defined(MSDOSFSDEBUG) */
* Be sure vdp is a directory. Since dos filesystems don't have
* the concept of execute permission anybody can search a
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 and return.
if (error
= cache_lookup(ndp
)) {
if (vdp
== ndp
->ni_rootdir
&& ndp
->ni_isdotdot
)
panic("msdosfs_lookup: .. thru root");
else if (ndp
->ni_isdotdot
) {
if (!error
&& lockparent
&& *ndp
->ni_next
== '\0')
if (!lockparent
|| error
|| *ndp
->ni_next
!= '\0')
#if defined(MSDOSFSDEBUG)
printf("msdosfs_lookup(): cache hit, vnode %08x, file %s\n", vdp
, dp
->de_Name
);
#endif /* defined(MSDOSFSDEBUG) */
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] == '.'))) {
#if defined(MSDOSFSDEBUG)
printf("msdosfs_lookup(): looking for . or .. in root directory\n");
#endif /* defined(MSDOSFSDEBUG) */
diroff
= MSDOSFSROOT_OFS
;
* 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
);
#if defined(MSDOSFSDEBUG)
printf("msdosfs_lookup(): dos version of filename %s, length %d\n",
dosfilename
, ndp
->ni_namelen
);
#endif /* defined(MSDOSFSDEBUG) */
* Search the directory pointed at by vdp for the name pointed at
* 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
* little differently. 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 it.
if (dep
->deName
[0] == SLOT_EMPTY
||
dep
->deName
[0] == SLOT_DELETED
) {
if (slotstatus
!= FOUND
) {
if (cluster
== MSDOSFSROOT
)
if (dep
->deName
[0] == SLOT_EMPTY
) {
* Ignore volume labels (anywhere, not just
if ((dep
->deAttributes
& ATTR_VOLUME
) == 0 &&
bcmp(dosfilename
, dep
->deName
, 11) == 0) {
#if defined(MSDOSFSDEBUG)
printf("msdosfs_lookup(): match diroff %d, rootreloff %d\n", diroff
, rootreloff
);
#endif /* defined(MSDOSFSDEBUG) */
* 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).
if (cluster
== MSDOSFSROOT
)
ndp
->ni_msdosfs
.msdosfs_offset
= diroff
;
ndp
->ni_msdosfs
.msdosfs_cluster
= cluster
;
} /* for (diroff = 0; .... */
* Release the buffer holding the directory cluster just
} /* 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 been removed.
#if defined(MSDOSFSDEBUG)
printf("msdosfs_lookup(): flag %d, refcnt %d, slotstatus %d\n",
flag
, dp
->de_refcnt
, slotstatus
);
printf(" slotoffset %d, slotcluster %d\n",
slotoffset
, slotcluster
);
#endif /* defined(MSDOSFSDEBUG) */
if ((flag
== CREATE
|| flag
== RENAME
) &&
*ndp
->ni_next
== '\0' && dp
->de_refcnt
!= 0) {
if (slotstatus
== NONE
) {
ndp
->ni_msdosfs
.msdosfs_offset
= 0;
ndp
->ni_msdosfs
.msdosfs_cluster
= 0;
ndp
->ni_msdosfs
.msdosfs_count
= 0;
#if defined(MSDOSFSDEBUG)
printf("msdosfs_lookup(): saving empty slot location\n");
#endif /* defined(MSDOSFSDEBUG) */
ndp
->ni_msdosfs
.msdosfs_offset
= slotoffset
;
ndp
->ni_msdosfs
.msdosfs_cluster
= slotcluster
;
ndp
->ni_msdosfs
.msdosfs_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 trying to create it.
if (ndp
->ni_makeentry
&& flag
!= CREATE
)
if (flag
== CREATE
|| flag
== RENAME
)
* NOTE: We still have the buffer with matched directory entry at
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
* 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 entry.
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
u_long dirclust
, diroffset
;
struct denode
*ddep
= VTODE(ndp
->ni_dvp
); /* directory to add to */
struct msdosfsmount
*pmp
= dep
->de_pmp
;
#if defined(MSDOSFSDEBUG)
printf("createde(dep %08x, ndp %08x, depp %08x)\n", dep
, ndp
, depp
);
#endif /* defined(MSDOSFSDEBUG) */
* 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 in that
if (ndp
->ni_msdosfs
.msdosfs_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_msdosfs
.msdosfs_cluster
= dirclust
;
ndp
->ni_msdosfs
.msdosfs_offset
= diroffset
= 0;
* Update the size of the directory
ddep
->de_FileSize
+= pmp
->pm_bpcluster
;
* 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 disk. NOTE: DOS directories
* do not get smaller as clusters are emptied.
dirclust
= ndp
->ni_msdosfs
.msdosfs_cluster
;
diroffset
= ndp
->ni_msdosfs
.msdosfs_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
);
if (error
= bwrite(bp
)) {
deput(*depp
); /* free the vnode we got on error */
* Read in a directory entry and mark it as being deleted.
markdeleted(pmp
, dirclust
, diroffset
)
struct msdosfsmount
*pmp
;
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 msdosfs_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
* msdosfs_reclaim() which will remove the denode from the denode cache.
struct denode
*dep
= VTODE(ndp
->ni_vp
); /* the file being removed */
struct msdosfsmount
*pmp
= dep
->de_pmp
;
#if defined(MSDOSFSDEBUG)
* printf("removede(): filename %s\n", dep->de_Name);
* printf("rmde(): dep %08x, ndpcluster %d, ndpoffset %d\n", dep,
* ndp->ni_msdosfs.msdosfs_cluster,
* ndp->ni_msdosfs.msdosfs_offset);
#endif /* defined(MSDOSFSDEBUG) */
* 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_msdosfs
.msdosfs_cluster
,
ndp
->ni_msdosfs
.msdosfs_offset
);
* Be sure a directory is empty except for "." and "..". Return 1 if empty,
* return 0 if not empty or error.
struct msdosfsmount
*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
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
if (dentp
->deName
[0] == SLOT_EMPTY
) {
* Any names other than "." and ".." in a
* directory mean it is not empty.
if (bcmp(dentp
->deName
, ". ", 11) &&
bcmp(dentp
->deName
, ".. ", 11)) {
#if defined(MSDOSFSDEBUG)
printf("dosdirempty(): entry %d found %02x, %02x\n", dei
, dentp
->deName
[0],
#endif /* defined(MSDOSFSDEBUG) */
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. mv
* /a/b/c /a/b/c/d/e/f 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
)
struct msdosfsmount
*pmp
;
if ((target
->de_Attributes
& ATTR_DIRECTORY
) == 0 ||
(source
->de_Attributes
& ATTR_DIRECTORY
) == 0) {
if (dep
->de_StartCluster
== source
->de_StartCluster
) {
if (dep
->de_StartCluster
== MSDOSFSROOT
)
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
== MSDOSFSROOT
)
/* 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
)
struct msdosfsmount
*pmp
;
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
,