* 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_vnops.c,v 1.1 1993/08/13 11:35:40 cgd Exp
* $Id: msdosfs_vnops.c,v 1.1 1994/01/24 06:04:57 rgrimes Exp $
#include <sys/resourcevar.h> /* defines plimit structure in proc struct */
#include <sys/file.h> /* define FWRITE ... */
#include <fs/specfs/specdev.h> /* XXX */ /* defines v_rdev */
#include <sys/dir.h> /* defines dirent structure */
#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>
* In the ufs filesystem the inodes, superblocks, and indirect blocks are
* read/written using the vnode for the filesystem. Blocks that represent
* the contents of a file are read/written using the vnode for the file
* (including directories when they are read/written as files). This
* presents problems for the dos filesystem because data that should be in
* an inode (if dos had them) resides in the directory itself. Since we
* must update directory entries without the benefit of having the vnode
* for the directory we must use the vnode for the filesystem. This means
* that when a directory is actually read/written (via read, write, or
* readdir, or seek) we must use the vnode for the filesystem instead of
* the vnode for the directory as would happen in ufs. This is to insure we
* retreive the correct block from the buffer cache since the hash value is
* based upon the vnode address and the desired block number.
* Create a regular file. On entry the directory to contain the file being
* created is locked. We must release before we return. We must also free
* the pathname buffer pointed at by ndp->ni_pnbuf, always on error, or
* only if the SAVESTART bit in ni_nameiop is clear on success.
msdosfs_create(ndp
, vap
, p
)
struct direntry
*ndirp
= &ndirent
.de_de
;
struct denode
*pdep
= VTODE(ndp
->ni_dvp
);
#if defined(MSDOSFSDEBUG)
printf("msdosfs_create(ndp %08x, vap %08x, p %08x\n", ndp
, vap
, p
);
#endif /* defined(MSDOSFSDEBUG) */
* Create a directory entry for the file, then call createde() to
* have it installed. NOTE: DOS files are always executable. We
* use the absence of the owner write bit to make the file
bzero(&ndirent
, sizeof(ndirent
));
unix2dostime(&time
, (union dosdate
*) & ndirp
->deDate
,
(union dostime
*) & ndirp
->deTime
);
unix2dosfn((u_char
*) ndp
->ni_ptr
, ndirp
->deName
, ndp
->ni_namelen
);
ndirp
->deAttributes
= (vap
->va_mode
& VWRITE
) ? 0 : ATTR_READONLY
;
ndirp
->deStartCluster
= 0;
ndirent
.de_pmp
= pdep
->de_pmp
;
ndirent
.de_dev
= pdep
->de_dev
;
ndirent
.de_devvp
= pdep
->de_devvp
;
if ((error
= createde(&ndirent
, ndp
, &dep
)) == 0) {
if ((ndp
->ni_nameiop
& SAVESTART
) == 0)
free(ndp
->ni_pnbuf
, M_NAMEI
);
free(ndp
->ni_pnbuf
, M_NAMEI
);
deput(pdep
); /* release parent dir */
msdosfs_mknod(ndp
, vap
, cred
, p
)
struct denode
*pdep
= VTODE(ndp
->ni_dvp
);
error
= msdosfs_mkdir(ndp
, vap
, p
);
* msdosfs_create() sets ndp->ni_vp.
error
= msdosfs_create(ndp
, vap
, p
);
free(ndp
->ni_pnbuf
, M_NAMEI
);
msdosfs_open(vp
, mode
, cred
, p
)
msdosfs_close(vp
, fflag
, cred
, p
)
struct denode
*dep
= VTODE(vp
);
if (vp
->v_usecount
> 1 && !(dep
->de_flag
& DELOCKED
))
msdosfs_access(vp
, mode
, cred
, p
)
struct denode
*dep
= VTODE(vp
);
* Root gets to do anything. Even execute a file without the x-bit
* on? But, for dos filesystems every file is executable. I may
* mode is filled with a combination of VREAD, VWRITE, and/or VEXEC
* bits turned on. In an octal number these are the Y in 0Y00.
* Since the dos filesystem doesn't have the concept of file ownership
* we just give everybody read and execute access and write access
* if the readonly bit is off.
dosmode
= VEXEC
| VREAD
|
((dep
->de_Attributes
& ATTR_READONLY
) ? 0 : VWRITE
);
return ((dosmode
& mode
) != 0) ? 0 : EACCES
;
msdosfs_getattr(vp
, vap
, cred
, p
)
struct denode
*dep
= VTODE(vp
);
vap
->va_fsid
= dep
->de_dev
;
* The following computation of the fileid must be the same as that
* used in msdosfs_readdir() to compute d_fileno. If not, pwd
if (dep
->de_Attributes
& ATTR_DIRECTORY
) {
if ((cn
= dep
->de_StartCluster
) == MSDOSFSROOT
)
if ((cn
= dep
->de_dirclust
) == MSDOSFSROOT
)
cn
= (cn
<< 16) | (dep
->de_diroffset
& 0xffff);
vap
->va_mode
= (dep
->de_Attributes
& ATTR_READONLY
) ? 0555 : 0777;
if (dep
->de_Attributes
& ATTR_DIRECTORY
)
vap
->va_size
= dep
->de_FileSize
;
dos2unixtime((union dosdate
*) & dep
->de_Date
,
(union dostime
*) & dep
->de_Time
, &vap
->va_atime
);
vap
->va_atime
.tv_usec
= 0;
vap
->va_mtime
.tv_sec
= vap
->va_atime
.tv_sec
;
vap
->va_mtime
.tv_usec
= 0;
if (vap
->va_mode
& S_IFDIR
) {
vap
->va_mtime
.tv_sec
= time
.tv_sec
;
vap
->va_mtime
.tv_usec
= time
.tv_usec
;
vap
->va_ctime
.tv_sec
= vap
->va_atime
.tv_sec
;
vap
->va_ctime
.tv_usec
= 0;
vap
->va_flags
= dep
->de_flag
;
vap
->va_blocksize
= dep
->de_pmp
->pm_bpcluster
;
vap
->va_bytes
= (dep
->de_FileSize
+ dep
->de_pmp
->pm_crbomask
) &
~(dep
->de_pmp
->pm_crbomask
);
vap
->va_type
= vp
->v_type
;
msdosfs_setattr(vp
, vap
, cred
, p
)
struct denode
*dep
= VTODE(vp
);
#if defined(MSDOSFSDEBUG)
printf("msdosfs_setattr(): vp %08x, vap %08x, cred %08x, p %08x\n",
#endif /* defined(MSDOSFSDEBUG) */
if ((vap
->va_type
!= VNON
) ||
(vap
->va_nlink
!= VNOVAL
) ||
(vap
->va_fsid
!= VNOVAL
) ||
(vap
->va_fileid
!= VNOVAL
) ||
(vap
->va_blocksize
!= VNOVAL
) ||
(vap
->va_rdev
!= VNOVAL
) ||
(vap
->va_bytes
!= VNOVAL
) ||
(vap
->va_gen
!= VNOVAL
) ||
(vap
->va_uid
!= (u_short
) VNOVAL
) ||
(vap
->va_gid
!= (u_short
) VNOVAL
) ||
(vap
->va_atime
.tv_sec
!= VNOVAL
)) {
#if defined(MSDOSFSDEBUG)
printf("msdosfs_setattr(): returning EINVAL\n");
printf(" va_type %d, va_nlink %x, va_fsid %x, va_fileid %x\n",
vap
->va_type
, vap
->va_nlink
, vap
->va_fsid
, vap
->va_fileid
);
printf(" va_blocksize %x, va_rdev %x, va_bytes %x, va_gen %x\n",
vap
->va_blocksize
, vap
->va_rdev
, vap
->va_bytes
, vap
->va_gen
);
printf(" va_uid %x, va_gid %x, va_atime.tv_sec %x\n",
vap
->va_uid
, vap
->va_gid
, vap
->va_atime
.tv_sec
);
#endif /* defined(MSDOSFSDEBUG) */
if (vap
->va_size
!= VNOVAL
) {
if (error
= detrunc(dep
, vap
->va_size
, 0))
if (vap
->va_mtime
.tv_sec
!= VNOVAL
) {
if (error
= deupdat(dep
, &vap
->va_mtime
, 1))
* DOS files only have the ability to have thier writability
* attribute set, so we use the owner write bit to set the readonly
if (vap
->va_mode
!= (u_short
) VNOVAL
) {
/* We ignore the read and execute bits */
if (vap
->va_mode
& VWRITE
)
dep
->de_Attributes
&= ~ATTR_READONLY
;
dep
->de_Attributes
|= ATTR_READONLY
;
if (vap
->va_flags
!= VNOVAL
) {
if (error
= suser(cred
, &p
->p_acflag
))
dep
->de_flag
= vap
->va_flags
;
dep
->de_flag
&= 0xffff0000;
dep
->de_flag
|= (vap
->va_flags
& 0xffff);
msdosfs_read(vp
, uio
, ioflag
, cred
)
struct denode
*dep
= VTODE(vp
);
struct msdosfsmount
*pmp
= dep
->de_pmp
;
* If they didn't ask for any data, then we are done.
isadir
= dep
->de_Attributes
& ATTR_DIRECTORY
;
lbn
= uio
->uio_offset
>> pmp
->pm_cnshift
;
on
= uio
->uio_offset
& pmp
->pm_crbomask
;
n
= MIN((u_long
) (pmp
->pm_bpcluster
- on
), uio
->uio_resid
);
diff
= dep
->de_FileSize
- uio
->uio_offset
;
/* convert cluster # to block # if a directory */
error
= pcbmap(dep
, lbn
, &lbn
, 0);
* If we are operating on a directory file then be sure to
* do i/o with the vnode for the filesystem instead of the
* vnode for the directory.
error
= bread(pmp
->pm_devvp
, lbn
, pmp
->pm_bpcluster
,
if (vp
->v_lastr
+ 1 == lbn
&&
rablock
* pmp
->pm_bpcluster
< dep
->de_FileSize
) {
error
= breada(vp
, lbn
, pmp
->pm_bpcluster
,
rablock
, pmp
->pm_bpcluster
, NOCRED
, &bp
);
error
= bread(vp
, lbn
, pmp
->pm_bpcluster
, NOCRED
,
n
= MIN(n
, pmp
->pm_bpcluster
- bp
->b_resid
);
error
= uiomove(bp
->b_un
.b_addr
+ on
, (int) n
, uio
);
* If we have read everything from this block or have read
* to end of file then we are done with this block. Mark
* it to say the buffer can be reused if need be.
if (n
+ on
== pmp
->pm_bpcluster
||
uio
->uio_offset
== dep
->de_FileSize
)
} while (error
== 0 && uio
->uio_resid
> 0 && n
!= 0);
* Write data to a file or directory.
msdosfs_write(vp
, uio
, ioflag
, cred
)
struct proc
*p
= uio
->uio_procp
;
struct denode
*dep
= VTODE(vp
);
struct msdosfsmount
*pmp
= dep
->de_pmp
;
#if defined(MSDOSFSDEBUG)
printf("msdosfs_write(vp %08x, uio %08x, ioflag %08x, cred %08x\n",
printf("msdosfs_write(): diroff %d, dirclust %d, startcluster %d\n",
dep
->de_diroffset
, dep
->de_dirclust
, dep
->de_StartCluster
);
#endif /* defined(MSDOSFSDEBUG) */
uio
->uio_offset
= dep
->de_FileSize
;
if ((ioflag
& IO_SYNC
) == 0)
panic("msdosfs_write(): non-sync directory update");
panic("msdosfs_write(): bad file type");
if (uio
->uio_offset
< 0) {
* If they've exceeded their filesize limit, tell them about it.
if (vp
->v_type
== VREG
&& p
&&
((uio
->uio_offset
+ uio
->uio_resid
) >
p
->p_rlimit
[RLIMIT_FSIZE
].rlim_cur
)) {
* If attempting to write beyond the end of the root directory we
* stop that here because the root directory can not grow.
if ((dep
->de_Attributes
& ATTR_DIRECTORY
) &&
dep
->de_StartCluster
== MSDOSFSROOT
&&
(uio
->uio_offset
+ uio
->uio_resid
) > dep
->de_FileSize
)
* If the offset we are starting the write at is beyond the end of
* the file, then they've done a seek. Unix filesystems allow
* files with holes in them, DOS doesn't so we must fill the hole
* with zeroed blocks. We do this by calling our seek function.
* This could probably be cleaned up someday.
if (uio
->uio_offset
> dep
->de_FileSize
) {
error
= msdosfs_seek(vp
, (off_t
) 0, uio
->uio_offset
, cred
);
* Remember some values in case the write fails.
osize
= dep
->de_FileSize
;
bn
= uio
->uio_offset
>> pmp
->pm_cnshift
;
* If we are appending to the file and we are on a cluster
* boundary, then allocate a new cluster and chain it onto
if (uio
->uio_offset
== dep
->de_FileSize
&&
(uio
->uio_offset
& pmp
->pm_crbomask
) == 0) {
if (error
= extendfile(dep
, &bp
, 0))
* The block we need to write into exists, so just
error
= pcbmap(dep
, bn
, &bn
, 0);
error
= bread(thisvp
, bn
, pmp
->pm_bpcluster
, cred
, &bp
);
croffset
= uio
->uio_offset
& pmp
->pm_crbomask
;
n
= MIN(uio
->uio_resid
, pmp
->pm_bpcluster
- croffset
);
if (uio
->uio_offset
+ n
> dep
->de_FileSize
) {
dep
->de_FileSize
= uio
->uio_offset
+ n
;
vnode_pager_setsize(vp
, dep
->de_FileSize
); /* why? */
(void) vnode_pager_uncache(vp
); /* why not? */
* Should these vnode_pager_* functions be done on dir
* Copy the data from user space into the buf header.
error
= uiomove(bp
->b_un
.b_addr
+ croffset
, n
, uio
);
* If they want this synchronous then write it and wait for
* it. Otherwise, if on a cluster boundary write it
* asynchronously so we can move on to the next block
* without delay. Otherwise do a delayed write because we
* may want to write somemore into the block later.
else if (n
+ croffset
== pmp
->pm_bpcluster
) {
} while (error
== 0 && uio
->uio_resid
> 0);
* If the write failed and they want us to, truncate the file back
* to the size it was before the write was attempted.
if (error
&& (ioflag
& IO_UNIT
)) {
detrunc(dep
, osize
, ioflag
& IO_SYNC
);
uio
->uio_offset
-= resid
- uio
->uio_resid
;
if (!error
&& (ioflag
& IO_UNIT
))
error
= deupdat(dep
, &time
, 1);
msdosfs_ioctl(vp
, com
, data
, fflag
, cred
, p
)
msdosfs_select(vp
, which
, fflags
, cred
, p
)
return 1; /* DOS filesystems never block? */
msdosfs_mmap(vp
, fflags
, cred
, p
)
* Flush the blocks of a file to disk.
* This function is worthless for vnodes that represent directories. Maybe we
* could just do a sync if they try an fsync on a directory file.
msdosfs_fsync(vp
, fflags
, cred
, waitfor
, p
)
struct denode
*dep
= VTODE(vp
);
* Does this call to vflushbuf() do anything? I can find no code
* anywhere that sets v_dirtyblkhd in the vnode, which vflushbuf()
vflushbuf(vp
, waitfor
== MNT_WAIT
? B_SYNC
: 0);
return deupdat(dep
, &time
, waitfor
== MNT_WAIT
);
* Since the dos filesystem does not allow files with holes in them we must
* fill the file with zeroed blocks when a seek past the end of file
* It seems that nothing in the kernel calls the filesystem specific file seek
* functions. And, someone on the net told me that NFS never sends
* announcements of seeks to the server. So, if msdosfs ever becomes NFS
* mountable it will have to use other means to fill in holes in what would
* be a sparse file. (This appears fixed since msdosfs_write() calls seek
* before writing if the offset is past EOF)
msdosfs_seek(vp
, oldoff
, newoff
, cred
)
struct denode
*dep
= VTODE(vp
);
struct msdosfsmount
*pmp
= dep
->de_pmp
;
#if defined(MSDOSFSDEBUG)
printf("msdosfs_seek(vp %08x, oldoff %d, newoff %d, cred %08x)\n",
vp
, oldoff
, newoff
, cred
);
#endif /* defined(MSDOSFSDEBUG) */
* Compute the offset of the first byte after the last block in the
* file. If seeking beyond the end of file then fill the file with
* zeroed blocks up to the seek address.
foff
= (dep
->de_FileSize
+ (pmp
->pm_bpcluster
- 1)) & ~pmp
->pm_crbomask
;
#if defined(MSDOSFSDEBUG)
printf("seek: newoff %d > foff %d\n", newoff
, foff
);
#endif /* defined(MSDOSFSDEBUG) */
* If this is the root directory and we are attempting to
* seek beyond the end disallow it. DOS filesystem root
* directories can not grow.
* If this is a directory and the caller is not root, then
* do not let them seek beyond the end of file. If we
* allowed this then users could cause directories to grow.
* Is this really that important?
if (dep
->de_Attributes
& ATTR_DIRECTORY
) {
if (error
= suser(cred
, NULL
)) {
* Allocate and chain together as many clusters as are
* needed to get to newoff.
if (error
= extendfile(dep
, &bp
, 0))
foff
+= pmp
->pm_bpcluster
;
dep
->de_FileSize
+= pmp
->pm_bpcluster
;
dep
->de_FileSize
= newoff
;
return deupdat(dep
, &time
);
struct denode
*dep
= VTODE(ndp
->ni_vp
);
struct denode
*ddep
= VTODE(ndp
->ni_dvp
);
#if defined(MSDOSFSDEBUG)
printf("msdosfs_remove(), dep %08x, v_usecount %d\n", dep
, ndp
->ni_vp
->v_usecount
);
#endif /* defined(MSDOSFSDEBUG) */
deput(dep
); /* causes msdosfs_inactive() to be called
* DOS filesystems don't know what links are. But since we already called
* msdosfs_lookup() with create and lockparent, the parent is locked so we
* have to free it before we return the error.
struct denode
*pdep
= VTODE(ndp
->ni_dvp
);
free(ndp
->ni_pnbuf
, M_NAMEI
);
* Renames on files require moving the denode to a new hash queue since the
* denode's location is used to compute which hash queue to put the file
* in. Unless it is a rename in place. For example "mv a b".
* What follows is the basic algorithm:
* if (file move) { if (dest file exists) { remove dest file } if (dest and
* src in same directory) { rewrite name in existing directory slot } else
* { write new entry in dest directory update offset and dirclust in denode
* move denode to new hash chain clear old directory entry } } else {
* directory move if (dest directory exists) { if (dest is not empty) {
* return ENOTEMPTY } remove dest directory } if (dest and src in same
* directory) { rewrite name in existing entry } else { be sure dest is not
* a child of src directory write entry in dest directory update "." and
* ".." in moved directory update offset and dirclust in denode move denode
* to new hash chain clear old directory entry for moved directory } }
* On entry: source's parent directory is unlocked source file or directory is
* unlocked destination's parent directory is locked destination file or
* directory is locked if it exists
* On exit: all denodes should be released Notes: I'm not sure how the memory
* containing the pathnames pointed at by the nameidata structures is
* freed, there may be some memory bleeding for each rename done.
msdosfs_rename(fndp
, tndp
, p
)
int sourceisadirectory
= 0;
struct denode
*fddep
; /* from file's parent directory */
struct denode
*fdep
; /* from file or directory */
struct denode
*tddep
; /* to file's parent directory */
struct denode
*tdep
; /* to file or directory */
struct msdosfsmount
*pmp
;
struct direntry
*dotdotp
;
#if defined(MSDOSFSDEBUG)
printf("msdosfs_rename(fndp %08x, tndp %08x, p %08x\n", fndp
, tndp
, p
);
#endif /* defined(MSDOSFSDEBUG) */
fddep
= VTODE(fndp
->ni_dvp
);
fdep
= VTODE(fndp
->ni_vp
);
tddep
= VTODE(tndp
->ni_dvp
);
tdep
= tndp
->ni_vp
? VTODE(tndp
->ni_vp
) : NULL
;
/* Check for cross-device rename */
if ((fndp
->ni_vp
->v_mount
!= tndp
->ni_dvp
->v_mount
) ||
(tndp
->ni_vp
&& (fndp
->ni_vp
->v_mount
!= tndp
->ni_vp
->v_mount
))) {
* Convert the filename in tdnp into a dos filename. We copy this
* into the denode and directory entry for the destination
unix2dosfn((u_char
*) tndp
->ni_ptr
, toname
, tndp
->ni_namelen
);
* At this point this is the lock state of the denodes: fddep
* referenced fdep referenced tddep locked tdep locked if it
* Be sure we are not renaming ".", "..", or an alias of ".". This
* leads to a crippled directory tree. It's pretty tough to do a
* "ls" or "pwd" with the "." directory entry missing, and "cd .."
* doesn't work if the ".." entry is missing.
if (fdep
->de_Attributes
& ATTR_DIRECTORY
) {
if ((fndp
->ni_namelen
== 1 && fndp
->ni_ptr
[0] == '.') ||
fddep
== fdep
|| /* won't happen ? */
* If we are renaming a directory, and the directory is being moved
* to another directory, then we must be sure the destination
* directory is not in the subtree of the source directory. This
* could orphan everything under the source directory.
* doscheckpath() unlocks the destination's parent directory so we
* must look it up again to relock it.
if (fddep
->de_StartCluster
!= tddep
->de_StartCluster
)
if (sourceisadirectory
&& newparent
) {
/* doscheckpath() deput()'s tddep */
error
= doscheckpath(fdep
, tddep
, tndp
->ni_cred
);
if ((tndp
->ni_nameiop
& SAVESTART
) == 0)
panic("msdosfs_rename(): lost to startdir");
if (error
= lookup(tndp
, p
)) {
tddep
= VTODE(tndp
->ni_dvp
);
tdep
= tndp
->ni_vp
? VTODE(tndp
->ni_vp
) : NULL
;
* If the destination exists, then be sure its type (file or dir)
* matches that of the source. And, if it is a directory make sure
* it is empty. Then delete the destination.
if (tdep
->de_Attributes
& ATTR_DIRECTORY
) {
if (!sourceisadirectory
) {
if (!dosdirempty(tdep
)) {
else { /* destination is file */
if (sourceisadirectory
) {
to_dirclust
= tdep
->de_dirclust
;
to_diroffset
= tdep
->de_diroffset
;
if (error
= removede(tndp
)) {
* Remember where the slot was for createde().
tndp
->ni_msdosfs
.msdosfs_count
= 1;
tndp
->ni_msdosfs
.msdosfs_cluster
= to_dirclust
;
tndp
->ni_msdosfs
.msdosfs_offset
= to_diroffset
;
* If the source and destination are in the same directory then
* just read in the directory entry, change the name in the
* directory entry and write it back to disk.
/* tddep and fddep point to the same denode here */
DELOCK(fdep
); /* tddep is already locked */
if (error
= readep(fdep
->de_pmp
,
fndp
->ni_msdosfs
.msdosfs_cluster
,
fndp
->ni_msdosfs
.msdosfs_offset
,
bcopy(toname
, ep
->deName
, 11);
if (error
= bwrite(bp
)) {
bcopy(toname
, fdep
->de_Name
, 11); /* update denode */
* fdep locked fddep and tddep point to the same denode
* which is locked tdep is unlocked and unreferenced
* If the source and destination are in different
* directories, then mark the entry in the source directory
* as deleted and write a new entry in the destination
* directory. Then move the denode to the correct hash
* chain for its new location in the filesystem. And, if
* we moved a directory, then update its .. entry to point
* to the new parent directory. If we moved a directory
* will also insure that the directory entry on disk has a
bcopy(toname
, fdep
->de_Name
, 11); /* update denode */
if (fdep
->de_Attributes
& ATTR_DIRECTORY
) {
dirsize
= fdep
->de_FileSize
;
error
= createde(fdep
, tndp
, (struct denode
**) 0);
if (fdep
->de_Attributes
& ATTR_DIRECTORY
) {
fdep
->de_FileSize
= dirsize
;
/* should put back filename */
if (error
= readep(fdep
->de_pmp
,
fndp
->ni_msdosfs
.msdosfs_cluster
,
fndp
->ni_msdosfs
.msdosfs_offset
,
ep
->deName
[0] = SLOT_DELETED
;
if (error
= bwrite(bp
)) {
fdep
->de_dirclust
= tndp
->ni_msdosfs
.msdosfs_cluster
;
fdep
->de_diroffset
= tndp
->ni_msdosfs
.msdosfs_offset
;
/* fdep is still locked here */
* If we moved a directory to a new parent directory, then we must
* fixup the ".." entry in the moved directory.
if (sourceisadirectory
&& newparent
) {
cn
= fdep
->de_StartCluster
;
/* this should never happen */
panic("msdosfs_rename(): updating .. in root directory?\n");
error
= bread(pmp
->pm_devvp
, bn
, pmp
->pm_bpcluster
,
/* should really panic here, fs is corrupt */
dotdotp
= (struct direntry
*) bp
->b_un
.b_addr
+ 1;
dotdotp
->deStartCluster
= tddep
->de_StartCluster
;
/* should really panic here, fs is corrupt */
". ", " ", /* the . entry */
ATTR_DIRECTORY
, /* file attribute */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* resevered */
1234, 1234, /* time and date */
".. ", " ", /* the .. entry */
ATTR_DIRECTORY
, /* file attribute */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* resevered */
1234, 1234, /* time and date */
msdosfs_mkdir(ndp
, vap
, p
)
struct msdosfsmount
*pmp
;
* If this is the root directory and there is no space left we
* can't do anything. This is because the root directory can not
if (pdep
->de_StartCluster
== MSDOSFSROOT
&& ndp
->ni_msdosfs
.msdosfs_count
== 0) {
free(ndp
->ni_pnbuf
, M_NAMEI
);
* Allocate a cluster to hold the about to be created directory.
if (error
= clusteralloc(pmp
, &newcluster
, CLUST_EOFE
)) {
free(ndp
->ni_pnbuf
, M_NAMEI
);
* Now fill the cluster with the "." and ".." entries. And write
* the cluster to disk. This way it is there for the parent
* directory to be pointing at if there were a crash.
bn
= cntobn(pmp
, newcluster
);
bp
= getblk(pmp
->pm_devvp
, bn
, pmp
->pm_bpcluster
); /* always succeeds */
bzero(bp
->b_un
.b_addr
, pmp
->pm_bpcluster
);
bcopy(&dosdirtemplate
, bp
->b_un
.b_addr
, sizeof dosdirtemplate
);
denp
= (struct direntry
*) bp
->b_un
.b_addr
;
denp
->deStartCluster
= newcluster
;
unix2dostime(&time
, (union dosdate
*) & denp
->deDate
,
(union dostime
*) & denp
->deTime
);
denp
->deStartCluster
= pdep
->de_StartCluster
;
unix2dostime(&time
, (union dosdate
*) & denp
->deDate
,
(union dostime
*) & denp
->deTime
);
if (error
= bwrite(bp
)) {
clusterfree(pmp
, newcluster
, NULL
);
free(ndp
->ni_pnbuf
, M_NAMEI
);
* Now build up a directory entry pointing to the newly allocated
* cluster. This will be written to an empty slot in the parent
bzero(ndep
, sizeof(*ndep
));
unix2dosfn((u_char
*) ndp
->ni_ptr
, ndep
->de_Name
, ndp
->ni_namelen
);
unix2dostime(&time
, (union dosdate
*) & ndep
->de_Date
,
(union dostime
*) & ndep
->de_Time
);
ndep
->de_StartCluster
= newcluster
;
ndep
->de_Attributes
= ATTR_DIRECTORY
;
ndep
->de_pmp
= pmp
; /* createde() needs this */
error
= createde(ndep
, ndp
, &ndep
);
clusterfree(pmp
, newcluster
, NULL
);
ndp
->ni_vp
= DETOV(ndep
);
free(ndp
->ni_pnbuf
, M_NAMEI
);
#if defined(MSDOSFSDEBUG)
printf("msdosfs_mkdir(): deput(%08x), vnode %08x\n", pdep
, DETOV(pdep
));
#endif /* defined(MSDOSFSDEBUG) */
ddep
= VTODE(ndp
->ni_dvp
); /* parent dir of dir to delete */
dep
= VTODE(ndp
->ni_vp
);/* directory to delete */
* Don't let "rmdir ." go thru.
* Be sure the directory being deleted is empty.
if (dosdirempty(dep
) == 0) {
* Delete the entry from the directory. For dos filesystems this
* gets rid of the directory entry on disk, the in memory copy
* still exists but the de_refcnt is <= 0. This prevents it from
* being found by deget(). When the deput() on dep is done we give
* up access and eventually msdosfs_reclaim() will be called which
* will remove it from the denode cache.
if (error
= removede(ndp
))
* This is where we decrement the link count in the parent
* directory. Since dos filesystems don't do this we just purge
* the name cache and let go of the parent directory denode.
cache_purge(DETOV(ddep
));
ndp
->ni_dvp
= NULL
; /* getting rid of parent dir pointer? */
* Truncate the directory that is being deleted.
error
= detrunc(dep
, (u_long
) 0, IO_SYNC
);
* DOS filesystems don't know what symlinks are.
msdosfs_symlink(ndp
, vap
, target
, p
)
struct denode
*pdep
= VTODE(ndp
->ni_dvp
);
free(ndp
->ni_pnbuf
, M_NAMEI
);
* Dummy dirents to simulate the "." and ".." entries of the root directory
* in a dos filesystem. Dos doesn't provide these. Note that each entry
* must be the same size as a dos directory entry (32 bytes).
sizeof(struct direntry
), /* d_reclen */
sizeof(struct direntry
), /* d_reclen */
msdosfs_readdir(vp
, uio
, cred
, eofflagp
, cookies
, ncookies
)
struct denode
*dep
= VTODE(vp
);
struct msdosfsmount
*pmp
= dep
->de_pmp
;
u_char dirbuf
[512]; /* holds converted dos directories */
#if defined(MSDOSFSDEBUG)
printf("msdosfs_readdir(): vp %08x, uio %08x, cred %08x, eofflagp %08x\n",
vp
, uio
, cred
, eofflagp
);
#endif /* defined(MSDOSFSDEBUG) */
* msdosfs_readdir() won't operate properly on regular files since
* it does i/o only with the the filesystem vnode, and hence can
* retrieve the wrong block from the buffer cache for a plain file.
* So, fail attempts to readdir() on a plain file.
if ((dep
->de_Attributes
& ATTR_DIRECTORY
) == 0)
* If the user buffer is smaller than the size of one dos directory
* entry or the file offset is not a multiple of the size of a
* directory entry, then we fail the read.
count
= uio
->uio_resid
& ~(sizeof(struct direntry
) - 1);
lost
= uio
->uio_resid
- count
;
if (count
< sizeof(struct direntry
) ||
(uio
->uio_offset
& (sizeof(struct direntry
) - 1)))
uio
->uio_iov
->iov_len
= count
;
* If they are reading from the root directory then, we simulate
* the . and .. entries since these don't exist in the root
* directory. We also set the offset bias to make up for having to
* simulate these entries. By this I mean that at file offset 64 we
* read the first entry in the root directory that lives on disk.
if (dep
->de_StartCluster
== MSDOSFSROOT
) {
* printf("msdosfs_readdir(): going after . or .. in root
* dir, offset %d\n", uio->uio_offset);
bias
= 2 * sizeof(struct direntry
);
if (uio
->uio_offset
< 2 * sizeof(struct direntry
)) {
&& uio
->uio_offset
!= sizeof(struct direntry
)) {
*cookies
++ = sizeof(struct direntry
);
*cookies
++ = 2 * sizeof(struct direntry
);
error
= uiomove((char *) rootdots
+ uio
->uio_offset
,
n
* sizeof(struct direntry
), uio
);
while (!error
&& uio
->uio_resid
> 0 && ncookies
> 0) {
lbn
= (uio
->uio_offset
- bias
) >> pmp
->pm_cnshift
;
on
= (uio
->uio_offset
- bias
) & pmp
->pm_crbomask
;
n
= MIN((u_long
) (pmp
->pm_bpcluster
- on
), uio
->uio_resid
);
diff
= dep
->de_FileSize
- (uio
->uio_offset
- bias
);
error
= pcbmap(dep
, lbn
, &bn
, &cn
);
error
= bread(pmp
->pm_devvp
, bn
, pmp
->pm_bpcluster
, NOCRED
, &bp
);
n
= MIN(n
, pmp
->pm_bpcluster
- bp
->b_resid
);
* code to convert from dos directory entries to ufs
dentp
= (struct direntry
*) (bp
->b_un
.b_addr
+ on
);
crnt
= (struct dirent
*) dirbuf
;
while ((char *) dentp
< bp
->b_un
.b_addr
+ on
+ n
) {
* printf("rd: dentp %08x prev %08x crnt %08x
* deName %02x attr %02x\n", dentp, prev, crnt,
* dentp->deName[0], dentp->deAttributes);
* If we have an empty entry or a slot from a
* deleted file, or a volume label entry just
* concatenate its space onto the end of the
* previous entry or, manufacture an empty entry if
* there is no previous entry.
if (dentp
->deName
[0] == SLOT_EMPTY
||
dentp
->deName
[0] == SLOT_DELETED
||
(dentp
->deAttributes
& ATTR_VOLUME
)) {
prev
->d_reclen
+= sizeof(struct direntry
);
prev
->d_reclen
= sizeof(struct direntry
);
* this computation of d_fileno must match
* the computation of va_fileid in
if (dentp
->deAttributes
& ATTR_DIRECTORY
) {
/* if this is the root directory */
if ((fileno
= dentp
->deStartCluster
) == MSDOSFSROOT
)
* if the file's dirent lives in
if ((fileno
= cn
) == MSDOSFSROOT
)
fileno
= (fileno
<< 16) |
((dentp
- (struct direntry
*) bp
->b_un
.b_addr
) & 0xffff);
crnt
->d_reclen
= sizeof(struct direntry
);
crnt
->d_namlen
= dos2unixfn(dentp
->deName
,
(u_char
*) crnt
->d_name
);
* printf("readdir: file %s, fileno %08x,
* attr %02x, start %08x\n", crnt->d_name,
* crnt->d_fileno, dentp->deAttributes,
* dentp->deStartCluster);
*cookies
++ = (u_int
)((char *)dentp
- bp
->b_un
.b_addr
- on
)
crnt
= (struct dirent
*) ((char *) crnt
+ sizeof(struct direntry
));
* If our intermediate buffer is full then copy its
* contents to user space. I would just use the
* buffer the buf header points to but, I'm afraid
* that when we brelse() it someone else might find
* it in the cache and think its contents are
* valid. Maybe there is a way to invalidate the
* buffer before brelse()'ing it.
if ((u_char
*) crnt
>= &dirbuf
[sizeof dirbuf
]) {
error
= uiomove(dirbuf
, sizeof(dirbuf
), uio
);
crnt
= (struct dirent
*) dirbuf
;
error
= uiomove(dirbuf
, (char *) crnt
- (char *) dirbuf
,
* If we have read everything from this block or have read
* to end of file then we are done with this block. Mark
* it to say the buffer can be reused if need be.
if (n
+ on
== pmp
->pm_bpcluster
||
(uio
->uio_offset
- bias
) == dep
->de_FileSize
)
* I don't know why we bother setting this eofflag, getdirentries()
* in vfs_syscalls.c doesn't bother to look at it when we return.
* (because NFS uses it in nfs_serv.c -- JMP)
if (dep
->de_FileSize
- uio
->uio_offset
- bias
<= 0)
* DOS filesystems don't know what symlinks are.
msdosfs_readlink(vp
, uio
, cred
)
if ((ndp
->ni_nameiop
& (HASBUF
| SAVESTART
)) == HASBUF
)
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
struct denode
*dep
= VTODE(vp
);
struct denode
*dep
= VTODE(vp
);
if (!(dep
->de_flag
& DELOCKED
))
panic("msdosfs_unlock: denode not locked");
return VTODE(vp
)->de_flag
& DELOCKED
? 1 : 0;
* vp - address of vnode file the file bn - which cluster we are interested
* in mapping to a filesystem block number. vpp - returns the vnode for the
* block special file holding the filesystem containing the file of
* interest bnp - address of where to return the filesystem relative block
msdosfs_bmap(vp
, bn
, vpp
, bnp
)
struct denode
*dep
= VTODE(vp
);
struct msdosfsmount
*pmp
= dep
->de_pmp
;
return pcbmap(dep
, bn
<< (pmp
->pm_cnshift
- pmp
->pm_bnshift
), bnp
, 0);
struct denode
*dep
= VTODE(bp
->b_vp
);
struct msdosfsmount
*pmp
= dep
->de_pmp
;
if (bp
->b_vp
->v_type
== VBLK
|| bp
->b_vp
->v_type
== VCHR
)
panic("msdosfs_strategy: spec");
* If we don't already know the filesystem relative block number
* then get it using pcbmap(). If pcbmap() returns the block
* number as -1 then we've got a hole in the file. DOS filesystems
* don't allow files with holes, so we shouldn't ever see this.
if (bp
->b_blkno
== bp
->b_lblkno
) {
if (error
= pcbmap(dep
, bp
->b_lblkno
, &bp
->b_blkno
, 0))
if ((long) bp
->b_blkno
== -1)
if ((long) bp
->b_blkno
== -1) {
#endif /* defined(DIAGNOSTIC) */
* Read/write the block from/to the disk that contains the desired
(*(vp
->v_op
->vop_strategy
)) (bp
);
struct denode
*dep
= VTODE(vp
);
printf("tag VT_MSDOSFS, startcluster %d, dircluster %d, diroffset %d ",
dep
->de_StartCluster
, dep
->de_dirclust
, dep
->de_diroffset
);
printf(" dev %d, %d, %s\n",
major(dep
->de_dev
), minor(dep
->de_dev
),
dep
->de_flag
& DELOCKED
? "(LOCKED)" : "");
printf(" owner pid %d", dep
->de_spare0
);
printf(" waiting pid %d", dep
->de_spare1
);
msdosfs_advlock(vp
, id
, op
, fl
, flags
)
return EINVAL
; /* we don't do locking yet */
struct vnodeops msdosfs_vnodeops
= {