* 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.
#include "resourcevar.h" /* defines plimit structure in proc struct */
#include "file.h" /* define FWRITE ... */
#include "specdev.h" /* defines plimit structure in the proc struct */
#include "dir.h" /* defines dirent structure */
* 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.
* On entry the directory to contain the file being
* created is locked. We must release before we
* 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.
struct direntry
*ndirp
= &ndirent
.de_de
;
struct denode
*pdep
= VTODE(ndp
->ni_dvp
);
printf("pcfs_create(ndp %08x, vap %08x, p %08x\n", ndp
, vap
, p
);
#endif /* defined(PCFSDEBUG) */
* 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 readonly.
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 */
pcfs_mknod(ndp
, vap
, cred
, p
)
error
= pcfs_mkdir(ndp
, vap
, p
);
* pcfs_create() sets ndp->ni_vp.
error
= pcfs_create(ndp
, vap
, p
);
pcfs_open(vp
, mode
, cred
, p
)
pcfs_close(vp
, fflag
, cred
, p
)
struct denode
*dep
= VTODE(vp
);
if (vp
->v_usecount
> 1 && !(dep
->de_flag
& DELOCKED
))
pcfs_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 regret this.
* mode is filled with a combination of VREAD, VWRITE,
* and/or VEXEC bits turned on. In an octal number these
* 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
;
pcfs_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 pcfs_readdir() to compute d_fileno.
* If not, pwd doesn't work. */
if (dep
->de_Attributes
& ATTR_DIRECTORY
) {
if ((cn
= dep
->de_StartCluster
) == PCFSROOT
)
if ((cn
= dep
->de_dirclust
) == PCFSROOT
)
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;
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
;
pcfs_setattr(vp
, vap
, cred
, p
)
struct denode
*dep
= VTODE(vp
);
printf("pcfs_setattr(): vp %08x, vap %08x, cred %08x, p %08x\n",
#endif /* defined(PCFSDEBUG) */
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
)) {
printf("pcfs_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(PCFSDEBUG) */
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 attribute.
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);
pcfs_read(vp
, uio
, ioflag
, cred
)
struct denode
*dep
= VTODE(vp
);
struct pcfsmount
*pmp
= dep
->de_pmp
;
* If they didn't ask for any data, then we
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
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.
pcfs_write(vp
, uio
, ioflag
, cred
)
struct proc
*p
= uio
->uio_procp
;
struct denode
*dep
= VTODE(vp
);
struct pcfsmount
*pmp
= dep
->de_pmp
;
printf("pcfs_write(vp %08x, uio %08x, ioflag %08x, cred %08x\n",
printf("pcfs_write(): diroff %d, dirclust %d, startcluster %d\n",
dep
->de_diroffset
, dep
->de_dirclust
, dep
->de_StartCluster
);
#endif /* defined(PCFSDEBUG) */
uio
->uio_offset
= dep
->de_FileSize
;
if ((ioflag
& IO_SYNC
) == 0)
panic("pcfs_write(): non-sync directory update");
panic("pcfs_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
if ((dep
->de_Attributes
& ATTR_DIRECTORY
) &&
dep
->de_StartCluster
== PCFSROOT
&&
(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
if (uio
->uio_offset
> dep
->de_FileSize
) {
error
= pcfs_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 the file.
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,
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 files? */
* 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.
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
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);
pcfs_ioctl(vp
, com
, data
, fflag
, cred
, p
)
pcfs_select(vp
, which
, fflags
, cred
, p
)
return 1; /* DOS filesystems never block? */
pcfs_mmap(vp
, fflags
, cred
, p
)
* Flush the blocks of a file to disk.
* This function is worthless for vnodes that represent
* Maybe we could just do a sync if they try an fsync
pcfs_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() seems to depend upon.
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 happens.
* 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 pcfs ever becomes
* NFS mountable it will have to use other means to
* fill in holes in what would be a sparse file.
pcfs_seek(vp
, oldoff
, newoff
, cred
)
struct denode
*dep
= VTODE(vp
);
struct pcfsmount
*pmp
= dep
->de_pmp
;
printf("pcfs_seek(vp %08x, oldoff %d, newoff %d, cred %08x)\n",
vp
, oldoff
, newoff
, cred
);
#endif /* defined(PCFSDEBUG) */
* 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
;
printf("seek: newoff %d > foff %d\n", newoff
, foff
);
#endif /* defined(PCFSDEBUG) */
* If this is the root directory and we are
* attempting to seek beyond the end disallow
* it. DOS filesystem root directories can
* 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
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
);
printf("pcfs_remove(), dep %08x, v_usecount %d\n", dep
, ndp
->ni_vp
->v_usecount
);
#endif /* defined(PCFSDEBUG) */
deput(dep
); /* causes pcfs_inactive() to be called via vrele() */
* DOS filesystems don't know what links are.
* 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 (dest file exists) {
* if (dest and src in same directory) {
* rewrite name in existing directory slot
* 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) {
* if (dest and src in same directory) {
* rewrite name in existing entry
* 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
* 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
* all denodes should be released
* 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.
pcfs_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 direntry
*dotdotp
;
printf("pcfs_rename(fndp %08x, tndp %08x, p %08x\n", fndp
, tndp
, p
);
#endif /* defined(PCFSDEBUG) */
fddep
= VTODE(fndp
->ni_dvp
);
fdep
= VTODE(fndp
->ni_vp
);
tddep
= VTODE(tndp
->ni_dvp
);
tdep
= tndp
->ni_vp
? VTODE(tndp
->ni_vp
) : NULL
;
* Convert the filename in tdnp into a dos filename.
* We copy this into the denode and directory entry
* for the destination file/directory.
unix2dosfn((u_char
*)tndp
->ni_ptr
, toname
, tndp
->ni_namelen
);
* At this point this is the lock state of the denodes:
* tdep locked if it exists
* 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("pcfs_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_pcfs
.pcfs_count
= 1;
tndp
->ni_pcfs
.pcfs_cluster
= to_dirclust
;
tndp
->ni_pcfs
.pcfs_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
/* tddep and fddep point to the same denode here */
DELOCK(fdep
); /* tddep is already locked */
if (error
= readde(fdep
, &bp
, &ep
)) {
bcopy(toname
, ep
->deName
, 11);
if (error
= bwrite(bp
)) {
bcopy(toname
, fdep
->de_Name
, 11); /* update denode */
* 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
* If we moved a directory will also insure that
* the directory entry on disk has a filesize of
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
= readde(fdep
, &bp
, &ep
)) { /* read source de */
ep
->deName
[0] = SLOT_DELETED
;
if (error
= bwrite(bp
)) {
fdep
->de_dirclust
= tndp
->ni_pcfs
.pcfs_cluster
;
fdep
->de_diroffset
= tndp
->ni_pcfs
.pcfs_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
if (sourceisadirectory
&& newparent
) {
cn
= fdep
->de_StartCluster
;
/* this should never happen */
panic("pcfs_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 */
* If this is the root directory and there is no space left
* we can't do anything. This is because the root directory
if (pdep
->de_StartCluster
== PCFSROOT
&& ndp
->ni_pcfs
.pcfs_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
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 directory.
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
);
printf("pcfs_mkdir(): deput(%08x), vnode %08x\n", pdep
, DETOV(pdep
));
#endif /* defined(PCFSDEBUG) */
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 pcfs_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
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.
pcfs_symlink(ndp
, vap
, target
, p
)
* Dummy dirents to simulate the "." and ".." entries
* of the root directory in a dos filesystem. Dos doesn't
* 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 */
pcfs_readdir(vp
, uio
, cred
, eofflagp
)
struct denode
*dep
= VTODE(vp
);
struct pcfsmount
*pmp
= dep
->de_pmp
;
u_char dirbuf
[512]; /* holds converted dos directories */
printf("pcfs_readdir(): vp %08x, uio %08x, cred %08x, eofflagp %08x\n",
vp
, uio
, cred
, eofflagp
);
#endif /* defined(PCFSDEBUG) */
* pcfs_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()
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
== PCFSROOT
) {
/*printf("pcfs_readdir(): going after . or .. in root dir, offset %d\n",
bias
= 2*sizeof(struct direntry
);
if (uio
->uio_offset
< 2*sizeof(struct direntry
)) {
error
= uiomove((char *)rootdots
+ uio
->uio_offset
,
sizeof rootdots
- uio
->uio_offset
, uio
);
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 directory entries
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
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 pcfs_getattr */
if (dentp
->deAttributes
& ATTR_DIRECTORY
) {
/* if this is the root directory */
if ((fileno
= dentp
->deStartCluster
) == PCFSROOT
)
/* if the file's dirent lives in root dir */
if ((fileno
= cn
) == PCFSROOT
)
fileno
= (fileno
<< 16) |
((dentp
- (struct direntry
*)bp
->b_un
.b_addr
) & 0xffff);
crnt
->d_reclen
= sizeof(struct direntry
);
crnt
->d_namlen
= dos2unixfn(dentp
->deName
,
/*printf("readdir: file %s, fileno %08x, attr %02x, start %08x\n",
crnt->d_name, crnt->d_fileno, dentp->deAttributes, dentp->deStartCluster);*/
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
if (n
+ on
== pmp
->pm_bpcluster
||
(uio
->uio_offset
-bias
) == dep
->de_FileSize
)
} while (error
== 0 && uio
->uio_resid
> 0 && n
!= 0);
* 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.
if (dep
->de_FileSize
- uio
->uio_offset
- bias
<= 0)
* DOS filesystems don't know what symlinks are.
pcfs_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("pcfs_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
pcfs_bmap(vp
, bn
, vpp
, bnp
)
struct denode
*dep
= VTODE(vp
);
struct pcfsmount
*pmp
= dep
->de_pmp
;
return pcbmap(dep
, bn
<< (pmp
->pm_cnshift
- pmp
->pm_bnshift
), bnp
, 0);
struct denode
*dep
= VTODE(bp
->b_vp
);
struct pcfsmount
*pmp
= dep
->de_pmp
;
if (bp
->b_vp
->v_type
== VBLK
|| bp
->b_vp
->v_type
== VCHR
)
panic("pcfs_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_PCFS, 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
);
pcfs_advlock(vp
, id
, op
, fl
, flags
)
return EINVAL
; /* we don't do locking yet */
struct vnodeops pcfs_vnodeops
= {