diff options
31 files changed, 12659 insertions, 9 deletions
diff --git a/sys/conf/files b/sys/conf/files index f314a1b..a3d4978 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -22,6 +22,12 @@ isofs/cd9660/cd9660_rrip.c optional cd9660 isofs/cd9660/cd9660_util.c optional cd9660 isofs/cd9660/cd9660_vfsops.c optional cd9660 isofs/cd9660/cd9660_vnops.c optional cd9660 +msdosfs/msdosfs_conv.c optional msdosfs +msdosfs/msdosfs_denode.c optional msdosfs +msdosfs/msdosfs_fat.c optional msdosfs +msdosfs/msdosfs_lookup.c optional msdosfs +msdosfs/msdosfs_vfsops.c optional msdosfs +msdosfs/msdosfs_vnops.c optional msdosfs kdb/kdb_access.c optional kadb kdb/kdb_command.c optional kadb kdb/kdb_ctype.c optional kadb diff --git a/sys/fs/msdosfs/bootsect.h b/sys/fs/msdosfs/bootsect.h new file mode 100644 index 0000000..099b15f --- /dev/null +++ b/sys/fs/msdosfs/bootsect.h @@ -0,0 +1,70 @@ +/* $Id$ */ +/* $NetBSD: bootsect.h,v 1.4 1994/06/29 06:35:28 cgd Exp $ */ + +/* + * 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. + * + * October 1992 + */ + +/* + * Format of a boot sector. This is the first sector on a DOS floppy disk + * or the fist sector of a partition on a hard disk. But, it is not the + * first sector of a partitioned hard disk. + */ +struct bootsector33 { + u_char bsJump[3]; /* jump instruction E9xxxx or EBxx90 */ + char bsOemName[8]; /* OEM name and version */ + char bsBPB[19]; /* BIOS parameter block */ + char bsDriveNumber; /* drive number (0x80) */ + char bsBootCode[474]; /* pad so structure is 512 bytes long */ + u_short bsBootSectSig; +#define BOOTSIG 0xaa55 +}; + +struct bootsector50 { + u_char bsJump[3]; /* jump instruction E9xxxx or EBxx90 */ + char bsOemName[8]; /* OEM name and version */ + char bsBPB[25]; /* BIOS parameter block */ + char bsDriveNumber; /* drive number (0x80) */ + char bsReserved1; /* reserved */ + char bsBootSignature; /* extended boot signature (0x29) */ +#define EXBOOTSIG 0x29 + char bsVolumeID[4]; /* volume ID number */ + char bsVolumeLabel[11]; /* volume label */ + char bsFileSysType[8]; /* file system type (FAT12 or FAT16) */ + char bsBootCode[448]; /* pad so structure is 512 bytes long */ + u_short bsBootSectSig; +#define BOOTSIG 0xaa55 +}; + +union bootsector { + struct bootsector33 bs33; + struct bootsector50 bs50; +}; + +/* + * Shorthand for fields in the bpb. + */ +#define bsBytesPerSec bsBPB.bpbBytesPerSec +#define bsSectPerClust bsBPB.bpbSectPerClust +#define bsResSectors bsBPB.bpbResSectors +#define bsFATS bsBPB.bpbFATS +#define bsRootDirEnts bsBPB.bpbRootDirEnts +#define bsSectors bsBPB.bpbSectors +#define bsMedia bsBPB.bpbMedia +#define bsFATsecs bsBPB.bpbFATsecs +#define bsSectPerTrack bsBPB.bpbSectPerTrack +#define bsHeads bsBPB.bpbHeads +#define bsHiddenSecs bsBPB.bpbHiddenSecs +#define bsHugeSectors bsBPB.bpbHugeSectors diff --git a/sys/fs/msdosfs/bpb.h b/sys/fs/msdosfs/bpb.h new file mode 100644 index 0000000..70e4de2 --- /dev/null +++ b/sys/fs/msdosfs/bpb.h @@ -0,0 +1,120 @@ +/* $Id$ */ +/* $NetBSD: bpb.h,v 1.3 1994/06/29 06:35:29 cgd Exp $ */ + +/* + * 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. + * + * October 1992 + */ + +/* + * BIOS Parameter Block (BPB) for DOS 3.3 + */ +struct bpb33 { + u_short bpbBytesPerSec; /* bytes per sector */ + u_char bpbSecPerClust; /* sectors per cluster */ + u_short bpbResSectors; /* number of reserved sectors */ + u_char bpbFATs; /* number of FATs */ + u_short bpbRootDirEnts; /* number of root directory entries */ + u_short bpbSectors; /* total number of sectors */ + u_char bpbMedia; /* media descriptor */ + u_short bpbFATsecs; /* number of sectors per FAT */ + u_short bpbSecPerTrack; /* sectors per track */ + u_short bpbHeads; /* number of heads */ + u_short bpbHiddenSecs; /* number of hidden sectors */ +}; + +/* + * BPB for DOS 5.0 The difference is bpbHiddenSecs is a short for DOS 3.3, + * and bpbHugeSectors is not in the 3.3 bpb. + */ +struct bpb50 { + u_short bpbBytesPerSec; /* bytes per sector */ + u_char bpbSecPerClust; /* sectors per cluster */ + u_short bpbResSectors; /* number of reserved sectors */ + u_char bpbFATs; /* number of FATs */ + u_short bpbRootDirEnts; /* number of root directory entries */ + u_short bpbSectors; /* total number of sectors */ + u_char bpbMedia; /* media descriptor */ + u_short bpbFATsecs; /* number of sectors per FAT */ + u_short bpbSecPerTrack; /* sectors per track */ + u_short bpbHeads; /* number of heads */ + u_long bpbHiddenSecs; /* number of hidden sectors */ + u_long bpbHugeSectors; /* number of sectrs if bpbSectors == 0 */ +}; + +/* + * The following structures represent how the bpb's look on disk. shorts + * and longs are just character arrays of the appropriate length. This is + * because the compiler forces shorts and longs to align on word or + * halfword boundaries. + * + * XXX The little-endian code here assumes that the processor can access + * 16-bit and 32-bit quantities on byte boundaries. If this is not true, + * use the macros for the big-endian case. + */ +#include <machine/endian.h> +#if BYTE_ORDER == LITTLE_ENDIAN /* && can do unaligned accesses */ +#define getushort(x) *((u_short *)(x)) +#define getulong(x) *((u_long *)(x)) +#define putushort(p, v) (*((u_short *)(p)) = (v)) +#define putulong(p, v) (*((u_long *)(p)) = (v)) + +#else +#define getushort(x) (((u_char *)(x))[0] + (((u_char *)(x))[1] << 8)) +#define getulong(x) (((u_char *)(x))[0] + (((u_char *)(x))[1] << 8) \ + + (((u_char *)(x))[2] << 16) \ + + (((u_char *)(x))[3] << 24)) +#define putushort(p, v) (((u_char *)(p))[0] = (v), \ + ((u_char *)(p))[1] = (v) >> 8) +#define putulong(p, v) (((u_char *)(p))[0] = (v), \ + ((u_char *)(p))[1] = (v) >> 8, \ + ((u_char *)(p))[2] = (v) >> 16,\ + ((u_char *)(p))[3] = (v) >> 24) +#endif + +/* + * BIOS Parameter Block (BPB) for DOS 3.3 + */ +struct byte_bpb33 { + char bpbBytesPerSec[2]; /* bytes per sector */ + char bpbSecPerClust; /* sectors per cluster */ + char bpbResSectors[2]; /* number of reserved sectors */ + char bpbFATs; /* number of FATs */ + char bpbRootDirEnts[2]; /* number of root directory entries */ + char bpbSectors[2]; /* total number of sectors */ + char bpbMedia; /* media descriptor */ + char bpbFATsecs[2]; /* number of sectors per FAT */ + char bpbSecPerTrack[2]; /* sectors per track */ + char bpbHeads[2]; /* number of heads */ + char bpbHiddenSecs[2]; /* number of hidden sectors */ +}; + +/* + * BPB for DOS 5.0 The difference is bpbHiddenSecs is a short for DOS 3.3, + * and bpbHugeSectors is not in the 3.3 bpb. + */ +struct byte_bpb50 { + char bpbBytesPerSec[2]; /* bytes per sector */ + char bpbSecPerClust; /* sectors per cluster */ + char bpbResSectors[2]; /* number of reserved sectors */ + char bpbFATs; /* number of FATs */ + char bpbRootDirEnts[2]; /* number of root directory entries */ + char bpbSectors[2]; /* total number of sectors */ + char bpbMedia; /* media descriptor */ + char bpbFATsecs[2]; /* number of sectors per FAT */ + char bpbSecPerTrack[2]; /* sectors per track */ + char bpbHeads[2]; /* number of heads */ + char bpbHiddenSecs[4]; /* number of hidden sectors */ + char bpbHugeSectors[4]; /* number of sectrs if bpbSectors == 0 */ +}; diff --git a/sys/fs/msdosfs/denode.h b/sys/fs/msdosfs/denode.h new file mode 100644 index 0000000..8df3e4c --- /dev/null +++ b/sys/fs/msdosfs/denode.h @@ -0,0 +1,267 @@ +/* $Id$ */ +/* $NetBSD: denode.h,v 1.8 1994/08/21 18:43:49 ws Exp $ */ + +/*- + * Copyright (C) 1994 Wolfgang Solfrank. + * Copyright (C) 1994 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 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. + * + * October 1992 + */ + +/* + * This is the pc filesystem specific portion of the vnode structure. + * + * To describe a file uniquely the de_dirclust, de_diroffset, and + * de_StartCluster fields are used. + * + * de_dirclust contains the cluster number of the directory cluster + * containing the entry for a file or directory. + * de_diroffset is the index into the cluster for the entry describing + * a file or directory. + * de_StartCluster is the number of the first cluster of the file or directory. + * + * Now to describe the quirks of the pc filesystem. + * - Clusters 0 and 1 are reserved. + * - The first allocatable cluster is 2. + * - The root directory is of fixed size and all blocks that make it up + * are contiguous. + * - Cluster 0 refers to the root directory when it is found in the + * startcluster field of a directory entry that points to another directory. + * - Cluster 0 implies a 0 length file when found in the start cluster field + * of a directory entry that points to a file. + * - You can't use the cluster number 0 to derive the address of the root + * directory. + * - Multiple directory entries can point to a directory. The entry in the + * parent directory points to a child directory. Any directories in the + * child directory contain a ".." entry that points back to the parent. + * The child directory itself contains a "." entry that points to itself. + * - The root directory does not contain a "." or ".." entry. + * - Directory entries for directories are never changed once they are created + * (except when removed). The size stays 0, and the last modification time + * is never changed. This is because so many directory entries can point to + * the physical clusters that make up a directory. It would lead to an + * update nightmare. + * - The length field in a directory entry pointing to a directory contains 0 + * (always). The only way to find the end of a directory is to follow the + * cluster chain until the "last cluster" marker is found. + * + * My extensions to make this house of cards work. These apply only to the in + * memory copy of the directory entry. + * - A reference count for each denode will be kept since dos doesn't keep such + * things. + */ + +/* + * Internal pseudo-offset for (nonexistent) directory entry for the root + * dir in the root dir + */ +#define MSDOSFSROOT_OFS 0x1fffffff + +/* + * The fat cache structure. fc_fsrcn is the filesystem relative cluster + * number that corresponds to the file relative cluster number in this + * structure (fc_frcn). + */ +struct fatcache { + u_short fc_frcn; /* file relative cluster number */ + u_short fc_fsrcn; /* filesystem relative cluster number */ +}; + +/* + * The fat entry cache as it stands helps make extending files a "quick" + * operation by avoiding having to scan the fat to discover the last + * cluster of the file. The cache also helps sequential reads by + * remembering the last cluster read from the file. This also prevents us + * from having to rescan the fat to find the next cluster to read. This + * cache is probably pretty worthless if a file is opened by multiple + * processes. + */ +#define FC_SIZE 2 /* number of entries in the cache */ +#define FC_LASTMAP 0 /* entry the last call to pcbmap() resolved + * to */ +#define FC_LASTFC 1 /* entry for the last cluster in the file */ + +#define FCE_EMPTY 0xffff /* doesn't represent an actual cluster # */ + +/* + * Set a slot in the fat cache. + */ +#define fc_setcache(dep, slot, frcn, fsrcn) \ + (dep)->de_fc[slot].fc_frcn = frcn; \ + (dep)->de_fc[slot].fc_fsrcn = fsrcn; + +/* + * This is the in memory variant of a dos directory entry. It is usually + * contained within a vnode. + */ +struct denode { + struct denode *de_next; /* Hash chain forward */ + struct denode **de_prev; /* Hash chain back */ + struct vnode *de_vnode; /* addr of vnode we are part of */ + struct vnode *de_devvp; /* vnode of blk dev we live on */ + u_long de_flag; /* flag bits */ + dev_t de_dev; /* device where direntry lives */ + u_long de_dirclust; /* cluster of the directory file containing this entry */ + u_long de_diroffset; /* ordinal of this entry in the directory */ + u_long de_fndclust; /* cluster of found dir entry */ + u_long de_fndoffset; /* offset of found dir entry */ + long de_refcnt; /* reference count */ + struct msdosfsmount *de_pmp; /* addr of our mount struct */ + struct lockf *de_lockf; /* byte level lock list */ + pid_t de_lockholder; /* current lock holder */ + pid_t de_lockwaiter; /* lock wanter */ + /* the next two fields must be contiguous in memory... */ + u_char de_Name[8]; /* name, from directory entry */ + u_char de_Extension[3]; /* extension, from directory entry */ + u_char de_Attributes; /* attributes, from directory entry */ + u_short de_Time; /* creation time */ + u_short de_Date; /* creation date */ + u_short de_StartCluster; /* starting cluster of file */ + u_long de_FileSize; /* size of file in bytes */ + struct fatcache de_fc[FC_SIZE]; /* fat cache */ +}; + +/* + * Values for the de_flag field of the denode. + */ +#define DE_LOCKED 0x0001 /* directory entry is locked */ +#define DE_WANTED 0x0002 /* someone wants this de */ +#define DE_UPDATE 0x0004 /* file has been modified */ +#define DE_MODIFIED 0x0080 /* denode wants to be written back to disk */ + +/* + * Transfer directory entries between internal and external form. + * dep is a struct denode * (internal form), + * dp is a struct direntry * (external form). + */ +#define DE_INTERNALIZE(dep, dp) \ + (bcopy((dp)->deName, (dep)->de_Name, 11), \ + (dep)->de_Attributes = (dp)->deAttributes, \ + (dep)->de_Time = getushort((dp)->deTime), \ + (dep)->de_Date = getushort((dp)->deDate), \ + (dep)->de_StartCluster = getushort((dp)->deStartCluster), \ + (dep)->de_FileSize = getulong((dp)->deFileSize)) + +#define DE_EXTERNALIZE(dp, dep) \ + (bcopy((dep)->de_Name, (dp)->deName, 11), \ + (dp)->deAttributes = (dep)->de_Attributes, \ + putushort((dp)->deTime, (dep)->de_Time), \ + putushort((dp)->deDate, (dep)->de_Date), \ + putushort((dp)->deStartCluster, (dep)->de_StartCluster), \ + putulong((dp)->deFileSize, (dep)->de_FileSize)) + +#define de_forw de_chain[0] +#define de_back de_chain[1] + +#ifdef KERNEL + +#define VTODE(vp) ((struct denode *)(vp)->v_data) +#define DETOV(de) ((de)->de_vnode) + +#define DE_UPDAT(dep, t, waitfor) \ + if (dep->de_flag & DE_UPDATE) \ + (void) deupdat(dep, t, waitfor); + +#define DE_TIMES(dep, t) \ + if (dep->de_flag & DE_UPDATE) { \ + (dep)->de_flag |= DE_MODIFIED; \ + unix2dostime(t, &dep->de_Date, &dep->de_Time); \ + (dep)->de_flag &= ~DE_UPDATE; \ + } + +/* + * This overlays the fid sturcture (see mount.h) + */ +struct defid { + u_short defid_len; /* length of structure */ + u_short defid_pad; /* force long alignment */ + + u_long defid_dirclust; /* cluster this dir entry came from */ + u_long defid_dirofs; /* index of entry within the cluster */ + + /* u_long defid_gen; generation number */ +}; + +/* + * Prototypes for MSDOSFS vnode operations + */ +int msdosfs_lookup __P((struct vop_lookup_args *)); +int msdosfs_create __P((struct vop_create_args *)); +int msdosfs_mknod __P((struct vop_mknod_args *)); +int msdosfs_open __P((struct vop_open_args *)); +int msdosfs_close __P((struct vop_close_args *)); +int msdosfs_access __P((struct vop_access_args *)); +int msdosfs_getattr __P((struct vop_getattr_args *)); +int msdosfs_setattr __P((struct vop_setattr_args *)); +int msdosfs_read __P((struct vop_read_args *)); +int msdosfs_write __P((struct vop_write_args *)); +int msdosfs_ioctl __P((struct vop_ioctl_args *)); +int msdosfs_select __P((struct vop_select_args *)); +int msdosfs_mmap __P((struct vop_mmap_args *)); +int msdosfs_fsync __P((struct vop_fsync_args *)); +int msdosfs_seek __P((struct vop_seek_args *)); +int msdosfs_remove __P((struct vop_remove_args *)); +int msdosfs_link __P((struct vop_link_args *)); +int msdosfs_rename __P((struct vop_rename_args *)); +int msdosfs_mkdir __P((struct vop_mkdir_args *)); +int msdosfs_rmdir __P((struct vop_rmdir_args *)); +int msdosfs_symlink __P((struct vop_symlink_args *)); +int msdosfs_readdir __P((struct vop_readdir_args *)); +int msdosfs_readlink __P((struct vop_readlink_args *)); +int msdosfs_abortop __P((struct vop_abortop_args *)); +int msdosfs_inactive __P((struct vop_inactive_args *)); +int msdosfs_reclaim __P((struct vop_reclaim_args *)); +int msdosfs_lock __P((struct vop_lock_args *)); +int msdosfs_unlock __P((struct vop_unlock_args *)); +int msdosfs_bmap __P((struct vop_bmap_args *)); +int msdosfs_strategy __P((struct vop_strategy_args *)); +int msdosfs_print __P((struct vop_print_args *)); +int msdosfs_islocked __P((struct vop_islocked_args *)); +int msdosfs_advlock __P((struct vop_advlock_args *)); +int msdosfs_reallocblks __P((struct vop_reallocblks_args *)); + +/* + * Internal service routine prototypes. + */ +int deget __P((struct msdosfsmount * pmp, u_long dirclust, u_long diroffset, struct direntry * direntptr, struct denode ** depp)); +#endif /* KERNEL */ diff --git a/sys/fs/msdosfs/direntry.h b/sys/fs/msdosfs/direntry.h new file mode 100644 index 0000000..d2ec019 --- /dev/null +++ b/sys/fs/msdosfs/direntry.h @@ -0,0 +1,104 @@ +/* $Id$ */ +/* $NetBSD: direntry.h,v 1.7 1994/08/21 18:43:54 ws Exp $ */ + +/*- + * Copyright (C) 1994 Wolfgang Solfrank. + * Copyright (C) 1994 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 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. + * + * October 1992 + */ + +/* + * Structure of a dos directory entry. + */ +struct direntry { + u_char deName[8]; /* filename, blank filled */ +#define SLOT_EMPTY 0x00 /* slot has never been used */ +#define SLOT_E5 0x05 /* the real value is 0xe5 */ +#define SLOT_DELETED 0xe5 /* file in this slot deleted */ + u_char deExtension[3]; /* extension, blank filled */ + u_char deAttributes; /* file attributes */ +#define ATTR_NORMAL 0x00 /* normal file */ +#define ATTR_READONLY 0x01 /* file is readonly */ +#define ATTR_HIDDEN 0x02 /* file is hidden */ +#define ATTR_SYSTEM 0x04 /* file is a system file */ +#define ATTR_VOLUME 0x08 /* entry is a volume label */ +#define ATTR_DIRECTORY 0x10 /* entry is a directory name */ +#define ATTR_ARCHIVE 0x20 /* file is new or modified */ + u_char deReserved[10]; /* reserved */ + u_char deTime[2]; /* create/last update time */ + u_char deDate[2]; /* create/last update date */ + u_char deStartCluster[2]; /* starting cluster of file */ + u_char deFileSize[4]; /* size of file in bytes */ +}; + +/* + * This is the format of the contents of the deTime field in the direntry + * structure. + * We don't use bitfields because we don't know how compilers for + * arbitrary machines will lay them out. + */ +#define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */ +#define DT_2SECONDS_SHIFT 0 +#define DT_MINUTES_MASK 0x7E0 /* minutes */ +#define DT_MINUTES_SHIFT 5 +#define DT_HOURS_MASK 0xF800 /* hours */ +#define DT_HOURS_SHIFT 11 + +/* + * This is the format of the contents of the deDate field in the direntry + * structure. + */ +#define DD_DAY_MASK 0x1F /* day of month */ +#define DD_DAY_SHIFT 0 +#define DD_MONTH_MASK 0x1E0 /* month */ +#define DD_MONTH_SHIFT 5 +#define DD_YEAR_MASK 0xFE00 /* year - 1980 */ +#define DD_YEAR_SHIFT 9 + +#ifdef KERNEL +void unix2dostime __P((struct timespec * tsp, u_short * ddp, u_short * dtp)); +void dos2unixtime __P((u_short dd, u_short dt, struct timespec * tsp)); +int dos2unixfn __P((u_char dn[11], u_char * un)); +void unix2dosfn __P((u_char * un, u_char dn[11], int unlen)); +#endif /* KERNEL */ diff --git a/sys/fs/msdosfs/fat.h b/sys/fs/msdosfs/fat.h new file mode 100644 index 0000000..3867c87 --- /dev/null +++ b/sys/fs/msdosfs/fat.h @@ -0,0 +1,97 @@ +/* $Id$ */ +/* $NetBSD: fat.h,v 1.4 1994/08/21 18:43:57 ws Exp $ */ + +/*- + * Copyright (C) 1994 Wolfgang Solfrank. + * Copyright (C) 1994 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 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. + * + * October 1992 + */ + +/* + * Some useful cluster numbers. + */ +#define MSDOSFSROOT 0 /* cluster 0 means the root dir */ +#define CLUST_FREE 0 /* cluster 0 also means a free cluster */ +#define MSDOSFSFREE CLUST_FREE +#define CLUST_FIRST 2 /* first legal cluster number */ +#define CLUST_RSRVS 0xfff0 /* start of reserved cluster range */ +#define CLUST_RSRVE 0xfff6 /* end of reserved cluster range */ +#define CLUST_BAD 0xfff7 /* a cluster with a defect */ +#define CLUST_EOFS 0xfff8 /* start of eof cluster range */ +#define CLUST_EOFE 0xffff /* end of eof cluster range */ + +#define FAT12_MASK 0x0fff /* mask for 12 bit cluster numbers */ +#define FAT16_MASK 0xffff /* mask for 16 bit cluster numbers */ + +/* + * Return true if filesystem uses 12 bit fats. Microsoft Programmer's + * Reference says if the maximum cluster number in a filesystem is greater + * than 4086 then we've got a 16 bit fat filesystem. + */ +#define FAT12(pmp) (pmp->pm_maxcluster <= 4086) +#define FAT16(pmp) (pmp->pm_maxcluster > 4086) + +#define MSDOSFSEOF(cn) (((cn) & 0xfff8) == 0xfff8) + +#ifdef KERNEL +/* + * These are the values for the function argument to the function + * fatentry(). + */ +#define FAT_GET 0x0001 /* get a fat entry */ +#define FAT_SET 0x0002 /* set a fat entry */ +#define FAT_GET_AND_SET (FAT_GET | FAT_SET) + +/* + * Flags to extendfile: + */ +#define DE_CLEAR 1 /* Zero out the blocks allocated */ + +int pcbmap __P((struct denode *dep, u_long findcn, daddr_t *bnp, u_long *cnp)); +int clusterfree __P((struct msdosfsmount *pmp, u_long cn, u_long *oldcnp)); +int clusteralloc __P((struct msdosfsmount *pmp, u_long start, u_long count, u_long fillwith, u_long *retcluster, u_long *got)); +int fatentry __P((int function, struct msdosfsmount *pmp, u_long cluster, u_long *oldcontents, u_long newcontents)); +int freeclusterchain __P((struct msdosfsmount *pmp, u_long startchain)); +int extendfile __P((struct denode *dep, u_long count, struct buf **bpp, u_long *ncp, int flags)); +#endif /* KERNEL */ diff --git a/sys/fs/msdosfs/msdosfs_conv.c b/sys/fs/msdosfs/msdosfs_conv.c new file mode 100644 index 0000000..da8ce97 --- /dev/null +++ b/sys/fs/msdosfs/msdosfs_conv.c @@ -0,0 +1,358 @@ +/* $Id$ */ +/* $NetBSD: msdosfs_conv.c,v 1.6.2.1 1994/08/30 02:27:57 cgd Exp $ */ + +/* + * 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. + * + * October 1992 + */ + +/* + * System include files. + */ +#include <sys/param.h> +#include <sys/time.h> +#include <sys/kernel.h> /* defines tz */ + +/* + * MSDOSFS include files. + */ +#include <msdosfs/direntry.h> + +/* + * Days in each month in a regular year. + */ +u_short regyear[] = { + 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 +}; + +/* + * Days in each month in a leap year. + */ +u_short leapyear[] = { + 31, 29, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 +}; + +/* + * Variables used to remember parts of the last time conversion. Maybe we + * can avoid a full conversion. + */ +u_long lasttime; +u_long lastday; +u_short lastddate; +u_short lastdtime; + +/* + * Convert the unix version of time to dos's idea of time to be used in + * file timestamps. The passed in unix time is assumed to be in GMT. + */ +void +unix2dostime(tsp, ddp, dtp) + struct timespec *tsp; + u_short *ddp; + u_short *dtp; +{ + u_long t; + u_long days; + u_long inc; + u_long year; + u_long month; + u_short *months; + + /* + * If the time from the last conversion is the same as now, then + * skip the computations and use the saved result. + */ + t = tsp->ts_sec - (tz.tz_minuteswest * 60) + /* +- daylight savings time correction */ ; + if (lasttime != t) { + lasttime = t; + lastdtime = (((t % 60) >> 1) << DT_2SECONDS_SHIFT) + + (((t / 60) % 60) << DT_MINUTES_SHIFT) + + (((t / 3600) % 24) << DT_HOURS_SHIFT); + + /* + * If the number of days since 1970 is the same as the last + * time we did the computation then skip all this leap year + * and month stuff. + */ + days = t / (24 * 60 * 60); + if (days != lastday) { + lastday = days; + for (year = 1970;; year++) { + inc = year & 0x03 ? 365 : 366; + if (days < inc) + break; + days -= inc; + } + months = year & 0x03 ? regyear : leapyear; + for (month = 0; month < 12; month++) { + if (days < months[month]) + break; + days -= months[month]; + } + lastddate = ((days + 1) << DD_DAY_SHIFT) + + ((month + 1) << DD_MONTH_SHIFT); + /* + * Remember dos's idea of time is relative to 1980. + * unix's is relative to 1970. If somehow we get a + * time before 1980 then don't give totally crazy + * results. + */ + if (year > 1980) + lastddate += (year - 1980) << DD_YEAR_SHIFT; + } + } + *dtp = lastdtime; + *ddp = lastddate; +} + +/* + * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that + * interval there were 8 regular years and 2 leap years. + */ +#define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60)) + +u_short lastdosdate; +u_long lastseconds; + +/* + * Convert from dos' idea of time to unix'. This will probably only be + * called from the stat(), and fstat() system calls and so probably need + * not be too efficient. + */ +void +dos2unixtime(dd, dt, tsp) + u_short dd; + u_short dt; + struct timespec *tsp; +{ + u_long seconds; + u_long m, month; + u_long y, year; + u_long days; + u_short *months; + + seconds = ((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) + + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60 + + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600; + /* + * If the year, month, and day from the last conversion are the + * same then use the saved value. + */ + if (lastdosdate != dd) { + lastdosdate = dd; + days = 0; + year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT; + for (y = 0; y < year; y++) { + days += y & 0x03 ? 365 : 366; + } + months = year & 0x03 ? regyear : leapyear; + /* + * Prevent going from 0 to 0xffffffff in the following + * loop. + */ + month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT; + if (month == 0) { + printf("dos2unixtime(): month value out of range (%d)\n", + month); + month = 1; + } + for (m = 0; m < month - 1; m++) { + days += months[m]; + } + days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1; + lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980; + } + tsp->ts_sec = seconds + lastseconds + (tz.tz_minuteswest * 60) + /* -+ daylight savings time correction */ ; + tsp->ts_nsec = 0; +} + +/* + * Cheezy macros to do case detection and conversion for the ascii + * character set. DOESN'T work for ebcdic. + */ +#define isupper(c) (c >= 'A' && c <= 'Z') +#define islower(c) (c >= 'a' && c <= 'z') +#define toupper(c) (c & ~' ') +#define tolower(c) (c | ' ') + +/* + * DOS filenames are made of 2 parts, the name part and the extension part. + * The name part is 8 characters long and the extension part is 3 + * characters long. They may contain trailing blanks if the name or + * extension are not long enough to fill their respective fields. + */ + +/* + * Convert a DOS filename to a unix filename. And, return the number of + * characters in the resulting unix filename excluding the terminating + * null. + */ +int +dos2unixfn(dn, un) + u_char dn[11]; + u_char *un; +{ + int i; + int ni; + int ei; + int thislong = 0; + u_char c; + u_char *origun = un; + + /* + * Find the last character in the name portion of the dos filename. + */ + for (ni = 7; ni >= 0; ni--) + if (dn[ni] != ' ') + break; + + /* + * Find the last character in the extension portion of the + * filename. + */ + for (ei = 10; ei >= 8; ei--) + if (dn[ei] != ' ') + break; + + /* + * Copy the name portion into the unix filename string. NOTE: DOS + * filenames are usually kept in upper case. To make it more unixy + * we convert all DOS filenames to lower case. Some may like this, + * some may not. + */ + for (i = 0; i <= ni; i++) { + c = dn[i]; + *un++ = isupper(c) ? tolower(c) : c; + thislong++; + } + + /* + * Now, if there is an extension then put in a period and copy in + * the extension. + */ + if (ei >= 8) { + *un++ = '.'; + thislong++; + for (i = 8; i <= ei; i++) { + c = dn[i]; + *un++ = isupper(c) ? tolower(c) : c; + thislong++; + } + } + *un++ = 0; + + /* + * If first char of the filename is SLOT_E5 (0x05), then the real + * first char of the filename should be 0xe5. But, they couldn't + * just have a 0xe5 mean 0xe5 because that is used to mean a freed + * directory slot. Another dos quirk. + */ + if (*origun == SLOT_E5) + *origun = 0xe5; + + return thislong; +} + +/* + * Convert a unix filename to a DOS filename. This function does not ensure + * that valid characters for a dos filename are supplied. + */ +void +unix2dosfn(un, dn, unlen) + u_char *un; + u_char dn[11]; + int unlen; +{ + int i; + u_char c; + + /* + * Fill the dos filename string with blanks. These are DOS's pad + * characters. + */ + for (i = 0; i <= 10; i++) + dn[i] = ' '; + + /* + * The filenames "." and ".." are handled specially, since they + * don't follow dos filename rules. + */ + if (un[0] == '.' && unlen == 1) { + dn[0] = '.'; + return; + } + if (un[0] == '.' && un[1] == '.' && unlen == 2) { + dn[0] = '.'; + dn[1] = '.'; + return; + } + + /* + * Copy the unix filename into the dos filename string upto the end + * of string, a '.', or 8 characters. Whichever happens first stops + * us. This forms the name portion of the dos filename. Fold to + * upper case. + */ + for (i = 0; i <= 7 && unlen && (c = *un) && c != '.'; i++) { + dn[i] = islower(c) ? toupper(c) : c; + un++; + unlen--; + } + + /* + * If the first char of the filename is 0xe5, then translate it to + * 0x05. This is because 0xe5 is the marker for a deleted + * directory slot. I guess this means you can't have filenames + * that start with 0x05. I suppose we should check for this and + * doing something about it. + */ + if (dn[0] == SLOT_DELETED) + dn[0] = SLOT_E5; + + /* + * Strip any further characters up to a '.' or the end of the + * string. + */ + while (unlen && (c = *un)) { + un++; + unlen--; + /* Make sure we've skipped over the dot before stopping. */ + if (c == '.') + break; + } + + /* + * Copy in the extension part of the name, if any. Force to upper + * case. Note that the extension is allowed to contain '.'s. + * Filenames in this form are probably inaccessable under dos. + */ + for (i = 8; i <= 10 && unlen && (c = *un); i++) { + dn[i] = islower(c) ? toupper(c) : c; + un++; + unlen--; + } +} + +/* + * Get rid of these macros before someone discovers we are using such + * hideous things. + */ +#undef isupper +#undef islower +#undef toupper +#undef tolower diff --git a/sys/fs/msdosfs/msdosfs_denode.c b/sys/fs/msdosfs/msdosfs_denode.c new file mode 100644 index 0000000..6812863 --- /dev/null +++ b/sys/fs/msdosfs/msdosfs_denode.c @@ -0,0 +1,689 @@ +/* $Id$ */ +/* $NetBSD: msdosfs_denode.c,v 1.9 1994/08/21 18:44:00 ws Exp $ */ + +/*- + * Copyright (C) 1994 Wolfgang Solfrank. + * Copyright (C) 1994 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 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. + * + * October 1992 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mount.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/buf.h> +#include <sys/vnode.h> +#include <sys/kernel.h> /* defines "time" */ + +#include <msdosfs/bpb.h> +#include <msdosfs/msdosfsmount.h> +#include <msdosfs/direntry.h> +#include <msdosfs/denode.h> +#include <msdosfs/fat.h> + +struct denode **dehashtbl; +u_long dehash; /* size of hash table - 1 */ +#define DEHASH(dev, deno) (((dev) + (deno)) & dehash) + +int msdosfs_init() +{ + dehashtbl = hashinit(desiredvnodes/2, M_MSDOSFSMNT, &dehash); + return 0; +} + +static struct denode * +msdosfs_hashget(dev, dirclust, diroff) + dev_t dev; + u_long dirclust; + u_long diroff; +{ + struct denode *dep; + + for (;;) + for (dep = dehashtbl[DEHASH(dev, dirclust + diroff)];; + dep = dep->de_next) { + if (dep == NULL) + return NULL; + if (dirclust != dep->de_dirclust + || diroff != dep->de_diroffset + || dev != dep->de_dev + || dep->de_refcnt == 0) + continue; + if (dep->de_flag & DE_LOCKED) { + dep->de_flag |= DE_WANTED; + sleep((caddr_t)dep, PINOD); + break; + } + if (!vget(DETOV(dep), 1)) + return dep; + break; + } + /* NOTREACHED */ +} + +static void +msdosfs_hashins(dep) + struct denode *dep; +{ + struct denode **depp, *deq; + + depp = &dehashtbl[DEHASH(dep->de_dev, dep->de_dirclust + dep->de_diroffset)]; + if (deq = *depp) + deq->de_prev = &dep->de_next; + dep->de_next = deq; + dep->de_prev = depp; + *depp = dep; + if (dep->de_flag & DE_LOCKED) + panic("msdosfs_hashins: already locked"); + if (curproc) + dep->de_lockholder = curproc->p_pid; + else + dep->de_lockholder = -1; + dep->de_flag |= DE_LOCKED; +} + +static void +msdosfs_hashrem(dep) + struct denode *dep; +{ + struct denode *deq; + if (deq = dep->de_next) + deq->de_prev = dep->de_prev; + *dep->de_prev = deq; +#ifdef DIAGNOSTIC + dep->de_next = NULL; + dep->de_prev = NULL; +#endif +} + +/* + * If deget() succeeds it returns with the gotten denode locked(). + * + * pmp - address of msdosfsmount structure of the filesystem containing + * the denode of interest. The pm_dev field and the address of + * the msdosfsmount structure are used. + * dirclust - which cluster bp contains, if dirclust is 0 (root directory) + * diroffset is relative to the beginning of the root directory, + * otherwise it is cluster relative. + * diroffset - offset past begin of cluster of denode we want + * direntptr - address of the direntry structure of interest. If direntptr is + * NULL, the block is read if necessary. + * depp - returns the address of the gotten denode. + */ +int +deget(pmp, dirclust, diroffset, direntptr, depp) + struct msdosfsmount *pmp; /* so we know the maj/min number */ + u_long dirclust; /* cluster this dir entry came from */ + u_long diroffset; /* index of entry within the cluster */ + struct direntry *direntptr; + struct denode **depp; /* returns the addr of the gotten denode */ +{ + int error; + dev_t dev = pmp->pm_dev; + struct mount *mntp = pmp->pm_mountp; + extern int (**msdosfs_vnodeop_p)(); + struct denode *ldep; + struct vnode *nvp; + struct buf *bp; + +#ifdef MSDOSFS_DEBUG + printf("deget(pmp %08x, dirclust %d, diroffset %x, direntptr %x, depp %08x)\n", + pmp, dirclust, diroffset, direntptr, depp); +#endif + + /* + * If dir entry is given and refers to a directory, convert to + * canonical form + */ + if (direntptr && (direntptr->deAttributes & ATTR_DIRECTORY)) { + dirclust = getushort(direntptr->deStartCluster); + if (dirclust == MSDOSFSROOT) + diroffset = MSDOSFSROOT_OFS; + else + diroffset = 0; + } + + /* + * See if the denode is in the denode cache. Use the location of + * the directory entry to compute the hash value. For subdir use + * address of "." entry. for root dir use cluster MSDOSFSROOT, + * offset MSDOSFSROOT_OFS + * + * NOTE: The check for de_refcnt > 0 below insures the denode being + * examined does not represent an unlinked but still open file. + * These files are not to be accessible even when the directory + * entry that represented the file happens to be reused while the + * deleted file is still open. + */ + if (ldep = msdosfs_hashget(dev, dirclust, diroffset)) { + *depp = ldep; + return 0; + } + + + /* + * Directory entry was not in cache, have to create a vnode and + * copy it from the passed disk buffer. + */ + /* getnewvnode() does a VREF() on the vnode */ + if (error = getnewvnode(VT_MSDOSFS, mntp, msdosfs_vnodeop_p, &nvp)) { + *depp = 0; + return error; + } + MALLOC(ldep, struct denode *, sizeof(struct denode), M_MSDOSFSNODE, M_WAITOK); + bzero((caddr_t)ldep, sizeof *ldep); + nvp->v_data = ldep; + ldep->de_vnode = nvp; + ldep->de_flag = 0; + ldep->de_devvp = 0; + ldep->de_lockf = 0; + ldep->de_dev = dev; + ldep->de_dirclust = dirclust; + ldep->de_diroffset = diroffset; + fc_purge(ldep, 0); /* init the fat cache for this denode */ + + /* + * Insert the denode into the hash queue and lock the denode so it + * can't be accessed until we've read it in and have done what we + * need to it. + */ + msdosfs_hashins(ldep); + + /* + * Copy the directory entry into the denode area of the vnode. + */ + if (dirclust == MSDOSFSROOT && diroffset == MSDOSFSROOT_OFS) { + /* + * Directory entry for the root directory. There isn't one, + * so we manufacture one. We should probably rummage + * through the root directory and find a label entry (if it + * exists), and then use the time and date from that entry + * as the time and date for the root denode. + */ + ldep->de_Attributes = ATTR_DIRECTORY; + ldep->de_StartCluster = MSDOSFSROOT; + ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec; + /* + * fill in time and date so that dos2unixtime() doesn't + * spit up when called from msdosfs_getattr() with root + * denode + */ + ldep->de_Time = 0x0000; /* 00:00:00 */ + ldep->de_Date = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT) + | (1 << DD_DAY_SHIFT); + /* Jan 1, 1980 */ + /* leave the other fields as garbage */ + } else { + bp = NULL; + if (!direntptr) { + error = readep(pmp, dirclust, diroffset, &bp, + &direntptr); + if (error) + return error; + } + DE_INTERNALIZE(ldep, direntptr); + if (bp) + brelse(bp); + } + + /* + * Fill in a few fields of the vnode and finish filling in the + * denode. Then return the address of the found denode. + */ + ldep->de_pmp = pmp; + ldep->de_devvp = pmp->pm_devvp; + ldep->de_refcnt = 1; + if (ldep->de_Attributes & ATTR_DIRECTORY) { + /* + * Since DOS directory entries that describe directories + * have 0 in the filesize field, we take this opportunity + * to find out the length of the directory and plug it into + * the denode structure. + */ + u_long size; + + nvp->v_type = VDIR; + if (ldep->de_StartCluster == MSDOSFSROOT) + nvp->v_flag |= VROOT; + else { + error = pcbmap(ldep, 0xffff, 0, &size); + if (error == E2BIG) { + ldep->de_FileSize = size << pmp->pm_cnshift; + error = 0; + } else + printf("deget(): pcbmap returned %d\n", error); + } + } else + nvp->v_type = VREG; + VREF(ldep->de_devvp); + *depp = ldep; + return 0; +} + +int +deupdat(dep, tp, waitfor) + struct denode *dep; + struct timespec *tp; + int waitfor; +{ + int error; + daddr_t bn; + int diro; + struct buf *bp; + struct direntry *dirp; + struct msdosfsmount *pmp = dep->de_pmp; + struct timespec ts; + struct vnode *vp = DETOV(dep); + +#ifdef MSDOSFS_DEBUG + printf("deupdat(): dep %08x\n", dep); +#endif + + /* + * If the update bit is off, or this denode is from a readonly + * filesystem, or this denode is for a directory, or the denode + * represents an open but unlinked file then don't do anything. DOS + * directory entries that describe a directory do not ever get + * updated. This is the way dos treats them. + */ + if ((dep->de_flag & DE_UPDATE) == 0 || + vp->v_mount->mnt_flag & MNT_RDONLY || + dep->de_Attributes & ATTR_DIRECTORY || + dep->de_refcnt <= 0) + return 0; + + /* + * Read in the cluster containing the directory entry we want to + * update. + */ + if (error = readde(dep, &bp, &dirp)) + return error; + + /* + * Put the passed in time into the directory entry. + */ + TIMEVAL_TO_TIMESPEC(&time, &ts); + unix2dostime(&ts, &dep->de_Date, &dep->de_Time); + dep->de_flag &= ~DE_UPDATE; + + /* + * Copy the directory entry out of the denode into the cluster it + * came from. + */ + DE_EXTERNALIZE(dirp, dep); + + /* + * Write the cluster back to disk. If they asked for us to wait + * for the write to complete, then use bwrite() otherwise use + * bdwrite(). + */ + error = 0; /* note that error is 0 from above, but ... */ + if (waitfor) + error = bwrite(bp); + else + bdwrite(bp); + return error; +} + +/* + * Truncate the file described by dep to the length specified by length. + */ +int +detrunc(dep, length, flags, cred, p) + struct denode *dep; + u_long length; + int flags; + struct ucred *cred; + struct proc *p; +{ + int error; + int allerror; + int vflags; + u_long eofentry; + u_long chaintofree; + daddr_t bn; + int boff; + int isadir = dep->de_Attributes & ATTR_DIRECTORY; + struct buf *bp; + struct msdosfsmount *pmp = dep->de_pmp; + +#ifdef MSDOSFS_DEBUG + printf("detrunc(): file %s, length %d, flags %d\n", dep->de_Name, length, flags); +#endif + + /* + * Disallow attempts to truncate the root directory since it is of + * fixed size. That's just the way dos filesystems are. We use + * the VROOT bit in the vnode because checking for the directory + * bit and a startcluster of 0 in the denode is not adequate to + * recognize the root directory at this point in a file or + * directory's life. + */ + if (DETOV(dep)->v_flag & VROOT) { + printf("detrunc(): can't truncate root directory, clust %d, offset %d\n", + dep->de_dirclust, dep->de_diroffset); + return EINVAL; + } + + vnode_pager_setsize(DETOV(dep), length); + + if (dep->de_FileSize < length) + return deextend(dep, length, cred); + + /* + * If the desired length is 0 then remember the starting cluster of + * the file and set the StartCluster field in the directory entry + * to 0. If the desired length is not zero, then get the number of + * the last cluster in the shortened file. Then get the number of + * the first cluster in the part of the file that is to be freed. + * Then set the next cluster pointer in the last cluster of the + * file to CLUST_EOFE. + */ + if (length == 0) { + chaintofree = dep->de_StartCluster; + dep->de_StartCluster = 0; + eofentry = ~0; + } else { + if (error = pcbmap(dep, de_clcount(pmp, length) - 1, 0, &eofentry)) { +#ifdef MSDOSFS_DEBUG + printf("detrunc(): pcbmap fails %d\n", error); +#endif + return error; + } + } + + fc_purge(dep, (length + pmp->pm_crbomask) >> pmp->pm_cnshift); + + /* + * If the new length is not a multiple of the cluster size then we + * must zero the tail end of the new last cluster in case it + * becomes part of the file again because of a seek. + */ + if ((boff = length & pmp->pm_crbomask) != 0) { + /* + * should read from file vnode or filesystem vnode + * depending on if file or dir + */ + if (isadir) { + bn = cntobn(pmp, eofentry); + error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, + NOCRED, &bp); + } else { + bn = de_blk(pmp, length); + error = bread(DETOV(dep), bn, pmp->pm_bpcluster, + NOCRED, &bp); + } + if (error) { +#ifdef MSDOSFS_DEBUG + printf("detrunc(): bread fails %d\n", error); +#endif + return error; + } + vnode_pager_uncache(DETOV(dep)); /* what's this for? */ + /* + * is this the right place for it? + */ + bzero(bp->b_data + boff, pmp->pm_bpcluster - boff); + if (flags & IO_SYNC) + bwrite(bp); + else + bdwrite(bp); + } + + /* + * Write out the updated directory entry. Even if the update fails + * we free the trailing clusters. + */ + dep->de_FileSize = length; + dep->de_flag |= DE_UPDATE; + vflags = (length > 0 ? V_SAVE : 0) | V_SAVEMETA; + vinvalbuf(DETOV(dep), vflags, cred, p, 0, 0); + allerror = deupdat(dep, &time, 1); +#ifdef MSDOSFS_DEBUG + printf("detrunc(): allerror %d, eofentry %d\n", + allerror, eofentry); +#endif + + /* + * If we need to break the cluster chain for the file then do it + * now. + */ + if (eofentry != ~0) { + error = fatentry(FAT_GET_AND_SET, pmp, eofentry, + &chaintofree, CLUST_EOFE); + if (error) { +#ifdef MSDOSFS_DEBUG + printf("detrunc(): fatentry errors %d\n", error); +#endif + return error; + } + fc_setcache(dep, FC_LASTFC, (length - 1) >> pmp->pm_cnshift, + eofentry); + } + + /* + * Now free the clusters removed from the file because of the + * truncation. + */ + if (chaintofree != 0 && !MSDOSFSEOF(chaintofree)) + freeclusterchain(pmp, chaintofree); + + return allerror; +} + +/* + * Extend the file described by dep to length specified by length. + */ +int +deextend(dep, length, cred) + struct denode *dep; + off_t length; + struct ucred *cred; +{ + struct msdosfsmount *pmp = dep->de_pmp; + u_long count; + int error; + + /* + * The root of a DOS filesystem cannot be extended. + */ + if (DETOV(dep)->v_flag & VROOT) + return EINVAL; + + /* + * Directories can only be extended by the superuser. + * Is this really important? + */ + if (dep->de_Attributes & ATTR_DIRECTORY) { + if (error = suser(cred, NULL)) + return error; + } + + if (length <= dep->de_FileSize) + panic("deextend: file too large"); + + /* + * Compute the number of clusters to allocate. + */ + count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize); + if (count > 0) { + if (count > pmp->pm_freeclustercount) + return ENOSPC; + if (error = extendfile(dep, count, NULL, NULL, DE_CLEAR)) { + /* truncate the added clusters away again */ + (void) detrunc(dep, dep->de_FileSize, 0, cred, NULL); + return error; + } + } + + dep->de_flag |= DE_UPDATE; + dep->de_FileSize = length; + return deupdat(dep, &time, 1); +} + +/* + * Move a denode to its correct hash queue after the file it represents has + * been moved to a new directory. + */ +int reinsert(dep) + struct denode *dep; +{ + union dehead *deh; + + /* + * Fix up the denode cache. If the denode is for a directory, + * there is nothing to do since the hash is based on the starting + * cluster of the directory file and that hasn't changed. If for a + * file the hash is based on the location of the directory entry, + * so we must remove it from the cache and re-enter it with the + * hash based on the new location of the directory entry. + */ + if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) { + msdosfs_hashrem(dep); + msdosfs_hashins(dep); + } + return 0; +} + +int +msdosfs_reclaim(ap) + struct vop_reclaim_args /* { + struct vnode *a_vp; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct denode *dep = VTODE(vp); + int i; + extern int prtactive; + +#ifdef MSDOSFS_DEBUG + printf("msdosfs_reclaim(): dep %08x, file %s, refcnt %d\n", + dep, dep->de_Name, dep->de_refcnt); +#endif + + if (prtactive && vp->v_usecount != 0) + vprint("msdosfs_reclaim(): pushing active", vp); + + /* + * Remove the denode from the denode hash chain we are in. + */ + msdosfs_hashrem(dep); + + cache_purge(vp); + /* + * Indicate that one less file on the filesystem is open. + */ + if (dep->de_devvp) { + vrele(dep->de_devvp); + dep->de_devvp = 0; + } + + dep->de_flag = 0; + + FREE(dep, M_MSDOSFSNODE); + vp->v_data = NULL; + + return 0; +} + +int +msdosfs_inactive(ap) + struct vop_inactive_args /* { + struct vnode *a_vp; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct denode *dep = VTODE(vp); + int error = 0; + extern int prtactive; + +#ifdef MSDOSFS_DEBUG + printf("msdosfs_inactive(): dep %08x, de_Name[0] %x\n", dep, dep->de_Name[0]); +#endif + + if (prtactive && vp->v_usecount != 0) + vprint("msdosfs_inactive(): pushing active", vp); + + /* + * Get rid of denodes related to stale file handles. Hmmm, what + * does this really do? + */ + if (dep->de_Name[0] == SLOT_DELETED) { + if ((vp->v_flag & VXLOCK) == 0) + vgone(vp); + return 0; + } + + /* + * If the file has been deleted and it is on a read/write + * filesystem, then truncate the file, and mark the directory slot + * as empty. (This may not be necessary for the dos filesystem.) + */ +#ifdef MSDOSFS_DEBUG + printf("msdosfs_inactive(): dep %08x, refcnt %d, mntflag %x, MNT_RDONLY %x\n", + dep, dep->de_refcnt, vp->v_mount->mnt_flag, MNT_RDONLY); +#endif + VOP_LOCK(vp); + if (dep->de_refcnt <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { + error = detrunc(dep, (u_long) 0, 0, NOCRED, NULL); + dep->de_flag |= DE_UPDATE; + dep->de_Name[0] = SLOT_DELETED; + } + DE_UPDAT(dep, &time, 0); + VOP_UNLOCK(vp); + dep->de_flag = 0; + + /* + * If we are done with the denode, then reclaim it so that it can + * be reused now. + */ +#ifdef MSDOSFS_DEBUG + printf("msdosfs_inactive(): v_usecount %d, de_Name[0] %x\n", vp->v_usecount, + dep->de_Name[0]); +#endif + if (vp->v_usecount == 0 && dep->de_Name[0] == SLOT_DELETED) + vgone(vp); + return error; +} diff --git a/sys/fs/msdosfs/msdosfs_fat.c b/sys/fs/msdosfs/msdosfs_fat.c new file mode 100644 index 0000000..9a2f117 --- /dev/null +++ b/sys/fs/msdosfs/msdosfs_fat.c @@ -0,0 +1,984 @@ +/* $Id$ */ +/* $NetBSD: msdosfs_fat.c,v 1.12 1994/08/21 18:44:04 ws Exp $ */ + +/*- + * Copyright (C) 1994 Wolfgang Solfrank. + * Copyright (C) 1994 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 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. + * + * October 1992 + */ + +/* + * kernel include files. + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/file.h> +#include <sys/namei.h> +#include <sys/mount.h> /* to define statfs structure */ +#include <sys/vnode.h> /* to define vattr structure */ +#include <sys/errno.h> + +/* + * msdosfs include files. + */ +#include <msdosfs/bpb.h> +#include <msdosfs/msdosfsmount.h> +#include <msdosfs/direntry.h> +#include <msdosfs/denode.h> +#include <msdosfs/fat.h> + +/* + * Fat cache stats. + */ +int fc_fileextends; /* # of file extends */ +int fc_lfcempty; /* # of time last file cluster cache entry + * was empty */ +int fc_bmapcalls; /* # of times pcbmap was called */ + +#define LMMAX 20 +int fc_lmdistance[LMMAX]; /* counters for how far off the last + * cluster mapped entry was. */ +int fc_largedistance; /* off by more than LMMAX */ + +/* Byte offset in FAT on filesystem pmp, cluster cn */ +#define FATOFS(pmp, cn) (FAT12(pmp) ? (cn) * 3 / 2 : (cn) * 2) + +void fc_lookup(struct denode *dep, u_long findcn, + u_long *frcnp, u_long *fsrcnp); +void fc_purge(struct denode *dep, u_int frcn); + +static void +fatblock(pmp, ofs, bnp, sizep, bop) + struct msdosfsmount *pmp; + u_long ofs; + u_long *bnp; + u_long *sizep; + u_long *bop; +{ + u_long bn, size; + + bn = ofs / pmp->pm_fatblocksize * pmp->pm_fatblocksec; + size = min(pmp->pm_fatblocksec, pmp->pm_FATsecs - bn) + * pmp->pm_BytesPerSec; + bn += pmp->pm_fatblk; + if (bnp) + *bnp = bn; + if (sizep) + *sizep = size; + if (bop) + *bop = ofs % pmp->pm_fatblocksize; +} + +/* + * Map the logical cluster number of a file into a physical disk sector + * that is filesystem relative. + * + * dep - address of denode representing the file of interest + * findcn - file relative cluster whose filesystem relative cluster number + * and/or block number are/is to be found + * bnp - address of where to place the file system relative block number. + * If this pointer is null then don't return this quantity. + * cnp - address of where to place the file system relative cluster number. + * If this pointer is null then don't return this quantity. + * + * NOTE: Either bnp or cnp must be non-null. + * This function has one side effect. If the requested file relative cluster + * is beyond the end of file, then the actual number of clusters in the file + * is returned in *cnp. This is useful for determining how long a directory is. + * If cnp is null, nothing is returned. + */ +int +pcbmap(dep, findcn, bnp, cnp) + struct denode *dep; + u_long findcn; /* file relative cluster to get */ + daddr_t *bnp; /* returned filesys relative blk number */ + u_long *cnp; /* returned cluster number */ +{ + int error; + u_long i; + u_long cn; + u_long prevcn; + u_long byteoffset; + u_long bn; + u_long bo; + struct buf *bp = NULL; + u_long bp_bn = -1; + struct msdosfsmount *pmp = dep->de_pmp; + u_long bsize; + int fat12 = FAT12(pmp); /* 12 bit fat */ + + fc_bmapcalls++; + + /* + * If they don't give us someplace to return a value then don't + * bother doing anything. + */ + if (bnp == NULL && cnp == NULL) + return 0; + + cn = dep->de_StartCluster; + /* + * The "file" that makes up the root directory is contiguous, + * permanently allocated, of fixed size, and is not made up of + * clusters. If the cluster number is beyond the end of the root + * directory, then return the number of clusters in the file. + */ + if (cn == MSDOSFSROOT) { + if (dep->de_Attributes & ATTR_DIRECTORY) { + if (findcn * pmp->pm_SectPerClust > pmp->pm_rootdirsize) { + if (cnp) + *cnp = pmp->pm_rootdirsize / pmp->pm_SectPerClust; + return E2BIG; + } + if (bnp) + *bnp = pmp->pm_rootdirblk + (findcn * pmp->pm_SectPerClust); + if (cnp) + *cnp = MSDOSFSROOT; + return 0; + } else { /* just an empty file */ + if (cnp) + *cnp = 0; + return E2BIG; + } + } + + /* + * Rummage around in the fat cache, maybe we can avoid tromping + * thru every fat entry for the file. And, keep track of how far + * off the cache was from where we wanted to be. + */ + i = 0; + fc_lookup(dep, findcn, &i, &cn); + if ((bn = findcn - i) >= LMMAX) + fc_largedistance++; + else + fc_lmdistance[bn]++; + + /* + * Handle all other files or directories the normal way. + */ + prevcn = 0; + for (; i < findcn; i++) { + if (MSDOSFSEOF(cn)) + goto hiteof; + byteoffset = FATOFS(pmp, cn); + fatblock(pmp, byteoffset, &bn, &bsize, &bo); + if (bn != bp_bn) { + if (bp) + brelse(bp); + error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); + if (error) + return error; + bp_bn = bn; + } + prevcn = cn; + cn = getushort(&bp->b_data[bo]); + if (fat12) { + if (prevcn & 1) + cn >>= 4; + cn &= 0x0fff; + /* + * Force the special cluster numbers in the range + * 0x0ff0-0x0fff to be the same as for 16 bit + * cluster numbers to let the rest of msdosfs think + * it is always dealing with 16 bit fats. + */ + if ((cn & 0x0ff0) == 0x0ff0) + cn |= 0xf000; + } + } + + if (!MSDOSFSEOF(cn)) { + if (bp) + brelse(bp); + if (bnp) + *bnp = cntobn(pmp, cn); + if (cnp) + *cnp = cn; + fc_setcache(dep, FC_LASTMAP, i, cn); + return 0; + } + +hiteof:; + if (cnp) + *cnp = i; + if (bp) + brelse(bp); + /* update last file cluster entry in the fat cache */ + fc_setcache(dep, FC_LASTFC, i - 1, prevcn); + return E2BIG; +} + +/* + * Find the closest entry in the fat cache to the cluster we are looking + * for. + */ +void fc_lookup(dep, findcn, frcnp, fsrcnp) + struct denode *dep; + u_long findcn; + u_long *frcnp; + u_long *fsrcnp; +{ + int i; + u_long cn; + struct fatcache *closest = 0; + + for (i = 0; i < FC_SIZE; i++) { + cn = dep->de_fc[i].fc_frcn; + if (cn != FCE_EMPTY && cn <= findcn) { + if (closest == 0 || cn > closest->fc_frcn) + closest = &dep->de_fc[i]; + } + } + if (closest) { + *frcnp = closest->fc_frcn; + *fsrcnp = closest->fc_fsrcn; + } +} + +/* + * Purge the fat cache in denode dep of all entries relating to file + * relative cluster frcn and beyond. + */ +void fc_purge(dep, frcn) + struct denode *dep; + u_int frcn; +{ + int i; + struct fatcache *fcp; + + fcp = dep->de_fc; + for (i = 0; i < FC_SIZE; i++, fcp++) { + if (fcp->fc_frcn >= frcn) + fcp->fc_frcn = FCE_EMPTY; + } +} + +/* + * Update all copies of the fat. The first copy is updated last. + * + * pmp - msdosfsmount structure for filesystem to update + * bp - addr of modified fat block + * fatbn - block number relative to begin of filesystem of the modified fat block. + */ +void +updatefats(pmp, bp, fatbn) + struct msdosfsmount *pmp; + struct buf *bp; + u_long fatbn; +{ + int i; + struct buf *bpn; + +#ifdef MSDOSFS_DEBUG + printf("updatefats(pmp %08x, bp %08x, fatbn %d)\n", + pmp, bp, fatbn); +#endif + + /* + * Now copy the block(s) of the modified fat to the other copies of + * the fat and write them out. This is faster than reading in the + * other fats and then writing them back out. This could tie up + * the fat for quite a while. Preventing others from accessing it. + * To prevent us from going after the fat quite so much we use + * delayed writes, unless they specfied "synchronous" when the + * filesystem was mounted. If synch is asked for then use + * bwrite()'s and really slow things down. + */ + for (i = 1; i < pmp->pm_FATs; i++) { + fatbn += pmp->pm_FATsecs; + /* getblk() never fails */ + bpn = getblk(pmp->pm_devvp, fatbn, bp->b_bcount, 0, 0); + bcopy(bp->b_data, bpn->b_data, bp->b_bcount); + if (pmp->pm_waitonfat) + bwrite(bpn); + else + bdwrite(bpn); + } + /* + * Write out the first fat last. + */ + if (pmp->pm_waitonfat) + bwrite(bp); + else + bdwrite(bp); +} + +/* + * Updating entries in 12 bit fats is a pain in the butt. + * + * The following picture shows where nibbles go when moving from a 12 bit + * cluster number into the appropriate bytes in the FAT. + * + * byte m byte m+1 byte m+2 + * +----+----+ +----+----+ +----+----+ + * | 0 1 | | 2 3 | | 4 5 | FAT bytes + * +----+----+ +----+----+ +----+----+ + * + * +----+----+----+ +----+----+----+ + * | 3 0 1 | | 4 5 2 | + * +----+----+----+ +----+----+----+ + * cluster n cluster n+1 + * + * Where n is even. m = n + (n >> 2) + * + */ +extern __inline void +usemap_alloc(pmp, cn) + struct msdosfsmount *pmp; + u_long cn; +{ + pmp->pm_inusemap[cn / N_INUSEBITS] + |= 1 << (cn % N_INUSEBITS); + pmp->pm_freeclustercount--; +} + +extern __inline void +usemap_free(pmp, cn) + struct msdosfsmount *pmp; + u_long cn; +{ + pmp->pm_freeclustercount++; + pmp->pm_inusemap[cn / N_INUSEBITS] &= ~(1 << (cn % N_INUSEBITS)); +} + +int +clusterfree(pmp, cluster, oldcnp) + struct msdosfsmount *pmp; + u_long cluster; + u_long *oldcnp; +{ + int error; + u_long oldcn; + + error = fatentry(FAT_GET_AND_SET, pmp, cluster, &oldcn, MSDOSFSFREE); + if (error == 0) { + /* + * If the cluster was successfully marked free, then update + * the count of free clusters, and turn off the "allocated" + * bit in the "in use" cluster bit map. + */ + usemap_free(pmp, cluster); + if (oldcnp) + *oldcnp = oldcn; + } + return error; +} + +/* + * Get or Set or 'Get and Set' the cluster'th entry in the fat. + * + * function - whether to get or set a fat entry + * pmp - address of the msdosfsmount structure for the filesystem + * whose fat is to be manipulated. + * cn - which cluster is of interest + * oldcontents - address of a word that is to receive the contents of the + * cluster'th entry if this is a get function + * newcontents - the new value to be written into the cluster'th element of + * the fat if this is a set function. + * + * This function can also be used to free a cluster by setting the fat entry + * for a cluster to 0. + * + * All copies of the fat are updated if this is a set function. NOTE: If + * fatentry() marks a cluster as free it does not update the inusemap in + * the msdosfsmount structure. This is left to the caller. + */ +int +fatentry(function, pmp, cn, oldcontents, newcontents) + int function; + struct msdosfsmount *pmp; + u_long cn; + u_long *oldcontents; + u_long newcontents; +{ + int error; + u_long readcn; + u_long bn, bo, bsize, byteoffset; + struct buf *bp; + + /* + * printf("fatentry(func %d, pmp %08x, clust %d, oldcon %08x, newcon %d)\n", + * function, pmp, cluster, oldcontents, newcontents); + */ + +#ifdef DIAGNOSTIC + /* + * Be sure they asked us to do something. + */ + if ((function & (FAT_SET | FAT_GET)) == 0) { + printf("fatentry(): function code doesn't specify get or set\n"); + return EINVAL; + } + + /* + * If they asked us to return a cluster number but didn't tell us + * where to put it, give them an error. + */ + if ((function & FAT_GET) && oldcontents == NULL) { + printf("fatentry(): get function with no place to put result\n"); + return EINVAL; + } +#endif + + /* + * Be sure the requested cluster is in the filesystem. + */ + if (cn < CLUST_FIRST || cn > pmp->pm_maxcluster) + return EINVAL; + + byteoffset = FATOFS(pmp, cn); + fatblock(pmp, byteoffset, &bn, &bsize, &bo); + if (error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp)) + return error; + + if (function & FAT_GET) { + readcn = getushort(&bp->b_data[bo]); + if (FAT12(pmp)) { + if (cn & 1) + readcn >>= 4; + readcn &= 0x0fff; + /* map certain 12 bit fat entries to 16 bit */ + if ((readcn & 0x0ff0) == 0x0ff0) + readcn |= 0xf000; + } + *oldcontents = readcn; + } + if (function & FAT_SET) { + if (FAT12(pmp)) { + readcn = getushort(&bp->b_data[bo]); + if (cn & 1) { + readcn &= 0x000f; + readcn |= newcontents << 4; + } else { + readcn &= 0xf000; + readcn |= newcontents & 0xfff; + } + putushort(&bp->b_data[bo], readcn); + } else + putushort(&bp->b_data[bo], newcontents); + updatefats(pmp, bp, bn); + bp = NULL; + pmp->pm_fmod = 1; + } + if (bp) + brelse(bp); + return 0; +} + +/* + * Update a contiguous cluster chain + * + * pmp - mount point + * start - first cluster of chain + * count - number of clusters in chain + * fillwith - what to write into fat entry of last cluster + */ +static int +fatchain(pmp, start, count, fillwith) + struct msdosfsmount *pmp; + u_long start; + u_long count; + u_long fillwith; +{ + int error; + u_long bn, bo, bsize, byteoffset, readcn, newc; + struct buf *bp; + +#ifdef MSDOSFS_DEBUG + printf("fatchain(pmp %08x, start %d, count %d, fillwith %d)\n", + pmp, start, count, fillwith); +#endif + /* + * Be sure the clusters are in the filesystem. + */ + if (start < CLUST_FIRST || start + count - 1 > pmp->pm_maxcluster) + return EINVAL; + + while (count > 0) { + byteoffset = FATOFS(pmp, start); + fatblock(pmp, byteoffset, &bn, &bsize, &bo); + if (error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp)) + return error; + while (count > 0) { + start++; + newc = --count > 0 ? start : fillwith; + if (FAT12(pmp)) { + readcn = getushort(&bp->b_data[bo]); + if (start & 1) { + readcn &= 0xf000; + readcn |= newc & 0xfff; + } else { + readcn &= 0x000f; + readcn |= newc << 4; + } + putushort(&bp->b_data[bo], readcn); + bo++; + if (!(start & 1)) + bo++; + } else { + putushort(&bp->b_data[bo], newc); + bo += 2; + } + if (bo >= bsize) + break; + } + updatefats(pmp, bp, bn); + } + pmp->pm_fmod = 1; + return 0; +} + +/* + * Check the length of a free cluster chain starting at start. + * + * pmp - mount point + * start - start of chain + * count - maximum interesting length + */ +int +chainlength(pmp, start, count) + struct msdosfsmount *pmp; + u_long start; + u_long count; +{ + u_long idx, max_idx; + u_int map; + u_long len; + + max_idx = pmp->pm_maxcluster / N_INUSEBITS; + idx = start / N_INUSEBITS; + start %= N_INUSEBITS; + map = pmp->pm_inusemap[idx]; + map &= ~((1 << start) - 1); + if (map) { + len = ffs(map) - 1 - start; + return len > count ? count : len; + } + len = N_INUSEBITS - start; + if (len >= count) + return count; + while (++idx <= max_idx) { + if (len >= count) + break; + if (map = pmp->pm_inusemap[idx]) { + len += ffs(map) - 1; + break; + } + len += N_INUSEBITS; + } + return len > count ? count : len; +} + +/* + * Allocate contigous free clusters. + * + * pmp - mount point. + * start - start of cluster chain. + * count - number of clusters to allocate. + * fillwith - put this value into the fat entry for the + * last allocated cluster. + * retcluster - put the first allocated cluster's number here. + * got - how many clusters were actually allocated. + */ +int +chainalloc(pmp, start, count, fillwith, retcluster, got) + struct msdosfsmount *pmp; + u_long start; + u_long count; + u_long fillwith; + u_long *retcluster; + u_long *got; +{ + int error; + + error = fatchain(pmp, start, count, fillwith); + if (error == 0) { +#ifdef MSDOSFS_DEBUG + printf("clusteralloc(): allocated cluster chain at %d (%d clusters)\n", + start, count); +#endif + if (retcluster) + *retcluster = start; + if (got) + *got = count; + while (count-- > 0) + usemap_alloc(pmp, start++); + } + return error; +} + +/* + * Allocate contiguous free clusters. + * + * pmp - mount point. + * start - preferred start of cluster chain. + * count - number of clusters requested. + * fillwith - put this value into the fat entry for the + * last allocated cluster. + * retcluster - put the first allocated cluster's number here. + * got - how many clusters were actually allocated. + */ +int +clusteralloc(pmp, start, count, fillwith, retcluster, got) + struct msdosfsmount *pmp; + u_long start; + u_long count; + u_long fillwith; + u_long *retcluster; + u_long *got; +{ + int error; + u_long idx; + u_long len, newst, foundcn, foundl, cn, l; + u_int map; + +#ifdef MSDOSFS_DEBUG + printf("clusteralloc(): find %d clusters\n",count); +#endif + if (start) { + if ((len = chainlength(pmp, start, count)) >= count) + return chainalloc(pmp, start, count, fillwith, retcluster, got); + } else { + /* + * This is a new file, initialize start + */ + struct timeval tv; + + microtime(&tv); + start = (tv.tv_usec >> 10)|tv.tv_usec; + len = 0; + } + + /* + * Start at a (pseudo) random place to maximize cluster runs + * under multiple writers. + */ + foundcn = newst = (start * 1103515245 + 12345) % (pmp->pm_maxcluster + 1); + foundl = 0; + + for (cn = newst; cn <= pmp->pm_maxcluster;) { + idx = cn / N_INUSEBITS; + map = pmp->pm_inusemap[idx]; + map |= (1 << (cn % N_INUSEBITS)) - 1; + if (map != (u_int)-1) { + cn = idx * N_INUSEBITS + ffs(map^(u_int)-1) - 1; + if ((l = chainlength(pmp, cn, count)) >= count) + return chainalloc(pmp, cn, count, fillwith, retcluster, got); + if (l > foundl) { + foundcn = cn; + foundl = l; + } + cn += l + 1; + continue; + } + cn += N_INUSEBITS - cn % N_INUSEBITS; + } + for (cn = 0; cn < newst;) { + idx = cn / N_INUSEBITS; + map = pmp->pm_inusemap[idx]; + map |= (1 << (cn % N_INUSEBITS)) - 1; + if (map != (u_int)-1) { + cn = idx * N_INUSEBITS + ffs(map^(u_int)-1) - 1; + if ((l = chainlength(pmp, cn, count)) >= count) + return chainalloc(pmp, cn, count, fillwith, retcluster, got); + if (l > foundl) { + foundcn = cn; + foundl = l; + } + cn += l + 1; + continue; + } + cn += N_INUSEBITS - cn % N_INUSEBITS; + } + + if (!foundl) + return ENOSPC; + + if (len) + return chainalloc(pmp, start, len, fillwith, retcluster, got); + else + return chainalloc(pmp, foundcn, foundl, fillwith, retcluster, got); +} + + +/* + * Free a chain of clusters. + * + * pmp - address of the msdosfs mount structure for the filesystem + * containing the cluster chain to be freed. + * startcluster - number of the 1st cluster in the chain of clusters to be + * freed. + */ +int +freeclusterchain(pmp, cluster) + struct msdosfsmount *pmp; + u_long cluster; +{ + int error = 0; + struct buf *bp = NULL; + u_long bn, bo, bsize, byteoffset; + u_long readcn, lbn = -1; + + while (cluster >= CLUST_FIRST && cluster <= pmp->pm_maxcluster) { + byteoffset = FATOFS(pmp, cluster); + fatblock(pmp, byteoffset, &bn, &bsize, &bo); + if (lbn != bn) { + if (bp) + updatefats(pmp, bp, bn); + if (error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp)) + return error; + lbn = bn; + } + usemap_free(pmp, cluster); + readcn = getushort(&bp->b_data[bo]); + if (FAT12(pmp)) { + if (cluster & 1) { + cluster = readcn >> 4; + readcn &= 0x000f; + readcn |= MSDOSFSFREE << 4; + } else { + cluster = readcn; + readcn &= 0xf000; + readcn |= MSDOSFSFREE & 0xfff; + } + putushort(&bp->b_data[bo], readcn); + cluster &= 0x0fff; + if ((cluster&0x0ff0) == 0x0ff0) + cluster |= 0xf000; + } else { + cluster = readcn; + putushort(&bp->b_data[bo], MSDOSFSFREE); + } + } + if (bp) + updatefats(pmp, bp, bn); + return error; +} + +/* + * Read in fat blocks looking for free clusters. For every free cluster + * found turn off its corresponding bit in the pm_inusemap. + */ +int +fillinusemap(pmp) + struct msdosfsmount *pmp; +{ + struct buf *bp = NULL; + u_long cn, readcn; + int error; + int fat12 = FAT12(pmp); + u_long bn, bo, bsize, byteoffset; + + /* + * Mark all clusters in use, we mark the free ones in the fat scan + * loop further down. + */ + for (cn = 0; cn < (pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS; cn++) + pmp->pm_inusemap[cn] = (u_int)-1; + + /* + * Figure how many free clusters are in the filesystem by ripping + * through the fat counting the number of entries whose content is + * zero. These represent free clusters. + */ + pmp->pm_freeclustercount = 0; + for (cn = CLUST_FIRST; cn <= pmp->pm_maxcluster; cn++) { + byteoffset = FATOFS(pmp, cn); + bo = byteoffset % pmp->pm_fatblocksize; + if (!bo || !bp) { + /* Read new FAT block */ + if (bp) + brelse(bp); + fatblock(pmp, byteoffset, &bn, &bsize, NULL); + error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); + if (error) + return error; + } + readcn = getushort(&bp->b_data[bo]); + if (fat12) { + if (cn & 1) + readcn >>= 4; + readcn &= 0x0fff; + } + + if (readcn == 0) + usemap_free(pmp, cn); + } + brelse(bp); + return 0; +} + +/* + * Allocate a new cluster and chain it onto the end of the file. + * + * dep - the file to extend + * count - number of clusters to allocate + * bpp - where to return the address of the buf header for the first new + * file block + * ncp - where to put cluster number of the first newly allocated cluster + * If this pointer is 0, do not return the cluster number. + * flags - see fat.h + * + * NOTE: This function is not responsible for turning on the DE_UPDATE bit of + * the de_flag field of the denode and it does not change the de_FileSize + * field. This is left for the caller to do. + */ +int +extendfile(dep, count, bpp, ncp, flags) + struct denode *dep; + u_long count; + struct buf **bpp; + u_long *ncp; + int flags; +{ + int error = 0; + u_long frcn; + u_long cn, got; + struct msdosfsmount *pmp = dep->de_pmp; + struct buf *bp; + + /* + * Don't try to extend the root directory + */ + if (DETOV(dep)->v_flag & VROOT) { + printf("extendfile(): attempt to extend root directory\n"); + return ENOSPC; + } + + /* + * If the "file's last cluster" cache entry is empty, and the file + * is not empty, then fill the cache entry by calling pcbmap(). + */ + fc_fileextends++; + if (dep->de_fc[FC_LASTFC].fc_frcn == FCE_EMPTY && + dep->de_StartCluster != 0) { + fc_lfcempty++; + error = pcbmap(dep, 0xffff, 0, &cn); + /* we expect it to return E2BIG */ + if (error != E2BIG) + return error; + error = 0; + } + + while (count > 0) { + /* + * Allocate a new cluster chain and cat onto the end of the file. + * If the file is empty we make de_StartCluster point to the new + * block. Note that de_StartCluster being 0 is sufficient to be + * sure the file is empty since we exclude attempts to extend the + * root directory above, and the root dir is the only file with a + * startcluster of 0 that has blocks allocated (sort of). + */ + if (dep->de_StartCluster == 0) + cn = 0; + else + cn = dep->de_fc[FC_LASTFC].fc_fsrcn + 1; + if (error = clusteralloc(pmp, cn, count, CLUST_EOFE, &cn, &got)) + return error; + + count -= got; + + /* + * Give them the filesystem relative cluster number if they want + * it. + */ + if (ncp) { + *ncp = cn; + ncp = NULL; + } + + if (dep->de_StartCluster == 0) { + dep->de_StartCluster = cn; + frcn = 0; + } else { + error = fatentry(FAT_SET, pmp, dep->de_fc[FC_LASTFC].fc_fsrcn, + 0, cn); + if (error) { + clusterfree(pmp, cn, NULL); + return error; + } + + frcn = dep->de_fc[FC_LASTFC].fc_frcn + 1; + } + + /* + * Update the "last cluster of the file" entry in the denode's fat + * cache. + */ + fc_setcache(dep, FC_LASTFC, frcn + got - 1, cn + got - 1); + + if (flags & DE_CLEAR) { + while (got-- > 0) { + /* + * Get the buf header for the new block of the file. + */ + if (dep->de_Attributes & ATTR_DIRECTORY) + bp = getblk(pmp->pm_devvp, cntobn(pmp, cn++), + pmp->pm_bpcluster, 0, 0); + else { + bp = getblk(DETOV(dep), frcn++, pmp->pm_bpcluster, 0, 0); + /* + * Do the bmap now, as in msdosfs_write + */ + if (pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0)) + bp->b_blkno = -1; + if (bp->b_blkno == -1) + panic("extendfile: pcbmap"); + } + clrbuf(bp); + if (bpp) { + *bpp = bp; + bpp = NULL; + } else { + bp->b_flags |= B_AGE; + bawrite(bp); + } + } + } + } + + return 0; +} diff --git a/sys/fs/msdosfs/msdosfs_lookup.c b/sys/fs/msdosfs/msdosfs_lookup.c new file mode 100644 index 0000000..c67f888 --- /dev/null +++ b/sys/fs/msdosfs/msdosfs_lookup.c @@ -0,0 +1,763 @@ +/* $Id$ */ +/* $NetBSD: msdosfs_lookup.c,v 1.14 1994/08/21 18:44:07 ws Exp $ */ + +/*- + * Copyright (C) 1994 Wolfgang Solfrank. + * Copyright (C) 1994 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 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. + * + * October 1992 + */ + +#include <sys/param.h> +#include <sys/namei.h> +#include <sys/buf.h> +#include <sys/vnode.h> +#include <sys/mount.h> + +#include <msdosfs/bpb.h> +#include <msdosfs/direntry.h> +#include <msdosfs/denode.h> +#include <msdosfs/msdosfsmount.h> +#include <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. + */ +int +msdosfs_lookup(ap) + struct vop_lookup_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + } */ *ap; +{ + struct vnode *vdp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct componentname *cnp = ap->a_cnp; + daddr_t bn; + int error; + int lockparent; + int wantparent; + int slotstatus; + +#define NONE 0 +#define FOUND 1 + int slotoffset = -1; + int slotcluster = -1; + int frcn; + u_long cluster; + int rootreloff; + int diroff; + int isadir; /* ~0 if found direntry is a directory */ + u_long scn; /* starting cluster number */ + struct vnode *pdp; + struct denode *dp; + struct denode *tdp; + struct msdosfsmount *pmp; + struct buf *bp = 0; + struct direntry *dep = NULL; + u_char dosfilename[12]; + int flags = cnp->cn_flags; + int nameiop = cnp->cn_nameiop; + +#ifdef MSDOSFS_DEBUG + printf("msdosfs_lookup(): looking for %s\n", cnp->cn_nameptr); +#endif + dp = VTODE(vdp); + pmp = dp->de_pmp; + *vpp = NULL; + lockparent = flags & LOCKPARENT; + wantparent = flags & (LOCKPARENT | WANTPARENT); +#ifdef MSDOSFS_DEBUG + printf("msdosfs_lookup(): vdp %08x, dp %08x, Attr %02x\n", + vdp, dp, dp->de_Attributes); +#endif + + /* + * 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) + return ENOTDIR; + + /* + * 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(vdp, vpp, cnp)) { + int vpid; + + if (error == ENOENT) + return error; + pdp = vdp; + vdp = *vpp; + dp = VTODE(vdp); + vpid = vdp->v_id; + if (pdp == vdp) { + VREF(vdp); + error = 0; + } else if (flags & ISDOTDOT) { + VOP_UNLOCK(pdp); + error = vget(vdp, 1); + if (!error && lockparent && (flags & ISLASTCN)) + error = VOP_LOCK(pdp); + } else { + error = vget(vdp, 1); + if (!lockparent || error || !(flags & ISLASTCN)) + VOP_UNLOCK(pdp); + } + + if (!error) { + if (vpid == vdp->v_id) { +#ifdef MSDOSFS_DEBUG + printf("msdosfs_lookup(): cache hit, vnode %08x, file %s\n", + vdp, dp->de_Name); +#endif + return 0; + } + vput(vdp); + if (lockparent && pdp != vdp && (flags & ISLASTCN)) + VOP_UNLOCK(pdp); + } + if (error = VOP_LOCK(pdp)) + return error; + vdp = pdp; + dp = VTODE(vdp); + *vpp = NULL; + } + + /* + * 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) && cnp->cn_nameptr[0] == '.' && + (cnp->cn_namelen == 1 || + (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.'))) { + isadir = ATTR_DIRECTORY; + scn = MSDOSFSROOT; +#ifdef MSDOSFS_DEBUG + printf("msdosfs_lookup(): looking for . or .. in root directory\n"); +#endif + cluster = MSDOSFSROOT; + diroff = MSDOSFSROOT_OFS; + goto foundroot; + } + + /* + * Don't search for free slots unless we are creating a filename + * and we are at the end of the pathname. + */ + slotstatus = FOUND; + if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN)) { + slotstatus = NONE; + slotoffset = -1; + } + + unix2dosfn((u_char *) cnp->cn_nameptr, dosfilename, cnp->cn_namelen); + dosfilename[11] = 0; +#ifdef MSDOSFS_DEBUG + printf("msdosfs_lookup(): dos version of filename %s, length %d\n", + dosfilename, cnp->cn_namelen); +#endif + /* + * Search the directory pointed at by vdp for the name pointed at + * by cnp->cn_nameptr. + */ + tdp = NULL; + /* + * 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. + */ + rootreloff = 0; + for (frcn = 0;; frcn++) { + if (error = pcbmap(dp, frcn, &bn, &cluster)) { + if (error == E2BIG) + break; + return error; + } + if (error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp)) + return error; + for (diroff = 0; diroff < pmp->pm_depclust; diroff++) { + dep = (struct direntry *) bp->b_data + 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) { + slotstatus = FOUND; + if (cluster == MSDOSFSROOT) + slotoffset = rootreloff; + else + slotoffset = diroff; + slotcluster = cluster; + } + if (dep->deName[0] == SLOT_EMPTY) { + brelse(bp); + goto notfound; + } + } else { + /* + * Ignore volume labels (anywhere, not just + * the root directory). + */ + if ((dep->deAttributes & ATTR_VOLUME) == 0 && + bcmp(dosfilename, dep->deName, 11) == 0) { +#ifdef MSDOSFS_DEBUG + printf("msdosfs_lookup(): match diroff %d, rootreloff %d\n", + diroff, rootreloff); +#endif + /* + * 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) + diroff = rootreloff; + dp->de_fndoffset = diroff; + dp->de_fndclust = cluster; + goto found; + } + } + rootreloff++; + } /* for (diroff = 0; .... */ + /* + * Release the buffer holding the directory cluster just + * searched. + */ + brelse(bp); + } /* for (frcn = 0; ; frcn++) */ +notfound:; + /* + * 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. + */ +#ifdef MSDOSFS_DEBUG + printf("msdosfs_lookup(): op %d, refcnt %d, slotstatus %d\n", + nameiop, dp->de_refcnt, slotstatus); + printf(" slotoffset %d, slotcluster %d\n", + slotoffset, slotcluster); +#endif + if ((nameiop == CREATE || nameiop == RENAME) && + (flags & ISLASTCN) && dp->de_refcnt != 0) { + if (slotstatus == NONE) { + dp->de_fndoffset = (u_long)-1; + dp->de_fndclust = (u_long)-1; + } else { +#ifdef MSDOSFS_DEBUG + printf("msdosfs_lookup(): saving empty slot location\n"); +#endif + dp->de_fndoffset = slotoffset; + dp->de_fndclust = slotcluster; + } + /* dp->de_flag |= DE_UPDATE; never update dos directories */ + cnp->cn_flags |= SAVENAME; + if (!lockparent)/* leave searched dir locked? */ + VOP_UNLOCK(vdp); + return EJUSTRETURN; + } + /* + * Insert name in cache as non-existant if not trying to create it. + */ + if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) + cache_enter(vdp, *vpp, cnp); + return ENOENT; + +found: ; + /* + * NOTE: We still have the buffer with matched directory entry at + * this point. + */ + isadir = dep->deAttributes & ATTR_DIRECTORY; + scn = getushort(dep->deStartCluster); + +foundroot:; + /* + * 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 entry. + */ + if (nameiop == DELETE && (flags & ISLASTCN)) { + if (dp->de_StartCluster == scn && isadir) { /* "." */ + VREF(vdp); + *vpp = vdp; + if (bp) + brelse(bp); + return 0; + } + error = deget(pmp, cluster, diroff, dep, &tdp); + if (error) { + if (bp) + brelse(bp); + return error; + } + *vpp = DETOV(tdp); + if (!lockparent) + VOP_UNLOCK(vdp); + if (bp) + brelse(bp); + return 0; + } + + /* + * If renaming. + */ + if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) { + if (dp->de_StartCluster == scn && isadir) { + if (bp) + brelse(bp); + return EISDIR; + } + error = deget(pmp, cluster, diroff, dep, &tdp); + if (error) { + if (bp) + brelse(bp); + return error; + } + *vpp = DETOV(tdp); + cnp->cn_flags |= SAVENAME; + if (!lockparent) + VOP_UNLOCK(vdp); + if (bp) + brelse(bp); + return 0; + } + + /* + * ? + */ + pdp = vdp; + if (flags & ISDOTDOT) { + VOP_UNLOCK(pdp); + error = deget(pmp, cluster, diroff, dep, &tdp); + if (error) { + VOP_LOCK(pdp); + if (bp) + brelse(bp); + return error; + } + if (lockparent && (flags & ISLASTCN) + && (error = VOP_LOCK(pdp))) { + vput(DETOV(tdp)); + return error; + } + *vpp = DETOV(tdp); + } else if (dp->de_StartCluster == scn && isadir) { /* "." */ + VREF(vdp); + *vpp = vdp; + } else { + error = deget(pmp, cluster, diroff, dep, &tdp); + if (error) { + if (bp) + brelse(bp); + return error; + } + if (!lockparent || !(flags & ISLASTCN)) + VOP_UNLOCK(pdp); + *vpp = DETOV(tdp); + } + if (bp) + brelse(bp); + + /* + * Insert name in cache if wanted. + */ + if (cnp->cn_flags & MAKEENTRY) + cache_enter(vdp, *vpp, cnp); + return 0; +} + +/* + * dep - directory entry to copy into the directory + * ddep - directory to add to + * depp - return the address of the denode for the created directory entry + * if depp != 0 + */ +int +createde(dep, ddep, depp) + struct denode *dep; + struct denode *ddep; + struct denode **depp; +{ + int bn; + int error; + u_long dirclust, diroffset; + struct direntry *ndep; + struct msdosfsmount *pmp = ddep->de_pmp; + struct buf *bp; + +#ifdef MSDOSFS_DEBUG + printf("createde(dep %08x, ddep %08x, depp %08x)\n", dep, ddep, depp); +#endif + + /* + * 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 + * case. + */ + if (ddep->de_fndclust == (u_long)-1) { + if (error = extendfile(ddep, 1, &bp, &dirclust, DE_CLEAR)) + return error; + ndep = (struct direntry *) bp->b_data; + /* + * Let caller know where we put the directory entry. + */ + ddep->de_fndclust = dirclust; + ddep->de_fndoffset = diroffset = 0; + /* + * Update the size of the directory + */ + ddep->de_FileSize += pmp->pm_bpcluster; + } else { + /* + * 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 = ddep->de_fndclust; + diroffset = ddep->de_fndoffset; + + error = readep(pmp, dirclust, diroffset, &bp, &ndep); + if (error) + return error; + } + DE_EXTERNALIZE(ndep, dep); + + /* + * If they want us to return with the denode gotten. + */ + if (depp) { + error = deget(pmp, dirclust, diroffset, ndep, depp); + if (error) + return error; + } + if (error = bwrite(bp)) { + vput(DETOV(*depp)); /* free the vnode we got on error */ + return error; + } + return 0; +} + +/* + * Read in a directory entry and mark it as being deleted. + */ +int +markdeleted(pmp, dirclust, diroffset) + struct msdosfsmount *pmp; + u_long dirclust; + u_long diroffset; +{ + int error; + struct direntry *ep; + struct buf *bp; + + error = readep(pmp, dirclust, diroffset, &bp, &ep); + if (error) + return error; + ep->deName[0] = SLOT_DELETED; + return bwrite(bp); +} + +/* + * 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. + */ +int +removede(pdep,dep) + struct denode *pdep; /* directory where the entry is removed */ + struct denode *dep; /* file to be removed */ +{ + struct msdosfsmount *pmp = pdep->de_pmp; + int error; + +#ifdef MSDOSFS_DEBUG + printf("removede(): filename %s\n", dep->de_Name); + printf("removede(): dep %08x, ndpcluster %d, ndpoffset %d\n", + dep, pdep->de_fndclust, pdep->de_fndoffset); +#endif + + /* + * 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, pdep->de_fndclust, pdep->de_fndoffset); + + if (error == 0) + dep->de_refcnt--; + return error; +} + +/* + * Be sure a directory is empty except for "." and "..". Return 1 if empty, + * return 0 if not empty or error. + */ +int +dosdirempty(dep) + struct denode *dep; +{ + int dei; + int error; + u_long cn; + daddr_t bn; + struct buf *bp; + struct msdosfsmount *pmp = dep->de_pmp; + struct direntry *dentp; + + /* + * 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. + */ + for (cn = 0;; cn++) { + error = pcbmap(dep, cn, &bn, 0); + if (error == E2BIG) + return 1; /* it's empty */ + error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, + &bp); + if (error) + return error; + dentp = (struct direntry *) bp->b_data; + 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) { + brelse(bp); + return 1; + } + /* + * Any names other than "." and ".." in a + * directory mean it is not empty. + */ + if (bcmp(dentp->deName, ". ", 11) && + bcmp(dentp->deName, ".. ", 11)) { + brelse(bp); +#ifdef MSDOSFS_DEBUG + printf("dosdirempty(): entry %d found %02x, %02x\n", + dei, dentp->deName[0], dentp->deName[1]); +#endif + return 0; /* not empty */ + } + } + dentp++; + } + brelse(bp); + } + /* NOTREACHED */ +} + +/* + * 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. + */ +int +doscheckpath(source, target) + struct denode *source; + struct denode *target; +{ + daddr_t scn; + struct denode dummy; + struct msdosfsmount *pmp; + struct direntry *ep; + struct denode *dep; + struct buf *bp = NULL; + int error = 0; + + dep = target; + if ((target->de_Attributes & ATTR_DIRECTORY) == 0 || + (source->de_Attributes & ATTR_DIRECTORY) == 0) { + error = ENOTDIR; + goto out; + } + if (dep->de_StartCluster == source->de_StartCluster) { + error = EEXIST; + goto out; + } + if (dep->de_StartCluster == MSDOSFSROOT) + goto out; + for (;;) { + if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) { + error = ENOTDIR; + goto out; + } + pmp = dep->de_pmp; + scn = dep->de_StartCluster; + error = bread(pmp->pm_devvp, cntobn(pmp, scn), + pmp->pm_bpcluster, NOCRED, &bp); + if (error) { + break; + } + ep = (struct direntry *) bp->b_data + 1; + if ((ep->deAttributes & ATTR_DIRECTORY) == 0 || + bcmp(ep->deName, ".. ", 11) != 0) { + error = ENOTDIR; + break; + } + scn = getushort(ep->deStartCluster); + if (scn == source->de_StartCluster) { + error = EINVAL; + break; + } + if (scn == MSDOSFSROOT) + break; + vput(DETOV(dep)); + /* NOTE: deget() clears dep on error */ + error = deget(pmp, scn, 0, ep, &dep); + brelse(bp); + bp = NULL; + if (error) + break; + } +out: ; + if (bp) + brelse(bp); + if (error == ENOTDIR) + printf("doscheckpath(): .. not a directory?\n"); + if (dep != NULL) + vput(DETOV(dep)); + return error; +} + +/* + * 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. + */ +int +readep(pmp, dirclu, dirofs, bpp, epp) + struct msdosfsmount *pmp; + u_long dirclu, dirofs; + struct buf **bpp; + struct direntry **epp; +{ + int error; + daddr_t bn; + + bn = detobn(pmp, dirclu, dirofs); + if (error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, bpp)) { + *bpp = NULL; + return error; + } + if (epp) + *epp = bptoep(pmp, *bpp, dirofs); + return 0; +} + + +/* + * 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. + */ +int +readde(dep, bpp, epp) + struct denode *dep; + struct buf **bpp; + struct direntry **epp; +{ + return readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset, + bpp, epp); +} diff --git a/sys/fs/msdosfs/msdosfs_vfsops.c b/sys/fs/msdosfs/msdosfs_vfsops.c new file mode 100644 index 0000000..ac9c284 --- /dev/null +++ b/sys/fs/msdosfs/msdosfs_vfsops.c @@ -0,0 +1,733 @@ +/* $Id$ */ +/* $NetBSD: msdosfs_vfsops.c,v 1.19 1994/08/21 18:44:10 ws Exp $ */ + +/*- + * Copyright (C) 1994 Wolfgang Solfrank. + * Copyright (C) 1994 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 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. + * + * October 1992 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/namei.h> +#include <sys/proc.h> +#include <sys/kernel.h> +#include <sys/vnode.h> +#include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */ +#include <sys/mount.h> +#include <sys/buf.h> +#include <sys/file.h> +#include <sys/malloc.h> + +#include <msdosfs/bpb.h> +#include <msdosfs/bootsect.h> +#include <msdosfs/direntry.h> +#include <msdosfs/denode.h> +#include <msdosfs/msdosfsmount.h> +#include <msdosfs/fat.h> + +int msdosfsdoforce = 1; /* 1 = force unmount */ + +/* + * mp - path - addr in user space of mount point (ie /usr or whatever) + * data - addr in user space of mount params including the name of the block + * special file to treat as a filesystem. + */ +int +msdosfs_mount(mp, path, data, ndp, p) + struct mount *mp; + char *path; + caddr_t data; + struct nameidata *ndp; + struct proc *p; +{ + struct vnode *devvp; /* vnode for blk device to mount */ + struct msdosfs_args args; /* will hold data from mount request */ + struct msdosfsmount *pmp; /* msdosfs specific mount control block */ + int error, flags; + u_int size; + struct ucred *cred, *scred; + struct vattr va; + + /* + * Copy in the args for the mount request. + */ + if (error = copyin(data, (caddr_t) & args, sizeof(struct msdosfs_args))) + return error; + + /* + * If they just want to update then be sure we can do what is + * asked. Can't change a filesystem from read/write to read only. + * Why? And if they've supplied a new device file name then we + * continue, otherwise return. + */ + if (mp->mnt_flag & MNT_UPDATE) { + pmp = (struct msdosfsmount *) mp->mnt_data; + error = 0; + if (pmp->pm_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) { + flags = WRITECLOSE; + if (mp->mnt_flag & MNT_FORCE) + flags |= FORCECLOSE; + if (vfs_busy(mp)) + return EBUSY; + error = vflush(mp, NULLVP, flags); + vfs_unbusy(mp); + } + if (!error && (mp->mnt_flag & MNT_RELOAD)) + /* not yet implemented */ + error = EINVAL; + if (error) + return error; + if (pmp->pm_ronly && (mp->mnt_flag & MNT_RDONLY) == 0) + pmp->pm_ronly = 0; + if (args.fspec == 0) { + /* + * Process export requests. + */ + return vfs_export(mp, &pmp->pm_export, &args.export); + } + } else + pmp = NULL; + + /* + * check to see that the user in owns the target directory. + * Note the very XXX trick to make sure we're checking as the + * real user -- were mount() executable by anyone, this wouldn't + * be a problem. + * + * XXX there should be one consistent error out. + */ + cred = crdup(p->p_ucred); /* XXX */ + cred->cr_uid = p->p_cred->p_ruid; /* XXX */ + error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p); + if (error) { + crfree(cred); /* XXX */ + return error; + } + if (cred->cr_uid != 0) { + if (va.va_uid != cred->cr_uid) { + error = EACCES; + crfree(cred); /* XXX */ + return error; + } + + /* a user mounted it; we'll verify permissions when unmounting */ + mp->mnt_flag |= MNT_USER; + } + + /* + * Now, lookup the name of the block device this mount or name + * update request is to apply to. + */ + NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p); + scred = p->p_ucred; /* XXX */ + p->p_ucred = cred; /* XXX */ + error = namei(ndp); + p->p_ucred = scred; /* XXX */ + crfree(cred); /* XXX */ + if (error != 0) + return error; + + /* + * Be sure they've given us a block device to treat as a + * filesystem. And, that its major number is within the bdevsw + * table. + */ + devvp = ndp->ni_vp; + if (devvp->v_type != VBLK) { + vrele(devvp); + return ENOTBLK; + } + if (major(devvp->v_rdev) >= nblkdev) { + vrele(devvp); + return ENXIO; + } + + /* + * If this is an update, then make sure the vnode for the block + * special device is the same as the one our filesystem is in. + */ + if (mp->mnt_flag & MNT_UPDATE) { + if (devvp != pmp->pm_devvp) + error = EINVAL; + else + vrele(devvp); + } else { + + /* + * Well, it's not an update, it's a real mount request. + * Time to get dirty. + */ + error = mountmsdosfs(devvp, mp, p); + } + if (error) { + vrele(devvp); + return error; + } + + /* + * Copy in the name of the directory the filesystem is to be + * mounted on. Then copy in the name of the block special file + * representing the filesystem being mounted. And we clear the + * remainder of the character strings to be tidy. Set up the + * user id/group id/mask as specified by the user. Then, we try to + * fill in the filesystem stats structure as best we can with + * whatever applies from a dos file system. + */ + pmp = (struct msdosfsmount *) mp->mnt_data; + copyinstr(path, (caddr_t) mp->mnt_stat.f_mntonname, + sizeof(mp->mnt_stat.f_mntonname) - 1, &size); + bzero(mp->mnt_stat.f_mntonname + size, + sizeof(mp->mnt_stat.f_mntonname) - size); + copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); + bzero(mp->mnt_stat.f_mntfromname + size, + MNAMELEN - size); + pmp->pm_mounter = p->p_cred->p_ruid; + pmp->pm_gid = args.gid; + pmp->pm_uid = args.uid; + pmp->pm_mask = args.mask; + (void) msdosfs_statfs(mp, &mp->mnt_stat, p); +#ifdef MSDOSFS_DEBUG + printf("msdosfs_mount(): mp %x, pmp %x, inusemap %x\n", mp, pmp, pmp->pm_inusemap); +#endif + return 0; +} + +int +mountmsdosfs(devvp, mp, p) + struct vnode *devvp; + struct mount *mp; + struct proc *p; +{ + int i; + int bpc; + int bit; + int error; + int needclose; + int ronly = (mp->mnt_flag & MNT_RDONLY) != 0; + dev_t dev = devvp->v_rdev; + union bootsector *bsp; + struct msdosfsmount *pmp = NULL; + struct buf *bp0 = NULL; + struct byte_bpb33 *b33; + struct byte_bpb50 *b50; + + /* + * Multiple mounts of the same block special file aren't allowed. + * Make sure no one else has the special file open. And flush any + * old buffers from this filesystem. Presumably this prevents us + * from running into buffers that are the wrong blocksize. + */ + if (error = vfs_mountedon(devvp)) + return error; + if (vcount(devvp) > 1) + return EBUSY; + if (error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0)) + return error; + + /* + * Now open the block special file. + */ + if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD | FWRITE, FSCRED, p)) + return error; + needclose = 1; +#ifdef HDSUPPORT + /* + * Put this in when we support reading dos filesystems from + * partitioned harddisks. + */ + if (VOP_IOCTL(devvp, DIOCGPART, &msdosfspart, FREAD, NOCRED, p) == 0) { + } +#endif + + /* + * Read the boot sector of the filesystem, and then check the boot + * signature. If not a dos boot sector then error out. We could + * also add some checking on the bsOemName field. So far I've seen + * the following values: "IBM 3.3" "MSDOS3.3" "MSDOS5.0" + */ + if (error = bread(devvp, 0, 512, NOCRED, &bp0)) + goto error_exit; + bp0->b_flags |= B_AGE; + bsp = (union bootsector *) bp0->b_data; + b33 = (struct byte_bpb33 *) bsp->bs33.bsBPB; + b50 = (struct byte_bpb50 *) bsp->bs50.bsBPB; +#ifdef MSDOSFS_CHECKSIG + if (bsp->bs50.bsBootSectSig != BOOTSIG) { + error = EINVAL; + goto error_exit; + } +#endif + if ( bsp->bs50.bsJump[0] != 0xe9 && + (bsp->bs50.bsJump[0] != 0xeb || bsp->bs50.bsJump[2] != 0x90)) { + error = EINVAL; + goto error_exit; + } + + pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK); + bzero((caddr_t)pmp, sizeof *pmp); + pmp->pm_mountp = mp; + + /* + * Compute several useful quantities from the bpb in the + * bootsector. Copy in the dos 5 variant of the bpb then fix up + * the fields that are different between dos 5 and dos 3.3. + */ + pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec); + pmp->pm_SectPerClust = b50->bpbSecPerClust; + pmp->pm_ResSectors = getushort(b50->bpbResSectors); + pmp->pm_FATs = b50->bpbFATs; + pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts); + pmp->pm_Sectors = getushort(b50->bpbSectors); + pmp->pm_Media = b50->bpbMedia; + pmp->pm_FATsecs = getushort(b50->bpbFATsecs); + pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack); + pmp->pm_Heads = getushort(b50->bpbHeads); + + /* XXX - We should probably check more values here */ + if (!pmp->pm_BytesPerSec || !pmp->pm_SectPerClust || + !pmp->pm_Heads || pmp->pm_Heads > 255 || + !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) { + error = EINVAL; + goto error_exit; + } + + if (pmp->pm_Sectors == 0) { + pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); + pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors); + } else { + pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs); + pmp->pm_HugeSectors = pmp->pm_Sectors; + } + pmp->pm_fatblk = pmp->pm_ResSectors; + pmp->pm_rootdirblk = pmp->pm_fatblk + + (pmp->pm_FATs * pmp->pm_FATsecs); + pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)) + / + pmp->pm_BytesPerSec;/* in sectors */ + pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; + pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) / + pmp->pm_SectPerClust; + pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1; + pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec; + if (FAT12(pmp)) + /* + * This will usually be a floppy disk. This size makes sure + * that one fat entry will not be split across multiple + * blocks. + */ + pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec; + else + /* + * This will usually be a hard disk. Reading or writing one + * block should be quite fast. + */ + pmp->pm_fatblocksize = MAXBSIZE; + pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec; + + + if ((pmp->pm_rootdirsize % pmp->pm_SectPerClust) != 0) + printf("mountmsdosfs(): root directory is not a multiple of the clustersize in length\n"); + + /* + * Compute mask and shift value for isolating cluster relative byte + * offsets and cluster numbers from a file offset. + */ + bpc = pmp->pm_SectPerClust * pmp->pm_BytesPerSec; + pmp->pm_bpcluster = bpc; + pmp->pm_depclust = bpc / sizeof(struct direntry); + pmp->pm_crbomask = bpc - 1; + if (bpc == 0) { + error = EINVAL; + goto error_exit; + } + bit = 1; + for (i = 0; i < 32; i++) { + if (bit & bpc) { + if (bit ^ bpc) { + error = EINVAL; + goto error_exit; + } + pmp->pm_cnshift = i; + break; + } + bit <<= 1; + } + + pmp->pm_brbomask = 0x01ff; /* 512 byte blocks only (so far) */ + pmp->pm_bnshift = 9; /* shift right 9 bits to get bn */ + + /* + * Release the bootsector buffer. + */ + brelse(bp0); + bp0 = NULL; + + /* + * Allocate memory for the bitmap of allocated clusters, and then + * fill it in. + */ + pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS - 1) + / N_INUSEBITS) + * sizeof(*pmp->pm_inusemap), + M_MSDOSFSFAT, M_WAITOK); + + /* + * fillinusemap() needs pm_devvp. + */ + pmp->pm_dev = dev; + pmp->pm_devvp = devvp; + + /* + * Have the inuse map filled in. + */ + error = fillinusemap(pmp); + if (error) + goto error_exit; + + /* + * If they want fat updates to be synchronous then let them suffer + * the performance degradation in exchange for the on disk copy of + * the fat being correct just about all the time. I suppose this + * would be a good thing to turn on if the kernel is still flakey. + */ + pmp->pm_waitonfat = mp->mnt_flag & MNT_SYNCHRONOUS; + + /* + * Finish up. + */ + pmp->pm_ronly = ronly; + if (ronly == 0) + pmp->pm_fmod = 1; + mp->mnt_data = (qaddr_t) pmp; + mp->mnt_stat.f_fsid.val[0] = (long)dev; + mp->mnt_stat.f_fsid.val[1] = MOUNT_MSDOS; + mp->mnt_flag |= MNT_LOCAL; +#ifdef QUOTA + /* + * If we ever do quotas for DOS filesystems this would be a place + * to fill in the info in the msdosfsmount structure. You dolt, + * quotas on dos filesystems make no sense because files have no + * owners on dos filesystems. of course there is some empty space + * in the directory entry where we could put uid's and gid's. + */ +#endif + devvp->v_specflags |= SI_MOUNTEDON; + + return 0; + +error_exit:; + if (bp0) + brelse(bp0); + if (needclose) + (void) VOP_CLOSE(devvp, ronly ? FREAD : FREAD | FWRITE, + NOCRED, p); + if (pmp) { + if (pmp->pm_inusemap) + free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT); + free((caddr_t) pmp, M_MSDOSFSMNT); + mp->mnt_data = (qaddr_t) 0; + } + return error; +} + +int +msdosfs_start(mp, flags, p) + struct mount *mp; + int flags; + struct proc *p; +{ + return 0; +} + +/* + * Unmount the filesystem described by mp. + */ +int +msdosfs_unmount(mp, mntflags, p) + struct mount *mp; + int mntflags; + struct proc *p; +{ + int flags = 0; + int error; + struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; + struct vnode *vp = pmp->pm_devvp; + + /* only the mounter, or superuser can unmount */ + if ((p->p_cred->p_ruid != pmp->pm_mounter) && + (error = suser(p->p_ucred, &p->p_acflag))) + return error; + + if (mntflags & MNT_FORCE) { + if (!msdosfsdoforce) + return EINVAL; + flags |= FORCECLOSE; + } +#ifdef QUOTA +#endif + if (error = vflush(mp, NULLVP, flags)) + return error; + pmp->pm_devvp->v_specflags &= ~SI_MOUNTEDON; +#ifdef MSDOSFS_DEBUG + printf("msdosfs_umount(): just before calling VOP_CLOSE()\n"); + printf("flag %08x, usecount %d, writecount %d, holdcnt %d\n", + vp->v_flag, vp->v_usecount, vp->v_writecount, vp->v_holdcnt); + printf("lastr %d, id %d, mount %08x, op %08x\n", + vp->v_lastr, vp->v_id, vp->v_mount, vp->v_op); + printf("freef %08x, freeb %08x, mountf %08x, mountb %08x\n", + vp->v_freef, vp->v_freeb, vp->v_mountf, vp->v_mountb); + printf("cleanblkhd %08x, dirtyblkhd %08x, numoutput %d, type %d\n", + vp->v_cleanblkhd, vp->v_dirtyblkhd, vp->v_numoutput, vp->v_type); + printf("union %08x, tag %d, data[0] %08x, data[1] %08x\n", + vp->v_socket, vp->v_tag, vp->v_data[0], vp->v_data[1]); +#endif + error = VOP_CLOSE(pmp->pm_devvp, pmp->pm_ronly ? FREAD : FREAD | FWRITE, + NOCRED, p); + vrele(pmp->pm_devvp); + free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT); + free((caddr_t) pmp, M_MSDOSFSMNT); + mp->mnt_data = (qaddr_t) 0; + mp->mnt_flag &= ~MNT_LOCAL; + return error; +} + +int +msdosfs_root(mp, vpp) + struct mount *mp; + struct vnode **vpp; +{ + struct denode *ndep; + struct msdosfsmount *pmp = (struct msdosfsmount *) (mp->mnt_data); + int error; + + error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, NULL, &ndep); +#ifdef MSDOSFS_DEBUG + printf("msdosfs_root(); mp %08x, pmp %08x, ndep %08x, vp %08x\n", + mp, pmp, ndep, DETOV(ndep)); +#endif + if (error == 0) + *vpp = DETOV(ndep); + return error; +} + +int +msdosfs_quotactl(mp, cmds, uid, arg, p) + struct mount *mp; + int cmds; + uid_t uid; + caddr_t arg; + struct proc *p; +{ +#ifdef QUOTA +#else + return EOPNOTSUPP; +#endif +} + +int +msdosfs_statfs(mp, sbp, p) + struct mount *mp; + struct statfs *sbp; + struct proc *p; +{ + struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; + + /* + * Fill in the stat block. + */ +#ifdef COMPAT_09 + sbp->f_type = 4; +#else + sbp->f_type = 0; +#endif + sbp->f_bsize = pmp->pm_bpcluster; + sbp->f_iosize = pmp->pm_bpcluster; + sbp->f_blocks = pmp->pm_nmbrofclusters; + sbp->f_bfree = pmp->pm_freeclustercount; + sbp->f_bavail = pmp->pm_freeclustercount; + sbp->f_files = pmp->pm_RootDirEnts; /* XXX */ + sbp->f_ffree = 0; /* what to put in here? */ + + /* + * Copy the mounted on and mounted from names into the passed in + * stat block, if it is not the one in the mount structure. + */ + if (sbp != &mp->mnt_stat) { + bcopy((caddr_t) mp->mnt_stat.f_mntonname, + (caddr_t) & sbp->f_mntonname[0], MNAMELEN); + bcopy((caddr_t) mp->mnt_stat.f_mntfromname, + (caddr_t) & sbp->f_mntfromname[0], MNAMELEN); + } +#if 0 + strncpy(&sbp->f_fstypename[0], mp->mnt_op->vfs_name, MFSNAMELEN); + sbp->f_fstypename[MFSNAMELEN] = '\0'; +#endif + return 0; +} + +int +msdosfs_sync(mp, waitfor, cred, p) + struct mount *mp; + int waitfor; + struct ucred *cred; + struct proc *p; +{ + struct vnode *vp; + struct denode *dep; + struct msdosfsmount *pmp; + int error; + int allerror = 0; + + pmp = (struct msdosfsmount *) mp->mnt_data; + + /* + * If we ever switch to not updating all of the fats all the time, + * this would be the place to update them from the first one. + */ + if (pmp->pm_fmod) + if (pmp->pm_ronly) + panic("msdosfs_sync: rofs mod"); + else { + /* update fats here */ + } + + /* + * Go thru in memory denodes and write them out along with + * unwritten file blocks. + */ +loop: + for (vp = mp->mnt_vnodelist.lh_first; vp; + vp = vp->v_mntvnodes.le_next) { + if (vp->v_mount != mp) /* not ours anymore */ + goto loop; + if (VOP_ISLOCKED(vp)) /* file is busy */ + continue; + dep = VTODE(vp); + if ((dep->de_flag & DE_UPDATE) == 0 && + vp->v_dirtyblkhd.lh_first == NULL) + continue; + if (vget(vp, 1)) /* not there anymore? */ + goto loop; + if (error = VOP_FSYNC(vp, cred, waitfor, p)) + allerror = error; + vput(vp); /* done with this one */ + } + + /* + * Flush filesystem control info. + */ + if (error = VOP_FSYNC(pmp->pm_devvp, cred, waitfor, p)) + allerror = error; + return allerror; +} + +int +msdosfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) + struct mount *mp; + struct fid *fhp; + struct mbuf *nam; + struct vnode **vpp; + int *exflagsp; + struct ucred **credanonp; +{ + struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; + struct defid *defhp = (struct defid *) fhp; + struct denode *dep; + struct netcred *np; + int error; + + np = vfs_export_lookup(mp, &pmp->pm_export, nam); + if (np == NULL) + return EACCES; + error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, + NULL, &dep); + if (error) { + *vpp = NULLVP; + return error; + } + *vpp = DETOV(dep); + *exflagsp = np->netc_exflags; + *credanonp = &np->netc_anon; + return 0; +} + + +int +msdosfs_vptofh(vp, fhp) + struct vnode *vp; + struct fid *fhp; +{ + struct denode *dep = VTODE(vp); + struct defid *defhp = (struct defid *) fhp; + + defhp->defid_len = sizeof(struct defid); + defhp->defid_dirclust = dep->de_dirclust; + defhp->defid_dirofs = dep->de_diroffset; + /* defhp->defid_gen = ip->i_gen; */ + return 0; +} + +int +msdosfs_vget(mp, ino, vpp) + struct mount *mp; + ino_t ino; + struct vnode **vpp; +{ + return EOPNOTSUPP; +} + +struct vfsops msdosfs_vfsops = { + msdosfs_mount, + msdosfs_start, + msdosfs_unmount, + msdosfs_root, + msdosfs_quotactl, + msdosfs_statfs, + msdosfs_sync, + msdosfs_vget, + msdosfs_fhtovp, + msdosfs_vptofh, + msdosfs_init +}; diff --git a/sys/fs/msdosfs/msdosfs_vnops.c b/sys/fs/msdosfs/msdosfs_vnops.c new file mode 100644 index 0000000..4294034 --- /dev/null +++ b/sys/fs/msdosfs/msdosfs_vnops.c @@ -0,0 +1,1933 @@ +/* $Id$ */ +/* $NetBSD: msdosfs_vnops.c,v 1.20 1994/08/21 18:44:13 ws Exp $ */ + +/*- + * Copyright (C) 1994 Wolfgang Solfrank. + * Copyright (C) 1994 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 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. + * + * October 1992 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/namei.h> +#include <sys/resourcevar.h> /* defines plimit structure in proc struct */ +#include <sys/kernel.h> +#include <sys/file.h> /* define FWRITE ... */ +#include <sys/stat.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/mount.h> +#include <sys/vnode.h> +#include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */ +#include <sys/malloc.h> +#include <sys/dir.h> /* defines dirent structure */ + +#include <msdosfs/bpb.h> +#include <msdosfs/direntry.h> +#include <msdosfs/denode.h> +#include <msdosfs/msdosfsmount.h> +#include <msdosfs/fat.h> +/* + * Some general notes: + * + * 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 cnp->cn_pnbuf, always on error, or + * only if the SAVESTART bit in cn_flags is clear on success. + */ +int +msdosfs_create(ap) + struct vop_create_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; +{ + struct componentname *cnp = ap->a_cnp; + struct denode ndirent; + struct denode *dep; + struct denode *pdep = VTODE(ap->a_dvp); + struct timespec ts; + int error; + +#ifdef MSDOSFS_DEBUG + printf("msdosfs_create(cnp %08x, vap %08x\n", cnp, ap->a_vap); +#endif + + /* + * 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. + */ +#ifdef DIAGNOSTIC + if ((cnp->cn_flags & SAVENAME) == 0) + panic("msdosfs_create: no name"); +#endif + bzero(&ndirent, sizeof(ndirent)); + TIMEVAL_TO_TIMESPEC(&time, &ts); + unix2dostime(&ts, &ndirent.de_Date, &ndirent.de_Time); + unix2dosfn((u_char *)cnp->cn_nameptr, ndirent.de_Name, cnp->cn_namelen); + ndirent.de_Attributes = (ap->a_vap->va_mode & VWRITE) ? 0 : ATTR_READONLY; + ndirent.de_StartCluster = 0; + ndirent.de_FileSize = 0; + ndirent.de_dev = pdep->de_dev; + ndirent.de_devvp = pdep->de_devvp; + if ((error = createde(&ndirent, pdep, &dep)) == 0) { + *ap->a_vpp = DETOV(dep); + if ((cnp->cn_flags & SAVESTART) == 0) + free(cnp->cn_pnbuf, M_NAMEI); + } else { + free(cnp->cn_pnbuf, M_NAMEI); + } + vput(ap->a_dvp); /* release parent dir */ + return error; +} + +int +msdosfs_mknod(ap) + struct vop_mknod_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; +{ + int error; + struct denode *pdep = VTODE(ap->a_dvp); + + switch (ap->a_vap->va_type) { + case VDIR: + error = msdosfs_mkdir((struct vop_mkdir_args *)ap); + break; + + case VREG: + error = msdosfs_create((struct vop_create_args *)ap); + break; + + default: + error = EINVAL; + free(ap->a_cnp->cn_pnbuf, M_NAMEI); + vput(ap->a_dvp); + break; + } + return error; +} + +int +msdosfs_open(ap) + struct vop_open_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + return 0; +} + +int +msdosfs_close(ap) + struct vop_close_args /* { + struct vnode *a_vp; + int a_fflag; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct denode *dep = VTODE(vp); + struct timespec ts; + + if (vp->v_usecount > 1 && !(dep->de_flag & DE_LOCKED)) { + TIMEVAL_TO_TIMESPEC(&time, &ts); + DE_TIMES(dep, &ts); + } + return 0; +} + +/* + * This routine will go into sys/kern/vfs_subr.c one day! + * + * Do the usual access checking. + * file_node, uid and gid are from the vnode in question, + * while acc_mode and cred are from the VOP_ACCESS parameter list. + */ +static int +vaccess(file_mode, uid, gid, acc_mode, cred) + mode_t file_mode; + uid_t uid; + gid_t gid; + mode_t acc_mode; + struct ucred *cred; +{ + mode_t mask; + int i; + register gid_t *gp; + + /* User id 0 always gets access. */ + if (cred->cr_uid == 0) + return 0; + + mask = 0; + + /* Otherwise, check the owner. */ + if (cred->cr_uid == uid) { + if (acc_mode & VEXEC) + mask |= S_IXUSR; + if (acc_mode & VREAD) + mask |= S_IRUSR; + if (acc_mode & VWRITE) + mask |= S_IWUSR; + return (file_mode & mask) == mask ? 0 : EACCES; + } + + /* Otherwise, check the groups. */ + for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) + if (gid == *gp) { + if (acc_mode & VEXEC) + mask |= S_IXGRP; + if (acc_mode & VREAD) + mask |= S_IRGRP; + if (acc_mode & VWRITE) + mask |= S_IWGRP; + return (file_mode & mask) == mask ? 0 : EACCES; + } + + /* Otherwise, check everyone else. */ + if (acc_mode & VEXEC) + mask |= S_IXOTH; + if (acc_mode & VREAD) + mask |= S_IROTH; + if (acc_mode & VWRITE) + mask |= S_IWOTH; + return (file_mode & mask) == mask ? 0 : EACCES; +} + +int +msdosfs_access(ap) + struct vop_access_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + mode_t dosmode; + struct denode *dep = VTODE(ap->a_vp); + struct msdosfsmount *pmp = dep->de_pmp; + + dosmode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) | + ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH)); + dosmode &= pmp->pm_mask; + + return vaccess(dosmode, pmp->pm_uid, pmp->pm_gid, ap->a_mode, ap->a_cred); +} + +int +msdosfs_getattr(ap) + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + u_int cn; + struct denode *dep = VTODE(ap->a_vp); + struct vattr *vap = ap->a_vap; + struct timespec ts; + + TIMEVAL_TO_TIMESPEC(&time, &ts); + DE_TIMES(dep, &ts); + 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 + * doesn't work. + */ + if (dep->de_Attributes & ATTR_DIRECTORY) { + if ((cn = dep->de_StartCluster) == MSDOSFSROOT) + cn = 1; + } else { + if ((cn = dep->de_dirclust) == MSDOSFSROOT) + cn = 1; + cn = (cn << 16) | (dep->de_diroffset & 0xffff); + } + vap->va_fileid = cn; + vap->va_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) | + ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH)); + vap->va_mode &= dep->de_pmp->pm_mask; + if (dep->de_Attributes & ATTR_DIRECTORY) + vap->va_mode |= S_IFDIR; + vap->va_nlink = 1; + vap->va_gid = dep->de_pmp->pm_gid; + vap->va_uid = dep->de_pmp->pm_uid; + vap->va_rdev = 0; + vap->va_size = dep->de_FileSize; + dos2unixtime(dep->de_Date, dep->de_Time, &vap->va_atime); + vap->va_mtime = vap->va_atime; +#ifndef MSDOSFS_NODIRMOD + if (vap->va_mode & S_IFDIR) + TIMEVAL_TO_TIMESPEC(&time, &vap->va_mtime); +#endif + vap->va_ctime = vap->va_atime; + vap->va_flags = dep->de_flag; + vap->va_gen = 0; + 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 = ap->a_vp->v_type; + return 0; +} + +int +msdosfs_setattr(ap) + struct vop_setattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + int error = 0; + struct denode *dep = VTODE(ap->a_vp); + struct vattr *vap = ap->a_vap; + struct ucred *cred = ap->a_cred; + +#ifdef MSDOSFS_DEBUG + printf("msdosfs_setattr(): vp %08x, vap %08x, cred %08x, p %08x\n", + ap->a_vp, vap, cred, ap->a_p); +#endif + 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 != VNOVAL) || + (vap->va_gid != VNOVAL)) { +#ifdef MSDOSFS_DEBUG + 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\n", + vap->va_uid, vap->va_gid); +#endif + return EINVAL; + } + + if (vap->va_size != VNOVAL) { + if (ap->a_vp->v_type == VDIR) + return EISDIR; + if (error = detrunc(dep, vap->va_size, 0, cred, ap->a_p)) + return error; + } + if (vap->va_mtime.ts_sec != VNOVAL) { + dep->de_flag |= DE_UPDATE; + if (error = deupdat(dep, &vap->va_mtime, 1)) + return error; + } + + /* + * 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; + else + dep->de_Attributes |= ATTR_READONLY; + dep->de_flag |= DE_UPDATE; + } + + if (vap->va_flags != VNOVAL) { + if (error = suser(cred, &ap->a_p->p_acflag)) + return error; + if (cred->cr_uid == 0) + dep->de_flag = vap->va_flags; + else { + dep->de_flag &= 0xffff0000; + dep->de_flag |= (vap->va_flags & 0xffff); + } + dep->de_flag |= DE_UPDATE; + } + return error; +} + +int +msdosfs_read(ap) + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; +{ + int error = 0; + int diff; + int isadir; + long n; + long on; + daddr_t bn; + daddr_t lbn; + daddr_t rablock; + int rasize; + struct buf *bp; + struct vnode *vp = ap->a_vp; + struct denode *dep = VTODE(vp); + struct msdosfsmount *pmp = dep->de_pmp; + struct uio *uio = ap->a_uio; + + /* + * If they didn't ask for any data, then we are done. + */ + if (uio->uio_resid == 0) + return 0; + if (uio->uio_offset < 0) + return EINVAL; + + isadir = dep->de_Attributes & ATTR_DIRECTORY; + do { + 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; + if (diff <= 0) + return 0; + /* convert cluster # to block # if a directory */ + if (isadir) { + error = pcbmap(dep, lbn, &lbn, 0); + if (error) + return error; + } + if (diff < n) + n = diff; + /* + * 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. + */ + if (isadir) { + error = bread(pmp->pm_devvp, lbn, pmp->pm_bpcluster, + NOCRED, &bp); + } else { + rablock = lbn + 1; + if (vp->v_lastr + 1 == lbn && + rablock * pmp->pm_bpcluster < dep->de_FileSize) { + rasize = pmp->pm_bpcluster; + error = breadn(vp, lbn, pmp->pm_bpcluster, + &rablock, &rasize, 1, + NOCRED, &bp); + } else { + error = bread(vp, lbn, pmp->pm_bpcluster, NOCRED, + &bp); + } + vp->v_lastr = lbn; + } + n = min(n, pmp->pm_bpcluster - bp->b_resid); + if (error) { + brelse(bp); + return error; + } + error = uiomove(bp->b_data + 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 0 + if (n + on == pmp->pm_bpcluster || + uio->uio_offset == dep->de_FileSize) + bp->b_flags |= B_AGE; +#endif + brelse(bp); + } while (error == 0 && uio->uio_resid > 0 && n != 0); + return error; +} + +/* + * Write data to a file or directory. + */ +int +msdosfs_write(ap) + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; +{ + int n; + int isadir; + int croffset; + int resid; + int osize; + int error = 0; + u_long count; + daddr_t bn, lastcn; + struct buf *bp; + int ioflag = ap->a_ioflag; + struct uio *uio = ap->a_uio; + struct proc *p = uio->uio_procp; + struct vnode *vp = ap->a_vp; + struct vnode *thisvp; + struct denode *dep = VTODE(vp); + struct msdosfsmount *pmp = dep->de_pmp; + struct ucred *cred = ap->a_cred; + +#ifdef MSDOSFS_DEBUG + printf("msdosfs_write(vp %08x, uio %08x, ioflag %08x, cred %08x\n", + vp, uio, ioflag, cred); + printf("msdosfs_write(): diroff %d, dirclust %d, startcluster %d\n", + dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster); +#endif + + switch (vp->v_type) { + case VREG: + if (ioflag & IO_APPEND) + uio->uio_offset = dep->de_FileSize; + isadir = 0; + thisvp = vp; + break; + + case VDIR: + if ((ioflag & IO_SYNC) == 0) + panic("msdosfs_write(): non-sync directory update"); + isadir = 1; + thisvp = pmp->pm_devvp; + break; + + default: + panic("msdosfs_write(): bad file type"); + break; + } + + if (uio->uio_offset < 0) + return EINVAL; + + if (uio->uio_resid == 0) + return 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)) { + psignal(p, SIGXFSZ); + return EFBIG; + } + + /* + * 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) + return ENOSPC; + + /* + * 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. + */ + if (uio->uio_offset > dep->de_FileSize) { + error = deextend(dep, uio->uio_offset, cred); + if (error) + return error; + } + + /* + * Remember some values in case the write fails. + */ + resid = uio->uio_resid; + osize = dep->de_FileSize; + + + /* + * If we write beyond the end of the file, extend it to its ultimate + * size ahead of the time to hopefully get a contiguous area. + */ + if (uio->uio_offset + resid > osize) { + count = de_clcount(pmp, uio->uio_offset + resid) - de_clcount(pmp, osize); + if ((error = extendfile(dep, count, NULL, NULL, 0)) + && (error != ENOSPC || (ioflag & IO_UNIT))) + goto errexit; + lastcn = dep->de_fc[FC_LASTFC].fc_frcn; + } else + lastcn = de_clcount(pmp, osize) - 1; + + do { + bn = de_blk(pmp, uio->uio_offset); + if (isadir) { + if (error = pcbmap(dep, bn, &bn, 0)) + break; + } else if (bn > lastcn) { + error = ENOSPC; + break; + } + + if ((uio->uio_offset & pmp->pm_crbomask) == 0 + && (de_blk(pmp, uio->uio_offset + uio->uio_resid) > de_blk(pmp, uio->uio_offset) + || uio->uio_offset + uio->uio_resid >= dep->de_FileSize)) { + /* + * If either the whole cluster gets written, + * or we write the cluster from its start beyond EOF, + * then no need to read data from disk. + */ + bp = getblk(thisvp, bn, pmp->pm_bpcluster, 0, 0); + clrbuf(bp); + /* + * Do the bmap now, since pcbmap needs buffers + * for the fat table. (see msdosfs_strategy) + */ + if (!isadir) { + if (bp->b_blkno == bp->b_lblkno) { + if (error = pcbmap(dep, bp->b_lblkno, + &bp->b_blkno, 0)) + bp->b_blkno = -1; + } + if (bp->b_blkno == -1) { + brelse(bp); + if (!error) + error = EIO; /* XXX */ + break; + } + } + } else { + /* + * The block we need to write into exists, so read it in. + */ + if (error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp)) + break; + } + + 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_data + 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 (ioflag & IO_SYNC) + (void) bwrite(bp); + else if (n + croffset == pmp->pm_bpcluster) { + bp->b_flags |= B_AGE; + bawrite(bp); + } else + bdwrite(bp); + dep->de_flag |= DE_UPDATE; + } 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. + */ +errexit: + if (error) { + if (ioflag & IO_UNIT) { + detrunc(dep, osize, ioflag & IO_SYNC, NOCRED, NULL); + uio->uio_offset -= resid - uio->uio_resid; + uio->uio_resid = resid; + } else { + detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED, NULL); + if (uio->uio_resid != resid) + error = 0; + } + } else { + error = deupdat(dep, &time, 1); + } + return error; +} + +int +msdosfs_ioctl(ap) + struct vop_ioctl_args /* { + struct vnode *a_vp; + int a_command; + caddr_t a_data; + int a_fflag; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + return ENOTTY; +} + +int +msdosfs_select(ap) + struct vop_select_args /* { + struct vnode *a_vp; + int a_which; + int a_fflags; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + return 1; /* DOS filesystems never block? */ +} + +int +msdosfs_mmap(ap) + struct vop_mmap_args /* { + struct vnode *a_vp; + int a_fflags; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + return EINVAL; +} + +/* + * 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. + */ +int +msdosfs_fsync(ap) + struct vop_fsync_args /* { + struct vnode *a_vp; + struct ucred *a_cred; + int a_waitfor; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + int wait = ap->a_waitfor == MNT_WAIT; + int error; + +#if 0 + /* + * 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, wait ? B_SYNC : 0); +#endif + return deupdat(VTODE(vp), &time, wait); +} + +/* + * Now the whole work of extending a file is done in the write function. + * So nothing to do here. + */ +int +msdosfs_seek(ap) + struct vop_seek_args /* { + struct vnode *a_vp; + off_t a_oldoff; + off_t a_newoff; + struct ucred *a_cred; + } */ *ap; +{ + return 0; +} + +int +msdosfs_remove(ap) + struct vop_remove_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap; +{ + int error; + struct denode *dep = VTODE(ap->a_vp); + struct denode *ddep = VTODE(ap->a_dvp); + + error = removede(ddep,dep); +#ifdef MSDOSFS_DEBUG + printf("msdosfs_remove(), dep %08x, v_usecount %d\n", dep, ap->a_vp->v_usecount); +#endif + if (ddep == dep) + vrele(ap->a_vp); + else + vput(ap->a_vp); /* causes msdosfs_inactive() to be called + * via vrele() */ + vput(ap->a_dvp); + return error; +} + +/* + * 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. + */ +int +msdosfs_link(ap) + struct vop_link_args /* { + struct vnode *a_vp; + struct vnode *a_tdvp; + struct componentname *a_cnp; + } */ *ap; +{ + return VOP_ABORTOP(ap->a_vp, ap->a_cnp); +} + +/* + * 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 + * componentname structures is freed, there may be some memory bleeding + * for each rename done. + */ +int +msdosfs_rename(ap) + struct vop_rename_args /* { + struct vnode *a_fdvp; + struct vnode *a_fvp; + struct componentname *a_fcnp; + struct vnode *a_tdvp; + struct vnode *a_tvp; + struct componentname *a_tcnp; + } */ *ap; +{ + u_char toname[11]; + int error; + int newparent = 0; + int sourceisadirectory = 0; + u_long to_dirclust; + u_long to_diroffset; + u_long cn; + daddr_t bn; + struct vnode *tvp = ap->a_tvp; + 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; + struct direntry *ep; + struct buf *bp; + + fddep = VTODE(ap->a_fdvp); + fdep = VTODE(ap->a_fvp); + tddep = VTODE(ap->a_tdvp); + tdep = tvp ? VTODE(tvp) : NULL; + pmp = fddep->de_pmp; + +#ifdef __NetBSD__ + /* Check for cross-device rename */ + if ((ap->a_fvp->v_mount != ap->a_tdvp->v_mount) || + (tvp && (ap->a_fvp->v_mount != tvp->v_mount))) { + error = EXDEV; + goto bad; + } +#endif + + /* + * Convert the filename in tcnp into a dos filename. We copy this + * into the denode and directory entry for the destination + * file/directory. + */ + unix2dosfn((u_char *) ap->a_tcnp->cn_nameptr, + toname, ap->a_tcnp->cn_namelen); + + /* + * At this point this is the lock state of the denodes: + * fddep referenced + * fdep referenced + * tddep locked + * 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 ((ap->a_fcnp->cn_namelen == 1 + && ap->a_fcnp->cn_nameptr[0] == '.') + || fddep == fdep + || (ap->a_fcnp->cn_flags & ISDOTDOT)) { + VOP_ABORTOP(ap->a_tdvp, ap->a_tcnp); + vput(ap->a_tdvp); + if (tvp) + vput(tvp); + VOP_ABORTOP(ap->a_fdvp, ap->a_fcnp); + vrele(ap->a_fdvp); + vrele(ap->a_fvp); + return EINVAL; + } + sourceisadirectory = 1; + } + + /* + * 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) + newparent = 1; + if (sourceisadirectory && newparent) { + if (tdep) { + vput(ap->a_tvp); + tdep = NULL; + } + /* doscheckpath() vput()'s tddep */ + error = doscheckpath(fdep, tddep); + tddep = NULL; + if (error) { + goto bad; + } + if ((ap->a_tcnp->cn_flags & SAVESTART) == 0) + panic("msdosfs_rename(): lost to startdir"); + if (error = relookup(ap->a_tdvp, &tvp, ap->a_tcnp)) { + goto bad; + } + tddep = VTODE(ap->a_tdvp); + tdep = tvp ? VTODE(tvp) : 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) { + if (tdep->de_Attributes & ATTR_DIRECTORY) { + if (!sourceisadirectory) { + error = ENOTDIR; + goto bad; + } + if (!dosdirempty(tdep)) { + error = ENOTEMPTY; + goto bad; + } + } else { /* destination is file */ + if (sourceisadirectory) { + error = EISDIR; + goto bad; + } + } + to_dirclust = tdep->de_dirclust; + to_diroffset = tdep->de_diroffset; + if (error = removede(tddep,tdep)) { + goto bad; + } + vput(ap->a_tvp); + tdep = NULL; + + /* + * Remember where the slot was for createde(). + */ + tddep->de_fndclust = to_dirclust; + tddep->de_fndoffset = 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. + */ + if (newparent == 0) { + /* tddep and fddep point to the same denode here */ + VOP_LOCK(ap->a_fvp); /* ap->a_fdvp is already locked */ + if (error = readep(fddep->de_pmp, + fdep->de_dirclust, + fdep->de_diroffset, + &bp, &ep)) { + VOP_UNLOCK(ap->a_fvp); + goto bad; + } + bcopy(toname, ep->deName, 11); + if (error = bwrite(bp)) { + VOP_UNLOCK(ap->a_fvp); + goto bad; + } + bcopy(toname, fdep->de_Name, 11); /* update denode */ + /* + * fdep locked fddep and tddep point to the same denode + * which is locked tdep is NULL + */ + } else { + u_long dirsize = 0L; + + /* + * 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 + * filesize of zero. + */ + VOP_LOCK(ap->a_fvp); + bcopy(toname, fdep->de_Name, 11); /* update denode */ + if (fdep->de_Attributes & ATTR_DIRECTORY) { + dirsize = fdep->de_FileSize; + fdep->de_FileSize = 0; + } + error = createde(fdep, tddep, (struct denode **) 0); + if (fdep->de_Attributes & ATTR_DIRECTORY) { + fdep->de_FileSize = dirsize; + } + if (error) { + /* should put back filename */ + VOP_UNLOCK(ap->a_fvp); + goto bad; + } + VOP_LOCK(ap->a_fdvp); + if (error = readep(fddep->de_pmp, + fddep->de_fndclust, + fddep->de_fndoffset, + &bp, &ep)) { + VOP_UNLOCK(ap->a_fvp); + VOP_UNLOCK(ap->a_fdvp); + goto bad; + } + ep->deName[0] = SLOT_DELETED; + if (error = bwrite(bp)) { + VOP_UNLOCK(ap->a_fvp); + VOP_UNLOCK(ap->a_fdvp); + goto bad; + } + fdep->de_dirclust = tddep->de_fndclust; + fdep->de_diroffset = tddep->de_fndoffset; + reinsert(fdep); + VOP_UNLOCK(ap->a_fdvp); + } + /* 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; + if (cn == MSDOSFSROOT) { + /* this should never happen */ + panic("msdosfs_rename(): updating .. in root directory?\n"); + } else { + bn = cntobn(pmp, cn); + } + error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, + NOCRED, &bp); + if (error) { + /* should really panic here, fs is corrupt */ + VOP_UNLOCK(ap->a_fvp); + goto bad; + } + dotdotp = (struct direntry *) bp->b_data + 1; + putushort(dotdotp->deStartCluster, tddep->de_StartCluster); + error = bwrite(bp); + VOP_UNLOCK(ap->a_fvp); + if (error) { + /* should really panic here, fs is corrupt */ + goto bad; + } + } else + VOP_UNLOCK(ap->a_fvp); +bad: ; + vrele(DETOV(fdep)); + vrele(DETOV(fddep)); + if (tdep) + vput(DETOV(tdep)); + if (tddep) + vput(DETOV(tddep)); + return error; +} + +struct { + struct direntry dot; + struct direntry dotdot; +} dosdirtemplate = { + + ". ", " ", /* the . entry */ + ATTR_DIRECTORY, /* file attribute */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* resevered */ + 210, 4, 210, 4, /* time and date */ + 0, 0, /* startcluster */ + 0, 0, 0, 0, /* filesize */ + ".. ", " ", /* the .. entry */ + ATTR_DIRECTORY, /* file attribute */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* resevered */ + 210, 4, 210, 4, /* time and date */ + 0, 0, /* startcluster */ + 0, 0, 0, 0, /* filesize */ +}; + +int +msdosfs_mkdir(ap) + struct vop_mkdir_args /* { + struct vnode *a_dvp; + struvt vnode **a_vpp; + struvt componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; +{ + int bn; + int error; + u_long newcluster; + struct denode *pdep; + struct denode *ndep; + struct direntry *denp; + struct denode ndirent; + struct msdosfsmount *pmp; + struct buf *bp; + struct timespec ts; + u_short dDate, dTime; + + pdep = VTODE(ap->a_dvp); + + /* + * 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 + * change size. + */ + if (pdep->de_StartCluster == MSDOSFSROOT && pdep->de_fndclust == (u_long)-1) { + free(ap->a_cnp->cn_pnbuf, M_NAMEI); + vput(ap->a_dvp); + return ENOSPC; + } + + pmp = pdep->de_pmp; + + /* + * Allocate a cluster to hold the about to be created directory. + */ + if (error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL)) { + free(ap->a_cnp->cn_pnbuf, M_NAMEI); + vput(ap->a_dvp); + return error; + } + + /* + * 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); + /* always succeeds */ + bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0); + bzero(bp->b_data, pmp->pm_bpcluster); + bcopy(&dosdirtemplate, bp->b_data, sizeof dosdirtemplate); + denp = (struct direntry *) bp->b_data; + putushort(denp->deStartCluster, newcluster); + TIMEVAL_TO_TIMESPEC(&time, &ts); + unix2dostime(&ts, &dDate, &dTime); + putushort(denp->deDate, dDate); + putushort(denp->deTime, dTime); + denp++; + putushort(denp->deStartCluster, pdep->de_StartCluster); + putushort(denp->deDate, dDate); + putushort(denp->deTime, dTime); + if (error = bwrite(bp)) { + clusterfree(pmp, newcluster, NULL); + free(ap->a_cnp->cn_pnbuf, M_NAMEI); + vput(ap->a_dvp); + return error; + } + + /* + * Now build up a directory entry pointing to the newly allocated + * cluster. This will be written to an empty slot in the parent + * directory. + */ + ndep = &ndirent; + bzero(ndep, sizeof(*ndep)); + unix2dosfn((u_char *)ap->a_cnp->cn_nameptr, + ndep->de_Name, ap->a_cnp->cn_namelen); + TIMEVAL_TO_TIMESPEC(&time, &ts); + unix2dostime(&ts, &ndep->de_Date, &ndep->de_Time); + ndep->de_StartCluster = newcluster; + ndep->de_Attributes = ATTR_DIRECTORY; + + error = createde(ndep, pdep, &ndep); + if (error) { + clusterfree(pmp, newcluster, NULL); + } else { + *ap->a_vpp = DETOV(ndep); + } + free(ap->a_cnp->cn_pnbuf, M_NAMEI); +#ifdef MSDOSFS_DEBUG + printf("msdosfs_mkdir(): vput(%08x)\n", ap->a_dvp); +#endif + vput(ap->a_dvp); + return error; +} + +int +msdosfs_rmdir(ap) + struct vop_rmdir_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap; +{ + struct denode *ddep; + struct denode *dep; + int error = 0; + + ddep = VTODE(ap->a_dvp); /* parent dir of dir to delete */ + dep = VTODE(ap->a_vp);/* directory to delete */ + + /* + * Don't let "rmdir ." go thru. + */ + if (ddep == dep) { + vrele(ap->a_vp); + vput(ap->a_vp); + return EINVAL; + } + + /* + * Be sure the directory being deleted is empty. + */ + if (dosdirempty(dep) == 0) { + error = ENOTEMPTY; + goto out; + } + + /* + * 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 vput() 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(ddep,dep)) + goto out; + + /* + * 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)); + vput(ap->a_dvp); + ap->a_dvp = NULL; + + /* + * Truncate the directory that is being deleted. + */ + error = detrunc(dep, (u_long) 0, IO_SYNC); + cache_purge(DETOV(dep)); + +out: ; + if (ap->a_dvp) + vput(ap->a_dvp); + vput(ap->a_vp); + return error; +} + +/* + * DOS filesystems don't know what symlinks are. + */ +int +msdosfs_symlink(ap) + struct vop_symlink_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + char *a_target; + } */ *ap; +{ + struct denode *pdep = VTODE(ap->a_dvp); + + free(ap->a_cnp->cn_pnbuf, M_NAMEI); + vput(ap->a_dvp); + return EINVAL; +} + +/* + * 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). + */ +struct dos_dirent { + u_long d_fileno; + u_short d_reclen; + u_char d_type; + u_char d_namlen; + u_char d_name[24]; +} rootdots[2] = { + + { + 1, /* d_fileno */ + sizeof(struct direntry), /* d_reclen */ + DT_DIR, /* d_type */ + 1, /* d_namlen */ + "." /* d_name */ + }, + { + 1, /* d_fileno */ + sizeof(struct direntry), /* d_reclen */ + DT_DIR, /* d_type */ + 2, /* d_namlen */ + ".." /* d_name */ + } +}; + +int +msdosfs_readdir(ap) + struct vop_readdir_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + int *a_eofflag; + u_int *a_cookies; + int a_ncookies; + } */ *ap; +{ + int error = 0; + int diff; + char pushout; + long n; + long on; + long lost; + long count; + u_long cn; + u_long fileno; + long bias = 0; + daddr_t bn; + daddr_t lbn; + struct buf *bp; + struct denode *dep = VTODE(ap->a_vp); + struct msdosfsmount *pmp = dep->de_pmp; + struct direntry *dentp; + struct dirent *prev; + struct dirent *crnt; + u_char dirbuf[512]; /* holds converted dos directories */ + int i = 0; + struct uio *uio = ap->a_uio; + int ncookies = 1; + u_int* cookies = NULL; + +#ifdef MSDOSFS_DEBUG + printf("msdosfs_readdir(): vp %08x, uio %08x, cred %08x, eofflagp %08x\n", + ap->a_vp, uio, ap->a_cred, ap->a_eofflag); +#endif + +#if 0 + if (!ap->a_cookies) + ncookies = 1; +#endif + + /* + * 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) + return ENOTDIR; + + /* + * 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))) + return EINVAL; + uio->uio_resid = count; + 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)) { + if (uio->uio_offset + && uio->uio_offset != sizeof(struct direntry)) { + error = EINVAL; + goto out; + } + n = 1; + if (!uio->uio_offset) { + n = 2; + if (cookies) { + *cookies++ = sizeof(struct direntry); + ncookies--; + } + } + if (cookies) { + if (ncookies-- <= 0) + n--; + else + *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); + if (diff <= 0) + return 0; + if (diff < n) + n = diff; + error = pcbmap(dep, lbn, &bn, &cn); + if (error) + break; + error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp); + n = min(n, pmp->pm_bpcluster - bp->b_resid); + if (error) { + brelse(bp); + return error; + } + + /* + * code to convert from dos directory entries to ufs + * directory entries + */ + pushout = 0; + dentp = (struct direntry *)(bp->b_data + on); + prev = 0; + crnt = (struct dirent *) dirbuf; + while ((char *) dentp < bp->b_data + 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)) { + if (prev) { + prev->d_reclen += sizeof(struct direntry); + if (cookies) { + ncookies++; + cookies--; + } + } else { + prev = crnt; + prev->d_fileno = 0; + prev->d_reclen = sizeof(struct direntry); + prev->d_type = DT_UNKNOWN; + prev->d_namlen = 0; + prev->d_name[0] = 0; + } + } else { + /* + * this computation of d_fileno must match + * the computation of va_fileid in + * msdosfs_getattr + */ + if (dentp->deAttributes & ATTR_DIRECTORY) { + /* if this is the root directory */ + fileno = getushort(dentp->deStartCluster); + if (fileno == MSDOSFSROOT) + fileno = 1; + } else { + /* + * if the file's dirent lives in + * root dir + */ + if ((fileno = cn) == MSDOSFSROOT) + fileno = 1; + fileno = (fileno << 16) | + ((dentp - (struct direntry *) bp->b_data) & 0xffff); + } + crnt->d_fileno = fileno; + crnt->d_reclen = sizeof(struct direntry); + crnt->d_type = (dentp->deAttributes & ATTR_DIRECTORY) + ? DT_DIR : DT_REG; + 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); + */ + prev = crnt; + } + dentp++; + if (cookies) { + *cookies++ = (u_int)((char *)dentp - bp->b_data - on) + + uio->uio_offset; + ncookies--; + } + + crnt = (struct dirent *) ((char *) crnt + sizeof(struct direntry)); + pushout = 1; + + /* + * 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]) { + pushout = 0; + error = uiomove(dirbuf, sizeof(dirbuf), uio); + if (error) + break; + prev = 0; + crnt = (struct dirent *) dirbuf; + } + if (ncookies <= 0) + break; + } + if (pushout) { + pushout = 0; + error = uiomove(dirbuf, (char *) crnt - (char *) dirbuf, + uio); + } + +#if 0 + /* + * 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) + bp->b_flags |= B_AGE; +#endif /* if 0 */ + brelse(bp); + if (n == 0) + break; + } +out: ; + uio->uio_resid += lost; + +#if 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. + * (because NFS uses it in nfs_serv.c -- JMP) + */ + if (dep->de_FileSize - uio->uio_offset - bias <= 0) + *ap->a_eofflag = 1; + else + *ap->a_eofflag = 0; +#endif + + return error; +} + +/* + * DOS filesystems don't know what symlinks are. + */ +int +msdosfs_readlink(ap) + struct vop_readlink_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + } */ *ap; +{ + return EINVAL; +} + +int +msdosfs_abortop(ap) + struct vop_abortop_args /* { + struct vnode *a_dvp; + struct componentname *a_cnp; + } */ *ap; +{ + if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) + FREE(ap->a_cnp->cn_pnbuf, M_NAMEI); + return 0; +} + +int +msdosfs_lock(ap) + struct vop_lock_args /* { + struct vnode *a_vp; + } */ *ap; +{ + struct denode *dep = VTODE(ap->a_vp); + + while (dep->de_flag & DE_LOCKED) { + dep->de_flag |= DE_WANTED; + if (dep->de_lockholder == curproc->p_pid) + panic("msdosfs_lock: locking against myself"); + dep->de_lockwaiter = curproc->p_pid; + (void) sleep((caddr_t) dep, PINOD); + } + dep->de_lockwaiter = 0; + dep->de_lockholder = curproc->p_pid; + dep->de_flag |= DE_LOCKED; + return 0; +} + +int +msdosfs_unlock(ap) + struct vop_unlock_args /* { + struct vnode *vp; + } */ *ap; +{ + struct denode *dep = VTODE(ap->a_vp); + + if (!(dep->de_flag & DE_LOCKED)) + panic("msdosfs_unlock: denode not locked"); + dep->de_lockholder = 0; + dep->de_flag &= ~DE_LOCKED; + if (dep->de_flag & DE_WANTED) { + dep->de_flag &= ~DE_WANTED; + wakeup((caddr_t) dep); + } + return 0; +} + +int +msdosfs_islocked(ap) + struct vop_islocked_args /* { + struct vnode *a_vp; + } */ *ap; +{ + return VTODE(ap->a_vp)->de_flag & DE_LOCKED ? 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 number + */ +int +msdosfs_bmap(ap) + struct vop_bmap_args /* { + struct vnode *a_vp; + daddr_t a_bn; + struct vnode **a_vpp; + daddr_t *a_bnp; + int *a_runp; + } */ *ap; +{ + struct denode *dep = VTODE(ap->a_vp); + struct msdosfsmount *pmp = dep->de_pmp; + + if (ap->a_vpp != NULL) + *ap->a_vpp = dep->de_devvp; + if (ap->a_bnp == NULL) + return 0; + if (ap->a_runp) { + /* + * Sequential clusters should be counted here. + */ + *ap->a_runp = 0; + } + return pcbmap(dep, ap->a_bn, ap->a_bnp, 0); +} + +int msdosfs_reallocblks(ap) + struct vop_reallocblks_args /* { + struct vnode *a_vp; + struct cluster_save *a_buflist; + } */ *ap; +{ + /* Currently no support for clustering */ /* XXX */ + return ENOSPC; +} + +int +msdosfs_strategy(ap) + struct vop_strategy_args /* { + struct buf *a_bp; + } */ *ap; +{ + struct buf *bp = ap->a_bp; + struct denode *dep = VTODE(bp->b_vp); + struct msdosfsmount *pmp = dep->de_pmp; + struct vnode *vp; + int error = 0; + + 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)) + bp->b_blkno = -1; + if (bp->b_blkno == -1) + clrbuf(bp); + } + if (bp->b_blkno == -1) { + biodone(bp); + return error; + } +#ifdef DIAGNOSTIC +#endif + /* + * Read/write the block from/to the disk that contains the desired + * file block. + */ + vp = dep->de_devvp; + bp->b_dev = vp->v_rdev; + VOCALL(vp->v_op, VOFFSET(vop_strategy), ap); + return 0; +} + +int +msdosfs_print(ap) + struct vop_print_args /* { + struct vnode *vp; + } */ *ap; +{ + struct denode *dep = VTODE(ap->a_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 & DE_LOCKED ? "(LOCKED)" : ""); + if (dep->de_lockholder) { + printf(" owner pid %d", dep->de_lockholder); + if (dep->de_lockwaiter) + printf(" waiting pid %d", dep->de_lockwaiter); + printf("\n"); + } + return 0; +} + +int +msdosfs_advlock(ap) + struct vop_advlock_args /* { + struct vnode *a_vp; + caddr_t a_id; + int a_op; + struct flock *a_fl; + int a_flags; + } */ *ap; +{ + return EINVAL; /* we don't do locking yet */ +} + +int +msdosfs_pathconf(ap) + struct vop_pathconf_args /* { + struct vnode *a_vp; + int a_name; + int *a_retval; + } */ *ap; +{ + switch (ap->a_name) { + case _PC_LINK_MAX: + *ap->a_retval = 1; + return 0; + case _PC_NAME_MAX: + *ap->a_retval = 12; + return 0; + case _PC_PATH_MAX: + *ap->a_retval = PATH_MAX; /* 255? */ + return 0; + case _PC_CHOWN_RESTRICTED: + *ap->a_retval = 1; + return 0; + case _PC_NO_TRUNC: + *ap->a_retval = 0; + return 0; + default: + return EINVAL; + } +} + +/* Global vfs data structures for msdosfs */ +int (**msdosfs_vnodeop_p)(); +struct vnodeopv_entry_desc msdosfs_vnodeop_entries[] = { + { &vop_default_desc, vn_default_error }, + { &vop_lookup_desc, msdosfs_lookup }, /* lookup */ + { &vop_create_desc, msdosfs_create }, /* create */ + { &vop_mknod_desc, msdosfs_mknod }, /* mknod */ + { &vop_open_desc, msdosfs_open }, /* open */ + { &vop_close_desc, msdosfs_close }, /* close */ + { &vop_access_desc, msdosfs_access }, /* access */ + { &vop_getattr_desc, msdosfs_getattr }, /* getattr */ + { &vop_setattr_desc, msdosfs_setattr }, /* setattr */ + { &vop_read_desc, msdosfs_read }, /* read */ + { &vop_write_desc, msdosfs_write }, /* write */ + { &vop_ioctl_desc, msdosfs_ioctl }, /* ioctl */ + { &vop_select_desc, msdosfs_select }, /* select */ + { &vop_mmap_desc, msdosfs_mmap }, /* mmap */ + { &vop_fsync_desc, msdosfs_fsync }, /* fsync */ + { &vop_seek_desc, msdosfs_seek }, /* seek */ + { &vop_remove_desc, msdosfs_remove }, /* remove */ + { &vop_link_desc, msdosfs_link }, /* link */ + { &vop_rename_desc, msdosfs_rename }, /* rename */ + { &vop_mkdir_desc, msdosfs_mkdir }, /* mkdir */ + { &vop_rmdir_desc, msdosfs_rmdir }, /* rmdir */ + { &vop_symlink_desc, msdosfs_symlink }, /* symlink */ + { &vop_readdir_desc, msdosfs_readdir }, /* readdir */ + { &vop_readlink_desc, msdosfs_readlink }, /* readlink */ + { &vop_abortop_desc, msdosfs_abortop }, /* abortop */ + { &vop_inactive_desc, msdosfs_inactive }, /* inactive */ + { &vop_reclaim_desc, msdosfs_reclaim }, /* reclaim */ + { &vop_lock_desc, msdosfs_lock }, /* lock */ + { &vop_unlock_desc, msdosfs_unlock }, /* unlock */ + { &vop_bmap_desc, msdosfs_bmap }, /* bmap */ + { &vop_strategy_desc, msdosfs_strategy }, /* strategy */ + { &vop_print_desc, msdosfs_print }, /* print */ + { &vop_islocked_desc, msdosfs_islocked }, /* islocked */ + { &vop_pathconf_desc, msdosfs_pathconf }, /* pathconf */ + { &vop_advlock_desc, msdosfs_advlock }, /* advlock */ + { &vop_reallocblks_desc, msdosfs_reallocblks }, /* reallocblks */ + { &vop_bwrite_desc, vn_bwrite }, + { (struct vnodeop_desc *)NULL, (int (*)())NULL } +}; +struct vnodeopv_desc msdosfs_vnodeop_opv_desc = + { &msdosfs_vnodeop_p, msdosfs_vnodeop_entries }; diff --git a/sys/fs/msdosfs/msdosfsmount.h b/sys/fs/msdosfs/msdosfsmount.h new file mode 100644 index 0000000..62853c5 --- /dev/null +++ b/sys/fs/msdosfs/msdosfsmount.h @@ -0,0 +1,183 @@ +/* $Id$ */ +/* $NetBSD: msdosfsmount.h,v 1.7 1994/08/21 18:44:17 ws Exp $ */ + +/*- + * Copyright (C) 1994 Wolfgang Solfrank. + * Copyright (C) 1994 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 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. + * + * October 1992 + */ + +/* + * Layout of the mount control block for a msdos file system. + */ +struct msdosfsmount { + struct mount *pm_mountp;/* vfs mount struct for this fs */ + dev_t pm_dev; /* block special device mounted */ + uid_t pm_mounter; /* uid of the user who mounted the FS */ + uid_t pm_uid; /* uid to set as owner of the files */ + gid_t pm_gid; /* gid to set as owner of the files */ + mode_t pm_mask; /* mask to and with file protection bits */ + struct vnode *pm_devvp; /* vnode for block device mntd */ + struct bpb50 pm_bpb; /* BIOS parameter blk for this fs */ + u_long pm_fatblk; /* block # of first FAT */ + u_long pm_rootdirblk; /* block # of root directory */ + u_long pm_rootdirsize; /* size in blocks (not clusters) */ + u_long pm_firstcluster; /* block number of first cluster */ + u_long pm_nmbrofclusters; /* # of clusters in filesystem */ + u_long pm_maxcluster; /* maximum cluster number */ + u_long pm_freeclustercount; /* number of free clusters */ + u_long pm_bnshift; /* shift file offset right this amount to get a block number */ + u_long pm_brbomask; /* and a file offset with this mask to get block rel offset */ + u_long pm_cnshift; /* shift file offset right this amount to get a cluster number */ + u_long pm_crbomask; /* and a file offset with this mask to get cluster rel offset */ + u_long pm_bpcluster; /* bytes per cluster */ + u_long pm_depclust; /* directory entries per cluster */ + u_long pm_fmod; /* ~0 if fs is modified, this can rollover to 0 */ + u_long pm_fatblocksize; /* size of fat blocks in bytes */ + u_long pm_fatblocksec; /* size of fat blocks in sectors */ + u_long pm_fatsize; /* size of fat in bytes */ + u_int *pm_inusemap; /* ptr to bitmap of in-use clusters */ + char pm_ronly; /* read only if non-zero */ + char pm_waitonfat; /* wait for writes of the fat to complt, when 0 use bdwrite, else use bwrite */ + struct netexport pm_export; /* export information */ +}; + +/* Number of bits in one pm_inusemap item: */ +#define N_INUSEBITS (8 * sizeof(u_int)) + +/* + * How to compute pm_cnshift and pm_crbomask. + * + * pm_crbomask = (pm_SectPerClust * pm_BytesPerSect) - 1 + * if (bytesperclust == * 0) + * return EBADBLKSZ; + * bit = 1; + * for (i = 0; i < 32; i++) { + * if (bit & bytesperclust) { + * if (bit ^ bytesperclust) + * return EBADBLKSZ; + * pm_cnshift = * i; + * break; + * } + * bit <<= 1; + * } + */ + +/* + * Shorthand for fields in the bpb contained in the msdosfsmount structure. + */ +#define pm_BytesPerSec pm_bpb.bpbBytesPerSec +#define pm_SectPerClust pm_bpb.bpbSecPerClust +#define pm_ResSectors pm_bpb.bpbResSectors +#define pm_FATs pm_bpb.bpbFATs +#define pm_RootDirEnts pm_bpb.bpbRootDirEnts +#define pm_Sectors pm_bpb.bpbSectors +#define pm_Media pm_bpb.bpbMedia +#define pm_FATsecs pm_bpb.bpbFATsecs +#define pm_SecPerTrack pm_bpb.bpbSecPerTrack +#define pm_Heads pm_bpb.bpbHeads +#define pm_HiddenSects pm_bpb.bpbHiddenSecs +#define pm_HugeSectors pm_bpb.bpbHugeSectors + +/* + * Map a cluster number into a filesystem relative block number. + */ +#define cntobn(pmp, cn) \ + ((((cn)-CLUST_FIRST) * (pmp)->pm_SectPerClust) + (pmp)->pm_firstcluster) + +/* + * Map a filesystem relative block number back into a cluster number. + */ +#define bntocn(pmp, bn) \ + ((((bn) - pmp->pm_firstcluster)/ (pmp)->pm_SectPerClust) + CLUST_FIRST) + +/* + * Calculate block number for directory entry in root dir, offset dirofs + */ +#define roottobn(pmp, dirofs) \ + (((dirofs) / (pmp)->pm_depclust) * (pmp)->pm_SectPerClust \ + + (pmp)->pm_rootdirblk) + +/* + * Calculate block number for directory entry at cluster dirclu, offset + * dirofs + */ +#define detobn(pmp, dirclu, dirofs) \ + ((dirclu) == MSDOSFSROOT \ + ? roottobn((pmp), (dirofs)) \ + : cntobn((pmp), (dirclu))) + +/* + * Convert pointer to buffer -> pointer to direntry + */ +#define bptoep(pmp, bp, dirofs) \ + ((struct direntry *)((bp)->b_data) \ + + (dirofs) % (pmp)->pm_depclust) + + +/* + * Convert filesize to block number + */ +#define de_blk(pmp, off) \ + ((off) >> (pmp)->pm_cnshift) + +/* + * Clusters required to hold size bytes + */ +#define de_clcount(pmp, size) \ + (((size) + (pmp)->pm_bpcluster - 1) >> (pmp)->pm_cnshift) + +/* + * Prototypes for MSDOSFS virtual filesystem operations + */ +int msdosfs_mount __P((struct mount *, char *, caddr_t, struct nameidata *, struct proc *)); +int msdosfs_start __P((struct mount *, int, struct proc *)); +int msdosfs_unmount __P((struct mount *, int, struct proc *)); +int msdosfs_root __P((struct mount *, struct vnode **)); +int msdosfs_quotactl __P((struct mount *, int, uid_t, caddr_t, struct proc *)); +int msdosfs_statfs __P((struct mount *, struct statfs *, struct proc *)); +int msdosfs_sync __P((struct mount *, int, struct ucred *, struct proc *)); +int msdosfs_fhtovp __P((struct mount *, struct fid *, struct mbuf *, struct vnode **, int *, struct ucred **)); +int msdosfs_vptofh __P((struct vnode *, struct fid *)); +int msdosfs_init __P(()); diff --git a/sys/kern/kern_ntptime.c b/sys/kern/kern_ntptime.c index 9d4c1b9..e11628b 100644 --- a/sys/kern/kern_ntptime.c +++ b/sys/kern/kern_ntptime.c @@ -48,6 +48,7 @@ #include <sys/kernel.h> #include <sys/proc.h> #include <sys/timex.h> +#include <vm/vm.h> #include <sys/sysctl.h> /* diff --git a/sys/kern/vfs_conf.c b/sys/kern/vfs_conf.c index 70377ed..3480333 100644 --- a/sys/kern/vfs_conf.c +++ b/sys/kern/vfs_conf.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)vfs_conf.c 8.8 (Berkeley) 3/31/94 - * $Id: vfs_conf.c,v 1.2 1994/08/02 07:43:19 davidg Exp $ + * $Id: vfs_conf.c,v 1.3 1994/08/20 16:03:12 davidg Exp $ */ #include <sys/param.h> @@ -138,6 +138,13 @@ extern struct vfsops cd9660_vfsops; #define CD9660_VFSOPS NULL #endif +#ifdef MSDOSFS +extern struct vfsops msdosfs_vfsops; +#define MSDOSFS_VFSOPS &msdosfs_vfsops +#else +#define MSDOSFS_VFSOPS NULL +#endif + #ifdef UNION extern struct vfsops union_vfsops; #define UNION_VFSOPS &union_vfsops @@ -150,7 +157,7 @@ struct vfsops *vfssw[] = { UFS_VFSOPS, /* 1 = MOUNT_UFS */ NFS_VFSOPS, /* 2 = MOUNT_NFS */ MFS_VFSOPS, /* 3 = MOUNT_MFS */ - NULL, /* 4 = MOUNT_PC */ + MSDOSFS_VFSOPS, /* 4 = MOUNT_MSDOS */ LFS_VFSOPS, /* 5 = MOUNT_LFS */ NULL, /* 6 = MOUNT_LOFS */ FDESC_VFSOPS, /* 7 = MOUNT_FDESC */ @@ -195,6 +202,7 @@ extern struct vnodeopv_desc procfs_vnodeop_opv_desc; extern struct vnodeopv_desc cd9660_vnodeop_opv_desc; extern struct vnodeopv_desc cd9660_specop_opv_desc; extern struct vnodeopv_desc cd9660_fifoop_opv_desc; +extern struct vnodeopv_desc msdosfs_vnodeop_opv_desc; extern struct vnodeopv_desc union_vnodeop_opv_desc; struct vnodeopv_desc *vfs_opv_descs[] = { @@ -250,6 +258,9 @@ struct vnodeopv_desc *vfs_opv_descs[] = { &cd9660_fifoop_opv_desc, #endif #endif +#ifdef MSDOSFS + &msdosfs_vnodeop_opv_desc, +#endif #ifdef UNION &union_vnodeop_opv_desc, #endif diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c index 70377ed..3480333 100644 --- a/sys/kern/vfs_mount.c +++ b/sys/kern/vfs_mount.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)vfs_conf.c 8.8 (Berkeley) 3/31/94 - * $Id: vfs_conf.c,v 1.2 1994/08/02 07:43:19 davidg Exp $ + * $Id: vfs_conf.c,v 1.3 1994/08/20 16:03:12 davidg Exp $ */ #include <sys/param.h> @@ -138,6 +138,13 @@ extern struct vfsops cd9660_vfsops; #define CD9660_VFSOPS NULL #endif +#ifdef MSDOSFS +extern struct vfsops msdosfs_vfsops; +#define MSDOSFS_VFSOPS &msdosfs_vfsops +#else +#define MSDOSFS_VFSOPS NULL +#endif + #ifdef UNION extern struct vfsops union_vfsops; #define UNION_VFSOPS &union_vfsops @@ -150,7 +157,7 @@ struct vfsops *vfssw[] = { UFS_VFSOPS, /* 1 = MOUNT_UFS */ NFS_VFSOPS, /* 2 = MOUNT_NFS */ MFS_VFSOPS, /* 3 = MOUNT_MFS */ - NULL, /* 4 = MOUNT_PC */ + MSDOSFS_VFSOPS, /* 4 = MOUNT_MSDOS */ LFS_VFSOPS, /* 5 = MOUNT_LFS */ NULL, /* 6 = MOUNT_LOFS */ FDESC_VFSOPS, /* 7 = MOUNT_FDESC */ @@ -195,6 +202,7 @@ extern struct vnodeopv_desc procfs_vnodeop_opv_desc; extern struct vnodeopv_desc cd9660_vnodeop_opv_desc; extern struct vnodeopv_desc cd9660_specop_opv_desc; extern struct vnodeopv_desc cd9660_fifoop_opv_desc; +extern struct vnodeopv_desc msdosfs_vnodeop_opv_desc; extern struct vnodeopv_desc union_vnodeop_opv_desc; struct vnodeopv_desc *vfs_opv_descs[] = { @@ -250,6 +258,9 @@ struct vnodeopv_desc *vfs_opv_descs[] = { &cd9660_fifoop_opv_desc, #endif #endif +#ifdef MSDOSFS + &msdosfs_vnodeop_opv_desc, +#endif #ifdef UNION &union_vnodeop_opv_desc, #endif diff --git a/sys/msdosfs/bootsect.h b/sys/msdosfs/bootsect.h new file mode 100644 index 0000000..099b15f --- /dev/null +++ b/sys/msdosfs/bootsect.h @@ -0,0 +1,70 @@ +/* $Id$ */ +/* $NetBSD: bootsect.h,v 1.4 1994/06/29 06:35:28 cgd Exp $ */ + +/* + * 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. + * + * October 1992 + */ + +/* + * Format of a boot sector. This is the first sector on a DOS floppy disk + * or the fist sector of a partition on a hard disk. But, it is not the + * first sector of a partitioned hard disk. + */ +struct bootsector33 { + u_char bsJump[3]; /* jump instruction E9xxxx or EBxx90 */ + char bsOemName[8]; /* OEM name and version */ + char bsBPB[19]; /* BIOS parameter block */ + char bsDriveNumber; /* drive number (0x80) */ + char bsBootCode[474]; /* pad so structure is 512 bytes long */ + u_short bsBootSectSig; +#define BOOTSIG 0xaa55 +}; + +struct bootsector50 { + u_char bsJump[3]; /* jump instruction E9xxxx or EBxx90 */ + char bsOemName[8]; /* OEM name and version */ + char bsBPB[25]; /* BIOS parameter block */ + char bsDriveNumber; /* drive number (0x80) */ + char bsReserved1; /* reserved */ + char bsBootSignature; /* extended boot signature (0x29) */ +#define EXBOOTSIG 0x29 + char bsVolumeID[4]; /* volume ID number */ + char bsVolumeLabel[11]; /* volume label */ + char bsFileSysType[8]; /* file system type (FAT12 or FAT16) */ + char bsBootCode[448]; /* pad so structure is 512 bytes long */ + u_short bsBootSectSig; +#define BOOTSIG 0xaa55 +}; + +union bootsector { + struct bootsector33 bs33; + struct bootsector50 bs50; +}; + +/* + * Shorthand for fields in the bpb. + */ +#define bsBytesPerSec bsBPB.bpbBytesPerSec +#define bsSectPerClust bsBPB.bpbSectPerClust +#define bsResSectors bsBPB.bpbResSectors +#define bsFATS bsBPB.bpbFATS +#define bsRootDirEnts bsBPB.bpbRootDirEnts +#define bsSectors bsBPB.bpbSectors +#define bsMedia bsBPB.bpbMedia +#define bsFATsecs bsBPB.bpbFATsecs +#define bsSectPerTrack bsBPB.bpbSectPerTrack +#define bsHeads bsBPB.bpbHeads +#define bsHiddenSecs bsBPB.bpbHiddenSecs +#define bsHugeSectors bsBPB.bpbHugeSectors diff --git a/sys/msdosfs/bpb.h b/sys/msdosfs/bpb.h new file mode 100644 index 0000000..70e4de2 --- /dev/null +++ b/sys/msdosfs/bpb.h @@ -0,0 +1,120 @@ +/* $Id$ */ +/* $NetBSD: bpb.h,v 1.3 1994/06/29 06:35:29 cgd Exp $ */ + +/* + * 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. + * + * October 1992 + */ + +/* + * BIOS Parameter Block (BPB) for DOS 3.3 + */ +struct bpb33 { + u_short bpbBytesPerSec; /* bytes per sector */ + u_char bpbSecPerClust; /* sectors per cluster */ + u_short bpbResSectors; /* number of reserved sectors */ + u_char bpbFATs; /* number of FATs */ + u_short bpbRootDirEnts; /* number of root directory entries */ + u_short bpbSectors; /* total number of sectors */ + u_char bpbMedia; /* media descriptor */ + u_short bpbFATsecs; /* number of sectors per FAT */ + u_short bpbSecPerTrack; /* sectors per track */ + u_short bpbHeads; /* number of heads */ + u_short bpbHiddenSecs; /* number of hidden sectors */ +}; + +/* + * BPB for DOS 5.0 The difference is bpbHiddenSecs is a short for DOS 3.3, + * and bpbHugeSectors is not in the 3.3 bpb. + */ +struct bpb50 { + u_short bpbBytesPerSec; /* bytes per sector */ + u_char bpbSecPerClust; /* sectors per cluster */ + u_short bpbResSectors; /* number of reserved sectors */ + u_char bpbFATs; /* number of FATs */ + u_short bpbRootDirEnts; /* number of root directory entries */ + u_short bpbSectors; /* total number of sectors */ + u_char bpbMedia; /* media descriptor */ + u_short bpbFATsecs; /* number of sectors per FAT */ + u_short bpbSecPerTrack; /* sectors per track */ + u_short bpbHeads; /* number of heads */ + u_long bpbHiddenSecs; /* number of hidden sectors */ + u_long bpbHugeSectors; /* number of sectrs if bpbSectors == 0 */ +}; + +/* + * The following structures represent how the bpb's look on disk. shorts + * and longs are just character arrays of the appropriate length. This is + * because the compiler forces shorts and longs to align on word or + * halfword boundaries. + * + * XXX The little-endian code here assumes that the processor can access + * 16-bit and 32-bit quantities on byte boundaries. If this is not true, + * use the macros for the big-endian case. + */ +#include <machine/endian.h> +#if BYTE_ORDER == LITTLE_ENDIAN /* && can do unaligned accesses */ +#define getushort(x) *((u_short *)(x)) +#define getulong(x) *((u_long *)(x)) +#define putushort(p, v) (*((u_short *)(p)) = (v)) +#define putulong(p, v) (*((u_long *)(p)) = (v)) + +#else +#define getushort(x) (((u_char *)(x))[0] + (((u_char *)(x))[1] << 8)) +#define getulong(x) (((u_char *)(x))[0] + (((u_char *)(x))[1] << 8) \ + + (((u_char *)(x))[2] << 16) \ + + (((u_char *)(x))[3] << 24)) +#define putushort(p, v) (((u_char *)(p))[0] = (v), \ + ((u_char *)(p))[1] = (v) >> 8) +#define putulong(p, v) (((u_char *)(p))[0] = (v), \ + ((u_char *)(p))[1] = (v) >> 8, \ + ((u_char *)(p))[2] = (v) >> 16,\ + ((u_char *)(p))[3] = (v) >> 24) +#endif + +/* + * BIOS Parameter Block (BPB) for DOS 3.3 + */ +struct byte_bpb33 { + char bpbBytesPerSec[2]; /* bytes per sector */ + char bpbSecPerClust; /* sectors per cluster */ + char bpbResSectors[2]; /* number of reserved sectors */ + char bpbFATs; /* number of FATs */ + char bpbRootDirEnts[2]; /* number of root directory entries */ + char bpbSectors[2]; /* total number of sectors */ + char bpbMedia; /* media descriptor */ + char bpbFATsecs[2]; /* number of sectors per FAT */ + char bpbSecPerTrack[2]; /* sectors per track */ + char bpbHeads[2]; /* number of heads */ + char bpbHiddenSecs[2]; /* number of hidden sectors */ +}; + +/* + * BPB for DOS 5.0 The difference is bpbHiddenSecs is a short for DOS 3.3, + * and bpbHugeSectors is not in the 3.3 bpb. + */ +struct byte_bpb50 { + char bpbBytesPerSec[2]; /* bytes per sector */ + char bpbSecPerClust; /* sectors per cluster */ + char bpbResSectors[2]; /* number of reserved sectors */ + char bpbFATs; /* number of FATs */ + char bpbRootDirEnts[2]; /* number of root directory entries */ + char bpbSectors[2]; /* total number of sectors */ + char bpbMedia; /* media descriptor */ + char bpbFATsecs[2]; /* number of sectors per FAT */ + char bpbSecPerTrack[2]; /* sectors per track */ + char bpbHeads[2]; /* number of heads */ + char bpbHiddenSecs[4]; /* number of hidden sectors */ + char bpbHugeSectors[4]; /* number of sectrs if bpbSectors == 0 */ +}; diff --git a/sys/msdosfs/denode.h b/sys/msdosfs/denode.h new file mode 100644 index 0000000..8df3e4c --- /dev/null +++ b/sys/msdosfs/denode.h @@ -0,0 +1,267 @@ +/* $Id$ */ +/* $NetBSD: denode.h,v 1.8 1994/08/21 18:43:49 ws Exp $ */ + +/*- + * Copyright (C) 1994 Wolfgang Solfrank. + * Copyright (C) 1994 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 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. + * + * October 1992 + */ + +/* + * This is the pc filesystem specific portion of the vnode structure. + * + * To describe a file uniquely the de_dirclust, de_diroffset, and + * de_StartCluster fields are used. + * + * de_dirclust contains the cluster number of the directory cluster + * containing the entry for a file or directory. + * de_diroffset is the index into the cluster for the entry describing + * a file or directory. + * de_StartCluster is the number of the first cluster of the file or directory. + * + * Now to describe the quirks of the pc filesystem. + * - Clusters 0 and 1 are reserved. + * - The first allocatable cluster is 2. + * - The root directory is of fixed size and all blocks that make it up + * are contiguous. + * - Cluster 0 refers to the root directory when it is found in the + * startcluster field of a directory entry that points to another directory. + * - Cluster 0 implies a 0 length file when found in the start cluster field + * of a directory entry that points to a file. + * - You can't use the cluster number 0 to derive the address of the root + * directory. + * - Multiple directory entries can point to a directory. The entry in the + * parent directory points to a child directory. Any directories in the + * child directory contain a ".." entry that points back to the parent. + * The child directory itself contains a "." entry that points to itself. + * - The root directory does not contain a "." or ".." entry. + * - Directory entries for directories are never changed once they are created + * (except when removed). The size stays 0, and the last modification time + * is never changed. This is because so many directory entries can point to + * the physical clusters that make up a directory. It would lead to an + * update nightmare. + * - The length field in a directory entry pointing to a directory contains 0 + * (always). The only way to find the end of a directory is to follow the + * cluster chain until the "last cluster" marker is found. + * + * My extensions to make this house of cards work. These apply only to the in + * memory copy of the directory entry. + * - A reference count for each denode will be kept since dos doesn't keep such + * things. + */ + +/* + * Internal pseudo-offset for (nonexistent) directory entry for the root + * dir in the root dir + */ +#define MSDOSFSROOT_OFS 0x1fffffff + +/* + * The fat cache structure. fc_fsrcn is the filesystem relative cluster + * number that corresponds to the file relative cluster number in this + * structure (fc_frcn). + */ +struct fatcache { + u_short fc_frcn; /* file relative cluster number */ + u_short fc_fsrcn; /* filesystem relative cluster number */ +}; + +/* + * The fat entry cache as it stands helps make extending files a "quick" + * operation by avoiding having to scan the fat to discover the last + * cluster of the file. The cache also helps sequential reads by + * remembering the last cluster read from the file. This also prevents us + * from having to rescan the fat to find the next cluster to read. This + * cache is probably pretty worthless if a file is opened by multiple + * processes. + */ +#define FC_SIZE 2 /* number of entries in the cache */ +#define FC_LASTMAP 0 /* entry the last call to pcbmap() resolved + * to */ +#define FC_LASTFC 1 /* entry for the last cluster in the file */ + +#define FCE_EMPTY 0xffff /* doesn't represent an actual cluster # */ + +/* + * Set a slot in the fat cache. + */ +#define fc_setcache(dep, slot, frcn, fsrcn) \ + (dep)->de_fc[slot].fc_frcn = frcn; \ + (dep)->de_fc[slot].fc_fsrcn = fsrcn; + +/* + * This is the in memory variant of a dos directory entry. It is usually + * contained within a vnode. + */ +struct denode { + struct denode *de_next; /* Hash chain forward */ + struct denode **de_prev; /* Hash chain back */ + struct vnode *de_vnode; /* addr of vnode we are part of */ + struct vnode *de_devvp; /* vnode of blk dev we live on */ + u_long de_flag; /* flag bits */ + dev_t de_dev; /* device where direntry lives */ + u_long de_dirclust; /* cluster of the directory file containing this entry */ + u_long de_diroffset; /* ordinal of this entry in the directory */ + u_long de_fndclust; /* cluster of found dir entry */ + u_long de_fndoffset; /* offset of found dir entry */ + long de_refcnt; /* reference count */ + struct msdosfsmount *de_pmp; /* addr of our mount struct */ + struct lockf *de_lockf; /* byte level lock list */ + pid_t de_lockholder; /* current lock holder */ + pid_t de_lockwaiter; /* lock wanter */ + /* the next two fields must be contiguous in memory... */ + u_char de_Name[8]; /* name, from directory entry */ + u_char de_Extension[3]; /* extension, from directory entry */ + u_char de_Attributes; /* attributes, from directory entry */ + u_short de_Time; /* creation time */ + u_short de_Date; /* creation date */ + u_short de_StartCluster; /* starting cluster of file */ + u_long de_FileSize; /* size of file in bytes */ + struct fatcache de_fc[FC_SIZE]; /* fat cache */ +}; + +/* + * Values for the de_flag field of the denode. + */ +#define DE_LOCKED 0x0001 /* directory entry is locked */ +#define DE_WANTED 0x0002 /* someone wants this de */ +#define DE_UPDATE 0x0004 /* file has been modified */ +#define DE_MODIFIED 0x0080 /* denode wants to be written back to disk */ + +/* + * Transfer directory entries between internal and external form. + * dep is a struct denode * (internal form), + * dp is a struct direntry * (external form). + */ +#define DE_INTERNALIZE(dep, dp) \ + (bcopy((dp)->deName, (dep)->de_Name, 11), \ + (dep)->de_Attributes = (dp)->deAttributes, \ + (dep)->de_Time = getushort((dp)->deTime), \ + (dep)->de_Date = getushort((dp)->deDate), \ + (dep)->de_StartCluster = getushort((dp)->deStartCluster), \ + (dep)->de_FileSize = getulong((dp)->deFileSize)) + +#define DE_EXTERNALIZE(dp, dep) \ + (bcopy((dep)->de_Name, (dp)->deName, 11), \ + (dp)->deAttributes = (dep)->de_Attributes, \ + putushort((dp)->deTime, (dep)->de_Time), \ + putushort((dp)->deDate, (dep)->de_Date), \ + putushort((dp)->deStartCluster, (dep)->de_StartCluster), \ + putulong((dp)->deFileSize, (dep)->de_FileSize)) + +#define de_forw de_chain[0] +#define de_back de_chain[1] + +#ifdef KERNEL + +#define VTODE(vp) ((struct denode *)(vp)->v_data) +#define DETOV(de) ((de)->de_vnode) + +#define DE_UPDAT(dep, t, waitfor) \ + if (dep->de_flag & DE_UPDATE) \ + (void) deupdat(dep, t, waitfor); + +#define DE_TIMES(dep, t) \ + if (dep->de_flag & DE_UPDATE) { \ + (dep)->de_flag |= DE_MODIFIED; \ + unix2dostime(t, &dep->de_Date, &dep->de_Time); \ + (dep)->de_flag &= ~DE_UPDATE; \ + } + +/* + * This overlays the fid sturcture (see mount.h) + */ +struct defid { + u_short defid_len; /* length of structure */ + u_short defid_pad; /* force long alignment */ + + u_long defid_dirclust; /* cluster this dir entry came from */ + u_long defid_dirofs; /* index of entry within the cluster */ + + /* u_long defid_gen; generation number */ +}; + +/* + * Prototypes for MSDOSFS vnode operations + */ +int msdosfs_lookup __P((struct vop_lookup_args *)); +int msdosfs_create __P((struct vop_create_args *)); +int msdosfs_mknod __P((struct vop_mknod_args *)); +int msdosfs_open __P((struct vop_open_args *)); +int msdosfs_close __P((struct vop_close_args *)); +int msdosfs_access __P((struct vop_access_args *)); +int msdosfs_getattr __P((struct vop_getattr_args *)); +int msdosfs_setattr __P((struct vop_setattr_args *)); +int msdosfs_read __P((struct vop_read_args *)); +int msdosfs_write __P((struct vop_write_args *)); +int msdosfs_ioctl __P((struct vop_ioctl_args *)); +int msdosfs_select __P((struct vop_select_args *)); +int msdosfs_mmap __P((struct vop_mmap_args *)); +int msdosfs_fsync __P((struct vop_fsync_args *)); +int msdosfs_seek __P((struct vop_seek_args *)); +int msdosfs_remove __P((struct vop_remove_args *)); +int msdosfs_link __P((struct vop_link_args *)); +int msdosfs_rename __P((struct vop_rename_args *)); +int msdosfs_mkdir __P((struct vop_mkdir_args *)); +int msdosfs_rmdir __P((struct vop_rmdir_args *)); +int msdosfs_symlink __P((struct vop_symlink_args *)); +int msdosfs_readdir __P((struct vop_readdir_args *)); +int msdosfs_readlink __P((struct vop_readlink_args *)); +int msdosfs_abortop __P((struct vop_abortop_args *)); +int msdosfs_inactive __P((struct vop_inactive_args *)); +int msdosfs_reclaim __P((struct vop_reclaim_args *)); +int msdosfs_lock __P((struct vop_lock_args *)); +int msdosfs_unlock __P((struct vop_unlock_args *)); +int msdosfs_bmap __P((struct vop_bmap_args *)); +int msdosfs_strategy __P((struct vop_strategy_args *)); +int msdosfs_print __P((struct vop_print_args *)); +int msdosfs_islocked __P((struct vop_islocked_args *)); +int msdosfs_advlock __P((struct vop_advlock_args *)); +int msdosfs_reallocblks __P((struct vop_reallocblks_args *)); + +/* + * Internal service routine prototypes. + */ +int deget __P((struct msdosfsmount * pmp, u_long dirclust, u_long diroffset, struct direntry * direntptr, struct denode ** depp)); +#endif /* KERNEL */ diff --git a/sys/msdosfs/direntry.h b/sys/msdosfs/direntry.h new file mode 100644 index 0000000..d2ec019 --- /dev/null +++ b/sys/msdosfs/direntry.h @@ -0,0 +1,104 @@ +/* $Id$ */ +/* $NetBSD: direntry.h,v 1.7 1994/08/21 18:43:54 ws Exp $ */ + +/*- + * Copyright (C) 1994 Wolfgang Solfrank. + * Copyright (C) 1994 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 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. + * + * October 1992 + */ + +/* + * Structure of a dos directory entry. + */ +struct direntry { + u_char deName[8]; /* filename, blank filled */ +#define SLOT_EMPTY 0x00 /* slot has never been used */ +#define SLOT_E5 0x05 /* the real value is 0xe5 */ +#define SLOT_DELETED 0xe5 /* file in this slot deleted */ + u_char deExtension[3]; /* extension, blank filled */ + u_char deAttributes; /* file attributes */ +#define ATTR_NORMAL 0x00 /* normal file */ +#define ATTR_READONLY 0x01 /* file is readonly */ +#define ATTR_HIDDEN 0x02 /* file is hidden */ +#define ATTR_SYSTEM 0x04 /* file is a system file */ +#define ATTR_VOLUME 0x08 /* entry is a volume label */ +#define ATTR_DIRECTORY 0x10 /* entry is a directory name */ +#define ATTR_ARCHIVE 0x20 /* file is new or modified */ + u_char deReserved[10]; /* reserved */ + u_char deTime[2]; /* create/last update time */ + u_char deDate[2]; /* create/last update date */ + u_char deStartCluster[2]; /* starting cluster of file */ + u_char deFileSize[4]; /* size of file in bytes */ +}; + +/* + * This is the format of the contents of the deTime field in the direntry + * structure. + * We don't use bitfields because we don't know how compilers for + * arbitrary machines will lay them out. + */ +#define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */ +#define DT_2SECONDS_SHIFT 0 +#define DT_MINUTES_MASK 0x7E0 /* minutes */ +#define DT_MINUTES_SHIFT 5 +#define DT_HOURS_MASK 0xF800 /* hours */ +#define DT_HOURS_SHIFT 11 + +/* + * This is the format of the contents of the deDate field in the direntry + * structure. + */ +#define DD_DAY_MASK 0x1F /* day of month */ +#define DD_DAY_SHIFT 0 +#define DD_MONTH_MASK 0x1E0 /* month */ +#define DD_MONTH_SHIFT 5 +#define DD_YEAR_MASK 0xFE00 /* year - 1980 */ +#define DD_YEAR_SHIFT 9 + +#ifdef KERNEL +void unix2dostime __P((struct timespec * tsp, u_short * ddp, u_short * dtp)); +void dos2unixtime __P((u_short dd, u_short dt, struct timespec * tsp)); +int dos2unixfn __P((u_char dn[11], u_char * un)); +void unix2dosfn __P((u_char * un, u_char dn[11], int unlen)); +#endif /* KERNEL */ diff --git a/sys/msdosfs/fat.h b/sys/msdosfs/fat.h new file mode 100644 index 0000000..3867c87 --- /dev/null +++ b/sys/msdosfs/fat.h @@ -0,0 +1,97 @@ +/* $Id$ */ +/* $NetBSD: fat.h,v 1.4 1994/08/21 18:43:57 ws Exp $ */ + +/*- + * Copyright (C) 1994 Wolfgang Solfrank. + * Copyright (C) 1994 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 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. + * + * October 1992 + */ + +/* + * Some useful cluster numbers. + */ +#define MSDOSFSROOT 0 /* cluster 0 means the root dir */ +#define CLUST_FREE 0 /* cluster 0 also means a free cluster */ +#define MSDOSFSFREE CLUST_FREE +#define CLUST_FIRST 2 /* first legal cluster number */ +#define CLUST_RSRVS 0xfff0 /* start of reserved cluster range */ +#define CLUST_RSRVE 0xfff6 /* end of reserved cluster range */ +#define CLUST_BAD 0xfff7 /* a cluster with a defect */ +#define CLUST_EOFS 0xfff8 /* start of eof cluster range */ +#define CLUST_EOFE 0xffff /* end of eof cluster range */ + +#define FAT12_MASK 0x0fff /* mask for 12 bit cluster numbers */ +#define FAT16_MASK 0xffff /* mask for 16 bit cluster numbers */ + +/* + * Return true if filesystem uses 12 bit fats. Microsoft Programmer's + * Reference says if the maximum cluster number in a filesystem is greater + * than 4086 then we've got a 16 bit fat filesystem. + */ +#define FAT12(pmp) (pmp->pm_maxcluster <= 4086) +#define FAT16(pmp) (pmp->pm_maxcluster > 4086) + +#define MSDOSFSEOF(cn) (((cn) & 0xfff8) == 0xfff8) + +#ifdef KERNEL +/* + * These are the values for the function argument to the function + * fatentry(). + */ +#define FAT_GET 0x0001 /* get a fat entry */ +#define FAT_SET 0x0002 /* set a fat entry */ +#define FAT_GET_AND_SET (FAT_GET | FAT_SET) + +/* + * Flags to extendfile: + */ +#define DE_CLEAR 1 /* Zero out the blocks allocated */ + +int pcbmap __P((struct denode *dep, u_long findcn, daddr_t *bnp, u_long *cnp)); +int clusterfree __P((struct msdosfsmount *pmp, u_long cn, u_long *oldcnp)); +int clusteralloc __P((struct msdosfsmount *pmp, u_long start, u_long count, u_long fillwith, u_long *retcluster, u_long *got)); +int fatentry __P((int function, struct msdosfsmount *pmp, u_long cluster, u_long *oldcontents, u_long newcontents)); +int freeclusterchain __P((struct msdosfsmount *pmp, u_long startchain)); +int extendfile __P((struct denode *dep, u_long count, struct buf **bpp, u_long *ncp, int flags)); +#endif /* KERNEL */ diff --git a/sys/msdosfs/msdosfs_conv.c b/sys/msdosfs/msdosfs_conv.c new file mode 100644 index 0000000..da8ce97 --- /dev/null +++ b/sys/msdosfs/msdosfs_conv.c @@ -0,0 +1,358 @@ +/* $Id$ */ +/* $NetBSD: msdosfs_conv.c,v 1.6.2.1 1994/08/30 02:27:57 cgd Exp $ */ + +/* + * 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. + * + * October 1992 + */ + +/* + * System include files. + */ +#include <sys/param.h> +#include <sys/time.h> +#include <sys/kernel.h> /* defines tz */ + +/* + * MSDOSFS include files. + */ +#include <msdosfs/direntry.h> + +/* + * Days in each month in a regular year. + */ +u_short regyear[] = { + 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 +}; + +/* + * Days in each month in a leap year. + */ +u_short leapyear[] = { + 31, 29, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 +}; + +/* + * Variables used to remember parts of the last time conversion. Maybe we + * can avoid a full conversion. + */ +u_long lasttime; +u_long lastday; +u_short lastddate; +u_short lastdtime; + +/* + * Convert the unix version of time to dos's idea of time to be used in + * file timestamps. The passed in unix time is assumed to be in GMT. + */ +void +unix2dostime(tsp, ddp, dtp) + struct timespec *tsp; + u_short *ddp; + u_short *dtp; +{ + u_long t; + u_long days; + u_long inc; + u_long year; + u_long month; + u_short *months; + + /* + * If the time from the last conversion is the same as now, then + * skip the computations and use the saved result. + */ + t = tsp->ts_sec - (tz.tz_minuteswest * 60) + /* +- daylight savings time correction */ ; + if (lasttime != t) { + lasttime = t; + lastdtime = (((t % 60) >> 1) << DT_2SECONDS_SHIFT) + + (((t / 60) % 60) << DT_MINUTES_SHIFT) + + (((t / 3600) % 24) << DT_HOURS_SHIFT); + + /* + * If the number of days since 1970 is the same as the last + * time we did the computation then skip all this leap year + * and month stuff. + */ + days = t / (24 * 60 * 60); + if (days != lastday) { + lastday = days; + for (year = 1970;; year++) { + inc = year & 0x03 ? 365 : 366; + if (days < inc) + break; + days -= inc; + } + months = year & 0x03 ? regyear : leapyear; + for (month = 0; month < 12; month++) { + if (days < months[month]) + break; + days -= months[month]; + } + lastddate = ((days + 1) << DD_DAY_SHIFT) + + ((month + 1) << DD_MONTH_SHIFT); + /* + * Remember dos's idea of time is relative to 1980. + * unix's is relative to 1970. If somehow we get a + * time before 1980 then don't give totally crazy + * results. + */ + if (year > 1980) + lastddate += (year - 1980) << DD_YEAR_SHIFT; + } + } + *dtp = lastdtime; + *ddp = lastddate; +} + +/* + * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that + * interval there were 8 regular years and 2 leap years. + */ +#define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60)) + +u_short lastdosdate; +u_long lastseconds; + +/* + * Convert from dos' idea of time to unix'. This will probably only be + * called from the stat(), and fstat() system calls and so probably need + * not be too efficient. + */ +void +dos2unixtime(dd, dt, tsp) + u_short dd; + u_short dt; + struct timespec *tsp; +{ + u_long seconds; + u_long m, month; + u_long y, year; + u_long days; + u_short *months; + + seconds = ((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) + + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60 + + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600; + /* + * If the year, month, and day from the last conversion are the + * same then use the saved value. + */ + if (lastdosdate != dd) { + lastdosdate = dd; + days = 0; + year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT; + for (y = 0; y < year; y++) { + days += y & 0x03 ? 365 : 366; + } + months = year & 0x03 ? regyear : leapyear; + /* + * Prevent going from 0 to 0xffffffff in the following + * loop. + */ + month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT; + if (month == 0) { + printf("dos2unixtime(): month value out of range (%d)\n", + month); + month = 1; + } + for (m = 0; m < month - 1; m++) { + days += months[m]; + } + days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1; + lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980; + } + tsp->ts_sec = seconds + lastseconds + (tz.tz_minuteswest * 60) + /* -+ daylight savings time correction */ ; + tsp->ts_nsec = 0; +} + +/* + * Cheezy macros to do case detection and conversion for the ascii + * character set. DOESN'T work for ebcdic. + */ +#define isupper(c) (c >= 'A' && c <= 'Z') +#define islower(c) (c >= 'a' && c <= 'z') +#define toupper(c) (c & ~' ') +#define tolower(c) (c | ' ') + +/* + * DOS filenames are made of 2 parts, the name part and the extension part. + * The name part is 8 characters long and the extension part is 3 + * characters long. They may contain trailing blanks if the name or + * extension are not long enough to fill their respective fields. + */ + +/* + * Convert a DOS filename to a unix filename. And, return the number of + * characters in the resulting unix filename excluding the terminating + * null. + */ +int +dos2unixfn(dn, un) + u_char dn[11]; + u_char *un; +{ + int i; + int ni; + int ei; + int thislong = 0; + u_char c; + u_char *origun = un; + + /* + * Find the last character in the name portion of the dos filename. + */ + for (ni = 7; ni >= 0; ni--) + if (dn[ni] != ' ') + break; + + /* + * Find the last character in the extension portion of the + * filename. + */ + for (ei = 10; ei >= 8; ei--) + if (dn[ei] != ' ') + break; + + /* + * Copy the name portion into the unix filename string. NOTE: DOS + * filenames are usually kept in upper case. To make it more unixy + * we convert all DOS filenames to lower case. Some may like this, + * some may not. + */ + for (i = 0; i <= ni; i++) { + c = dn[i]; + *un++ = isupper(c) ? tolower(c) : c; + thislong++; + } + + /* + * Now, if there is an extension then put in a period and copy in + * the extension. + */ + if (ei >= 8) { + *un++ = '.'; + thislong++; + for (i = 8; i <= ei; i++) { + c = dn[i]; + *un++ = isupper(c) ? tolower(c) : c; + thislong++; + } + } + *un++ = 0; + + /* + * If first char of the filename is SLOT_E5 (0x05), then the real + * first char of the filename should be 0xe5. But, they couldn't + * just have a 0xe5 mean 0xe5 because that is used to mean a freed + * directory slot. Another dos quirk. + */ + if (*origun == SLOT_E5) + *origun = 0xe5; + + return thislong; +} + +/* + * Convert a unix filename to a DOS filename. This function does not ensure + * that valid characters for a dos filename are supplied. + */ +void +unix2dosfn(un, dn, unlen) + u_char *un; + u_char dn[11]; + int unlen; +{ + int i; + u_char c; + + /* + * Fill the dos filename string with blanks. These are DOS's pad + * characters. + */ + for (i = 0; i <= 10; i++) + dn[i] = ' '; + + /* + * The filenames "." and ".." are handled specially, since they + * don't follow dos filename rules. + */ + if (un[0] == '.' && unlen == 1) { + dn[0] = '.'; + return; + } + if (un[0] == '.' && un[1] == '.' && unlen == 2) { + dn[0] = '.'; + dn[1] = '.'; + return; + } + + /* + * Copy the unix filename into the dos filename string upto the end + * of string, a '.', or 8 characters. Whichever happens first stops + * us. This forms the name portion of the dos filename. Fold to + * upper case. + */ + for (i = 0; i <= 7 && unlen && (c = *un) && c != '.'; i++) { + dn[i] = islower(c) ? toupper(c) : c; + un++; + unlen--; + } + + /* + * If the first char of the filename is 0xe5, then translate it to + * 0x05. This is because 0xe5 is the marker for a deleted + * directory slot. I guess this means you can't have filenames + * that start with 0x05. I suppose we should check for this and + * doing something about it. + */ + if (dn[0] == SLOT_DELETED) + dn[0] = SLOT_E5; + + /* + * Strip any further characters up to a '.' or the end of the + * string. + */ + while (unlen && (c = *un)) { + un++; + unlen--; + /* Make sure we've skipped over the dot before stopping. */ + if (c == '.') + break; + } + + /* + * Copy in the extension part of the name, if any. Force to upper + * case. Note that the extension is allowed to contain '.'s. + * Filenames in this form are probably inaccessable under dos. + */ + for (i = 8; i <= 10 && unlen && (c = *un); i++) { + dn[i] = islower(c) ? toupper(c) : c; + un++; + unlen--; + } +} + +/* + * Get rid of these macros before someone discovers we are using such + * hideous things. + */ +#undef isupper +#undef islower +#undef toupper +#undef tolower diff --git a/sys/msdosfs/msdosfs_denode.c b/sys/msdosfs/msdosfs_denode.c new file mode 100644 index 0000000..6812863 --- /dev/null +++ b/sys/msdosfs/msdosfs_denode.c @@ -0,0 +1,689 @@ +/* $Id$ */ +/* $NetBSD: msdosfs_denode.c,v 1.9 1994/08/21 18:44:00 ws Exp $ */ + +/*- + * Copyright (C) 1994 Wolfgang Solfrank. + * Copyright (C) 1994 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 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. + * + * October 1992 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mount.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/buf.h> +#include <sys/vnode.h> +#include <sys/kernel.h> /* defines "time" */ + +#include <msdosfs/bpb.h> +#include <msdosfs/msdosfsmount.h> +#include <msdosfs/direntry.h> +#include <msdosfs/denode.h> +#include <msdosfs/fat.h> + +struct denode **dehashtbl; +u_long dehash; /* size of hash table - 1 */ +#define DEHASH(dev, deno) (((dev) + (deno)) & dehash) + +int msdosfs_init() +{ + dehashtbl = hashinit(desiredvnodes/2, M_MSDOSFSMNT, &dehash); + return 0; +} + +static struct denode * +msdosfs_hashget(dev, dirclust, diroff) + dev_t dev; + u_long dirclust; + u_long diroff; +{ + struct denode *dep; + + for (;;) + for (dep = dehashtbl[DEHASH(dev, dirclust + diroff)];; + dep = dep->de_next) { + if (dep == NULL) + return NULL; + if (dirclust != dep->de_dirclust + || diroff != dep->de_diroffset + || dev != dep->de_dev + || dep->de_refcnt == 0) + continue; + if (dep->de_flag & DE_LOCKED) { + dep->de_flag |= DE_WANTED; + sleep((caddr_t)dep, PINOD); + break; + } + if (!vget(DETOV(dep), 1)) + return dep; + break; + } + /* NOTREACHED */ +} + +static void +msdosfs_hashins(dep) + struct denode *dep; +{ + struct denode **depp, *deq; + + depp = &dehashtbl[DEHASH(dep->de_dev, dep->de_dirclust + dep->de_diroffset)]; + if (deq = *depp) + deq->de_prev = &dep->de_next; + dep->de_next = deq; + dep->de_prev = depp; + *depp = dep; + if (dep->de_flag & DE_LOCKED) + panic("msdosfs_hashins: already locked"); + if (curproc) + dep->de_lockholder = curproc->p_pid; + else + dep->de_lockholder = -1; + dep->de_flag |= DE_LOCKED; +} + +static void +msdosfs_hashrem(dep) + struct denode *dep; +{ + struct denode *deq; + if (deq = dep->de_next) + deq->de_prev = dep->de_prev; + *dep->de_prev = deq; +#ifdef DIAGNOSTIC + dep->de_next = NULL; + dep->de_prev = NULL; +#endif +} + +/* + * If deget() succeeds it returns with the gotten denode locked(). + * + * pmp - address of msdosfsmount structure of the filesystem containing + * the denode of interest. The pm_dev field and the address of + * the msdosfsmount structure are used. + * dirclust - which cluster bp contains, if dirclust is 0 (root directory) + * diroffset is relative to the beginning of the root directory, + * otherwise it is cluster relative. + * diroffset - offset past begin of cluster of denode we want + * direntptr - address of the direntry structure of interest. If direntptr is + * NULL, the block is read if necessary. + * depp - returns the address of the gotten denode. + */ +int +deget(pmp, dirclust, diroffset, direntptr, depp) + struct msdosfsmount *pmp; /* so we know the maj/min number */ + u_long dirclust; /* cluster this dir entry came from */ + u_long diroffset; /* index of entry within the cluster */ + struct direntry *direntptr; + struct denode **depp; /* returns the addr of the gotten denode */ +{ + int error; + dev_t dev = pmp->pm_dev; + struct mount *mntp = pmp->pm_mountp; + extern int (**msdosfs_vnodeop_p)(); + struct denode *ldep; + struct vnode *nvp; + struct buf *bp; + +#ifdef MSDOSFS_DEBUG + printf("deget(pmp %08x, dirclust %d, diroffset %x, direntptr %x, depp %08x)\n", + pmp, dirclust, diroffset, direntptr, depp); +#endif + + /* + * If dir entry is given and refers to a directory, convert to + * canonical form + */ + if (direntptr && (direntptr->deAttributes & ATTR_DIRECTORY)) { + dirclust = getushort(direntptr->deStartCluster); + if (dirclust == MSDOSFSROOT) + diroffset = MSDOSFSROOT_OFS; + else + diroffset = 0; + } + + /* + * See if the denode is in the denode cache. Use the location of + * the directory entry to compute the hash value. For subdir use + * address of "." entry. for root dir use cluster MSDOSFSROOT, + * offset MSDOSFSROOT_OFS + * + * NOTE: The check for de_refcnt > 0 below insures the denode being + * examined does not represent an unlinked but still open file. + * These files are not to be accessible even when the directory + * entry that represented the file happens to be reused while the + * deleted file is still open. + */ + if (ldep = msdosfs_hashget(dev, dirclust, diroffset)) { + *depp = ldep; + return 0; + } + + + /* + * Directory entry was not in cache, have to create a vnode and + * copy it from the passed disk buffer. + */ + /* getnewvnode() does a VREF() on the vnode */ + if (error = getnewvnode(VT_MSDOSFS, mntp, msdosfs_vnodeop_p, &nvp)) { + *depp = 0; + return error; + } + MALLOC(ldep, struct denode *, sizeof(struct denode), M_MSDOSFSNODE, M_WAITOK); + bzero((caddr_t)ldep, sizeof *ldep); + nvp->v_data = ldep; + ldep->de_vnode = nvp; + ldep->de_flag = 0; + ldep->de_devvp = 0; + ldep->de_lockf = 0; + ldep->de_dev = dev; + ldep->de_dirclust = dirclust; + ldep->de_diroffset = diroffset; + fc_purge(ldep, 0); /* init the fat cache for this denode */ + + /* + * Insert the denode into the hash queue and lock the denode so it + * can't be accessed until we've read it in and have done what we + * need to it. + */ + msdosfs_hashins(ldep); + + /* + * Copy the directory entry into the denode area of the vnode. + */ + if (dirclust == MSDOSFSROOT && diroffset == MSDOSFSROOT_OFS) { + /* + * Directory entry for the root directory. There isn't one, + * so we manufacture one. We should probably rummage + * through the root directory and find a label entry (if it + * exists), and then use the time and date from that entry + * as the time and date for the root denode. + */ + ldep->de_Attributes = ATTR_DIRECTORY; + ldep->de_StartCluster = MSDOSFSROOT; + ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec; + /* + * fill in time and date so that dos2unixtime() doesn't + * spit up when called from msdosfs_getattr() with root + * denode + */ + ldep->de_Time = 0x0000; /* 00:00:00 */ + ldep->de_Date = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT) + | (1 << DD_DAY_SHIFT); + /* Jan 1, 1980 */ + /* leave the other fields as garbage */ + } else { + bp = NULL; + if (!direntptr) { + error = readep(pmp, dirclust, diroffset, &bp, + &direntptr); + if (error) + return error; + } + DE_INTERNALIZE(ldep, direntptr); + if (bp) + brelse(bp); + } + + /* + * Fill in a few fields of the vnode and finish filling in the + * denode. Then return the address of the found denode. + */ + ldep->de_pmp = pmp; + ldep->de_devvp = pmp->pm_devvp; + ldep->de_refcnt = 1; + if (ldep->de_Attributes & ATTR_DIRECTORY) { + /* + * Since DOS directory entries that describe directories + * have 0 in the filesize field, we take this opportunity + * to find out the length of the directory and plug it into + * the denode structure. + */ + u_long size; + + nvp->v_type = VDIR; + if (ldep->de_StartCluster == MSDOSFSROOT) + nvp->v_flag |= VROOT; + else { + error = pcbmap(ldep, 0xffff, 0, &size); + if (error == E2BIG) { + ldep->de_FileSize = size << pmp->pm_cnshift; + error = 0; + } else + printf("deget(): pcbmap returned %d\n", error); + } + } else + nvp->v_type = VREG; + VREF(ldep->de_devvp); + *depp = ldep; + return 0; +} + +int +deupdat(dep, tp, waitfor) + struct denode *dep; + struct timespec *tp; + int waitfor; +{ + int error; + daddr_t bn; + int diro; + struct buf *bp; + struct direntry *dirp; + struct msdosfsmount *pmp = dep->de_pmp; + struct timespec ts; + struct vnode *vp = DETOV(dep); + +#ifdef MSDOSFS_DEBUG + printf("deupdat(): dep %08x\n", dep); +#endif + + /* + * If the update bit is off, or this denode is from a readonly + * filesystem, or this denode is for a directory, or the denode + * represents an open but unlinked file then don't do anything. DOS + * directory entries that describe a directory do not ever get + * updated. This is the way dos treats them. + */ + if ((dep->de_flag & DE_UPDATE) == 0 || + vp->v_mount->mnt_flag & MNT_RDONLY || + dep->de_Attributes & ATTR_DIRECTORY || + dep->de_refcnt <= 0) + return 0; + + /* + * Read in the cluster containing the directory entry we want to + * update. + */ + if (error = readde(dep, &bp, &dirp)) + return error; + + /* + * Put the passed in time into the directory entry. + */ + TIMEVAL_TO_TIMESPEC(&time, &ts); + unix2dostime(&ts, &dep->de_Date, &dep->de_Time); + dep->de_flag &= ~DE_UPDATE; + + /* + * Copy the directory entry out of the denode into the cluster it + * came from. + */ + DE_EXTERNALIZE(dirp, dep); + + /* + * Write the cluster back to disk. If they asked for us to wait + * for the write to complete, then use bwrite() otherwise use + * bdwrite(). + */ + error = 0; /* note that error is 0 from above, but ... */ + if (waitfor) + error = bwrite(bp); + else + bdwrite(bp); + return error; +} + +/* + * Truncate the file described by dep to the length specified by length. + */ +int +detrunc(dep, length, flags, cred, p) + struct denode *dep; + u_long length; + int flags; + struct ucred *cred; + struct proc *p; +{ + int error; + int allerror; + int vflags; + u_long eofentry; + u_long chaintofree; + daddr_t bn; + int boff; + int isadir = dep->de_Attributes & ATTR_DIRECTORY; + struct buf *bp; + struct msdosfsmount *pmp = dep->de_pmp; + +#ifdef MSDOSFS_DEBUG + printf("detrunc(): file %s, length %d, flags %d\n", dep->de_Name, length, flags); +#endif + + /* + * Disallow attempts to truncate the root directory since it is of + * fixed size. That's just the way dos filesystems are. We use + * the VROOT bit in the vnode because checking for the directory + * bit and a startcluster of 0 in the denode is not adequate to + * recognize the root directory at this point in a file or + * directory's life. + */ + if (DETOV(dep)->v_flag & VROOT) { + printf("detrunc(): can't truncate root directory, clust %d, offset %d\n", + dep->de_dirclust, dep->de_diroffset); + return EINVAL; + } + + vnode_pager_setsize(DETOV(dep), length); + + if (dep->de_FileSize < length) + return deextend(dep, length, cred); + + /* + * If the desired length is 0 then remember the starting cluster of + * the file and set the StartCluster field in the directory entry + * to 0. If the desired length is not zero, then get the number of + * the last cluster in the shortened file. Then get the number of + * the first cluster in the part of the file that is to be freed. + * Then set the next cluster pointer in the last cluster of the + * file to CLUST_EOFE. + */ + if (length == 0) { + chaintofree = dep->de_StartCluster; + dep->de_StartCluster = 0; + eofentry = ~0; + } else { + if (error = pcbmap(dep, de_clcount(pmp, length) - 1, 0, &eofentry)) { +#ifdef MSDOSFS_DEBUG + printf("detrunc(): pcbmap fails %d\n", error); +#endif + return error; + } + } + + fc_purge(dep, (length + pmp->pm_crbomask) >> pmp->pm_cnshift); + + /* + * If the new length is not a multiple of the cluster size then we + * must zero the tail end of the new last cluster in case it + * becomes part of the file again because of a seek. + */ + if ((boff = length & pmp->pm_crbomask) != 0) { + /* + * should read from file vnode or filesystem vnode + * depending on if file or dir + */ + if (isadir) { + bn = cntobn(pmp, eofentry); + error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, + NOCRED, &bp); + } else { + bn = de_blk(pmp, length); + error = bread(DETOV(dep), bn, pmp->pm_bpcluster, + NOCRED, &bp); + } + if (error) { +#ifdef MSDOSFS_DEBUG + printf("detrunc(): bread fails %d\n", error); +#endif + return error; + } + vnode_pager_uncache(DETOV(dep)); /* what's this for? */ + /* + * is this the right place for it? + */ + bzero(bp->b_data + boff, pmp->pm_bpcluster - boff); + if (flags & IO_SYNC) + bwrite(bp); + else + bdwrite(bp); + } + + /* + * Write out the updated directory entry. Even if the update fails + * we free the trailing clusters. + */ + dep->de_FileSize = length; + dep->de_flag |= DE_UPDATE; + vflags = (length > 0 ? V_SAVE : 0) | V_SAVEMETA; + vinvalbuf(DETOV(dep), vflags, cred, p, 0, 0); + allerror = deupdat(dep, &time, 1); +#ifdef MSDOSFS_DEBUG + printf("detrunc(): allerror %d, eofentry %d\n", + allerror, eofentry); +#endif + + /* + * If we need to break the cluster chain for the file then do it + * now. + */ + if (eofentry != ~0) { + error = fatentry(FAT_GET_AND_SET, pmp, eofentry, + &chaintofree, CLUST_EOFE); + if (error) { +#ifdef MSDOSFS_DEBUG + printf("detrunc(): fatentry errors %d\n", error); +#endif + return error; + } + fc_setcache(dep, FC_LASTFC, (length - 1) >> pmp->pm_cnshift, + eofentry); + } + + /* + * Now free the clusters removed from the file because of the + * truncation. + */ + if (chaintofree != 0 && !MSDOSFSEOF(chaintofree)) + freeclusterchain(pmp, chaintofree); + + return allerror; +} + +/* + * Extend the file described by dep to length specified by length. + */ +int +deextend(dep, length, cred) + struct denode *dep; + off_t length; + struct ucred *cred; +{ + struct msdosfsmount *pmp = dep->de_pmp; + u_long count; + int error; + + /* + * The root of a DOS filesystem cannot be extended. + */ + if (DETOV(dep)->v_flag & VROOT) + return EINVAL; + + /* + * Directories can only be extended by the superuser. + * Is this really important? + */ + if (dep->de_Attributes & ATTR_DIRECTORY) { + if (error = suser(cred, NULL)) + return error; + } + + if (length <= dep->de_FileSize) + panic("deextend: file too large"); + + /* + * Compute the number of clusters to allocate. + */ + count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize); + if (count > 0) { + if (count > pmp->pm_freeclustercount) + return ENOSPC; + if (error = extendfile(dep, count, NULL, NULL, DE_CLEAR)) { + /* truncate the added clusters away again */ + (void) detrunc(dep, dep->de_FileSize, 0, cred, NULL); + return error; + } + } + + dep->de_flag |= DE_UPDATE; + dep->de_FileSize = length; + return deupdat(dep, &time, 1); +} + +/* + * Move a denode to its correct hash queue after the file it represents has + * been moved to a new directory. + */ +int reinsert(dep) + struct denode *dep; +{ + union dehead *deh; + + /* + * Fix up the denode cache. If the denode is for a directory, + * there is nothing to do since the hash is based on the starting + * cluster of the directory file and that hasn't changed. If for a + * file the hash is based on the location of the directory entry, + * so we must remove it from the cache and re-enter it with the + * hash based on the new location of the directory entry. + */ + if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) { + msdosfs_hashrem(dep); + msdosfs_hashins(dep); + } + return 0; +} + +int +msdosfs_reclaim(ap) + struct vop_reclaim_args /* { + struct vnode *a_vp; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct denode *dep = VTODE(vp); + int i; + extern int prtactive; + +#ifdef MSDOSFS_DEBUG + printf("msdosfs_reclaim(): dep %08x, file %s, refcnt %d\n", + dep, dep->de_Name, dep->de_refcnt); +#endif + + if (prtactive && vp->v_usecount != 0) + vprint("msdosfs_reclaim(): pushing active", vp); + + /* + * Remove the denode from the denode hash chain we are in. + */ + msdosfs_hashrem(dep); + + cache_purge(vp); + /* + * Indicate that one less file on the filesystem is open. + */ + if (dep->de_devvp) { + vrele(dep->de_devvp); + dep->de_devvp = 0; + } + + dep->de_flag = 0; + + FREE(dep, M_MSDOSFSNODE); + vp->v_data = NULL; + + return 0; +} + +int +msdosfs_inactive(ap) + struct vop_inactive_args /* { + struct vnode *a_vp; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct denode *dep = VTODE(vp); + int error = 0; + extern int prtactive; + +#ifdef MSDOSFS_DEBUG + printf("msdosfs_inactive(): dep %08x, de_Name[0] %x\n", dep, dep->de_Name[0]); +#endif + + if (prtactive && vp->v_usecount != 0) + vprint("msdosfs_inactive(): pushing active", vp); + + /* + * Get rid of denodes related to stale file handles. Hmmm, what + * does this really do? + */ + if (dep->de_Name[0] == SLOT_DELETED) { + if ((vp->v_flag & VXLOCK) == 0) + vgone(vp); + return 0; + } + + /* + * If the file has been deleted and it is on a read/write + * filesystem, then truncate the file, and mark the directory slot + * as empty. (This may not be necessary for the dos filesystem.) + */ +#ifdef MSDOSFS_DEBUG + printf("msdosfs_inactive(): dep %08x, refcnt %d, mntflag %x, MNT_RDONLY %x\n", + dep, dep->de_refcnt, vp->v_mount->mnt_flag, MNT_RDONLY); +#endif + VOP_LOCK(vp); + if (dep->de_refcnt <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { + error = detrunc(dep, (u_long) 0, 0, NOCRED, NULL); + dep->de_flag |= DE_UPDATE; + dep->de_Name[0] = SLOT_DELETED; + } + DE_UPDAT(dep, &time, 0); + VOP_UNLOCK(vp); + dep->de_flag = 0; + + /* + * If we are done with the denode, then reclaim it so that it can + * be reused now. + */ +#ifdef MSDOSFS_DEBUG + printf("msdosfs_inactive(): v_usecount %d, de_Name[0] %x\n", vp->v_usecount, + dep->de_Name[0]); +#endif + if (vp->v_usecount == 0 && dep->de_Name[0] == SLOT_DELETED) + vgone(vp); + return error; +} diff --git a/sys/msdosfs/msdosfs_fat.c b/sys/msdosfs/msdosfs_fat.c new file mode 100644 index 0000000..9a2f117 --- /dev/null +++ b/sys/msdosfs/msdosfs_fat.c @@ -0,0 +1,984 @@ +/* $Id$ */ +/* $NetBSD: msdosfs_fat.c,v 1.12 1994/08/21 18:44:04 ws Exp $ */ + +/*- + * Copyright (C) 1994 Wolfgang Solfrank. + * Copyright (C) 1994 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 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. + * + * October 1992 + */ + +/* + * kernel include files. + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/file.h> +#include <sys/namei.h> +#include <sys/mount.h> /* to define statfs structure */ +#include <sys/vnode.h> /* to define vattr structure */ +#include <sys/errno.h> + +/* + * msdosfs include files. + */ +#include <msdosfs/bpb.h> +#include <msdosfs/msdosfsmount.h> +#include <msdosfs/direntry.h> +#include <msdosfs/denode.h> +#include <msdosfs/fat.h> + +/* + * Fat cache stats. + */ +int fc_fileextends; /* # of file extends */ +int fc_lfcempty; /* # of time last file cluster cache entry + * was empty */ +int fc_bmapcalls; /* # of times pcbmap was called */ + +#define LMMAX 20 +int fc_lmdistance[LMMAX]; /* counters for how far off the last + * cluster mapped entry was. */ +int fc_largedistance; /* off by more than LMMAX */ + +/* Byte offset in FAT on filesystem pmp, cluster cn */ +#define FATOFS(pmp, cn) (FAT12(pmp) ? (cn) * 3 / 2 : (cn) * 2) + +void fc_lookup(struct denode *dep, u_long findcn, + u_long *frcnp, u_long *fsrcnp); +void fc_purge(struct denode *dep, u_int frcn); + +static void +fatblock(pmp, ofs, bnp, sizep, bop) + struct msdosfsmount *pmp; + u_long ofs; + u_long *bnp; + u_long *sizep; + u_long *bop; +{ + u_long bn, size; + + bn = ofs / pmp->pm_fatblocksize * pmp->pm_fatblocksec; + size = min(pmp->pm_fatblocksec, pmp->pm_FATsecs - bn) + * pmp->pm_BytesPerSec; + bn += pmp->pm_fatblk; + if (bnp) + *bnp = bn; + if (sizep) + *sizep = size; + if (bop) + *bop = ofs % pmp->pm_fatblocksize; +} + +/* + * Map the logical cluster number of a file into a physical disk sector + * that is filesystem relative. + * + * dep - address of denode representing the file of interest + * findcn - file relative cluster whose filesystem relative cluster number + * and/or block number are/is to be found + * bnp - address of where to place the file system relative block number. + * If this pointer is null then don't return this quantity. + * cnp - address of where to place the file system relative cluster number. + * If this pointer is null then don't return this quantity. + * + * NOTE: Either bnp or cnp must be non-null. + * This function has one side effect. If the requested file relative cluster + * is beyond the end of file, then the actual number of clusters in the file + * is returned in *cnp. This is useful for determining how long a directory is. + * If cnp is null, nothing is returned. + */ +int +pcbmap(dep, findcn, bnp, cnp) + struct denode *dep; + u_long findcn; /* file relative cluster to get */ + daddr_t *bnp; /* returned filesys relative blk number */ + u_long *cnp; /* returned cluster number */ +{ + int error; + u_long i; + u_long cn; + u_long prevcn; + u_long byteoffset; + u_long bn; + u_long bo; + struct buf *bp = NULL; + u_long bp_bn = -1; + struct msdosfsmount *pmp = dep->de_pmp; + u_long bsize; + int fat12 = FAT12(pmp); /* 12 bit fat */ + + fc_bmapcalls++; + + /* + * If they don't give us someplace to return a value then don't + * bother doing anything. + */ + if (bnp == NULL && cnp == NULL) + return 0; + + cn = dep->de_StartCluster; + /* + * The "file" that makes up the root directory is contiguous, + * permanently allocated, of fixed size, and is not made up of + * clusters. If the cluster number is beyond the end of the root + * directory, then return the number of clusters in the file. + */ + if (cn == MSDOSFSROOT) { + if (dep->de_Attributes & ATTR_DIRECTORY) { + if (findcn * pmp->pm_SectPerClust > pmp->pm_rootdirsize) { + if (cnp) + *cnp = pmp->pm_rootdirsize / pmp->pm_SectPerClust; + return E2BIG; + } + if (bnp) + *bnp = pmp->pm_rootdirblk + (findcn * pmp->pm_SectPerClust); + if (cnp) + *cnp = MSDOSFSROOT; + return 0; + } else { /* just an empty file */ + if (cnp) + *cnp = 0; + return E2BIG; + } + } + + /* + * Rummage around in the fat cache, maybe we can avoid tromping + * thru every fat entry for the file. And, keep track of how far + * off the cache was from where we wanted to be. + */ + i = 0; + fc_lookup(dep, findcn, &i, &cn); + if ((bn = findcn - i) >= LMMAX) + fc_largedistance++; + else + fc_lmdistance[bn]++; + + /* + * Handle all other files or directories the normal way. + */ + prevcn = 0; + for (; i < findcn; i++) { + if (MSDOSFSEOF(cn)) + goto hiteof; + byteoffset = FATOFS(pmp, cn); + fatblock(pmp, byteoffset, &bn, &bsize, &bo); + if (bn != bp_bn) { + if (bp) + brelse(bp); + error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); + if (error) + return error; + bp_bn = bn; + } + prevcn = cn; + cn = getushort(&bp->b_data[bo]); + if (fat12) { + if (prevcn & 1) + cn >>= 4; + cn &= 0x0fff; + /* + * Force the special cluster numbers in the range + * 0x0ff0-0x0fff to be the same as for 16 bit + * cluster numbers to let the rest of msdosfs think + * it is always dealing with 16 bit fats. + */ + if ((cn & 0x0ff0) == 0x0ff0) + cn |= 0xf000; + } + } + + if (!MSDOSFSEOF(cn)) { + if (bp) + brelse(bp); + if (bnp) + *bnp = cntobn(pmp, cn); + if (cnp) + *cnp = cn; + fc_setcache(dep, FC_LASTMAP, i, cn); + return 0; + } + +hiteof:; + if (cnp) + *cnp = i; + if (bp) + brelse(bp); + /* update last file cluster entry in the fat cache */ + fc_setcache(dep, FC_LASTFC, i - 1, prevcn); + return E2BIG; +} + +/* + * Find the closest entry in the fat cache to the cluster we are looking + * for. + */ +void fc_lookup(dep, findcn, frcnp, fsrcnp) + struct denode *dep; + u_long findcn; + u_long *frcnp; + u_long *fsrcnp; +{ + int i; + u_long cn; + struct fatcache *closest = 0; + + for (i = 0; i < FC_SIZE; i++) { + cn = dep->de_fc[i].fc_frcn; + if (cn != FCE_EMPTY && cn <= findcn) { + if (closest == 0 || cn > closest->fc_frcn) + closest = &dep->de_fc[i]; + } + } + if (closest) { + *frcnp = closest->fc_frcn; + *fsrcnp = closest->fc_fsrcn; + } +} + +/* + * Purge the fat cache in denode dep of all entries relating to file + * relative cluster frcn and beyond. + */ +void fc_purge(dep, frcn) + struct denode *dep; + u_int frcn; +{ + int i; + struct fatcache *fcp; + + fcp = dep->de_fc; + for (i = 0; i < FC_SIZE; i++, fcp++) { + if (fcp->fc_frcn >= frcn) + fcp->fc_frcn = FCE_EMPTY; + } +} + +/* + * Update all copies of the fat. The first copy is updated last. + * + * pmp - msdosfsmount structure for filesystem to update + * bp - addr of modified fat block + * fatbn - block number relative to begin of filesystem of the modified fat block. + */ +void +updatefats(pmp, bp, fatbn) + struct msdosfsmount *pmp; + struct buf *bp; + u_long fatbn; +{ + int i; + struct buf *bpn; + +#ifdef MSDOSFS_DEBUG + printf("updatefats(pmp %08x, bp %08x, fatbn %d)\n", + pmp, bp, fatbn); +#endif + + /* + * Now copy the block(s) of the modified fat to the other copies of + * the fat and write them out. This is faster than reading in the + * other fats and then writing them back out. This could tie up + * the fat for quite a while. Preventing others from accessing it. + * To prevent us from going after the fat quite so much we use + * delayed writes, unless they specfied "synchronous" when the + * filesystem was mounted. If synch is asked for then use + * bwrite()'s and really slow things down. + */ + for (i = 1; i < pmp->pm_FATs; i++) { + fatbn += pmp->pm_FATsecs; + /* getblk() never fails */ + bpn = getblk(pmp->pm_devvp, fatbn, bp->b_bcount, 0, 0); + bcopy(bp->b_data, bpn->b_data, bp->b_bcount); + if (pmp->pm_waitonfat) + bwrite(bpn); + else + bdwrite(bpn); + } + /* + * Write out the first fat last. + */ + if (pmp->pm_waitonfat) + bwrite(bp); + else + bdwrite(bp); +} + +/* + * Updating entries in 12 bit fats is a pain in the butt. + * + * The following picture shows where nibbles go when moving from a 12 bit + * cluster number into the appropriate bytes in the FAT. + * + * byte m byte m+1 byte m+2 + * +----+----+ +----+----+ +----+----+ + * | 0 1 | | 2 3 | | 4 5 | FAT bytes + * +----+----+ +----+----+ +----+----+ + * + * +----+----+----+ +----+----+----+ + * | 3 0 1 | | 4 5 2 | + * +----+----+----+ +----+----+----+ + * cluster n cluster n+1 + * + * Where n is even. m = n + (n >> 2) + * + */ +extern __inline void +usemap_alloc(pmp, cn) + struct msdosfsmount *pmp; + u_long cn; +{ + pmp->pm_inusemap[cn / N_INUSEBITS] + |= 1 << (cn % N_INUSEBITS); + pmp->pm_freeclustercount--; +} + +extern __inline void +usemap_free(pmp, cn) + struct msdosfsmount *pmp; + u_long cn; +{ + pmp->pm_freeclustercount++; + pmp->pm_inusemap[cn / N_INUSEBITS] &= ~(1 << (cn % N_INUSEBITS)); +} + +int +clusterfree(pmp, cluster, oldcnp) + struct msdosfsmount *pmp; + u_long cluster; + u_long *oldcnp; +{ + int error; + u_long oldcn; + + error = fatentry(FAT_GET_AND_SET, pmp, cluster, &oldcn, MSDOSFSFREE); + if (error == 0) { + /* + * If the cluster was successfully marked free, then update + * the count of free clusters, and turn off the "allocated" + * bit in the "in use" cluster bit map. + */ + usemap_free(pmp, cluster); + if (oldcnp) + *oldcnp = oldcn; + } + return error; +} + +/* + * Get or Set or 'Get and Set' the cluster'th entry in the fat. + * + * function - whether to get or set a fat entry + * pmp - address of the msdosfsmount structure for the filesystem + * whose fat is to be manipulated. + * cn - which cluster is of interest + * oldcontents - address of a word that is to receive the contents of the + * cluster'th entry if this is a get function + * newcontents - the new value to be written into the cluster'th element of + * the fat if this is a set function. + * + * This function can also be used to free a cluster by setting the fat entry + * for a cluster to 0. + * + * All copies of the fat are updated if this is a set function. NOTE: If + * fatentry() marks a cluster as free it does not update the inusemap in + * the msdosfsmount structure. This is left to the caller. + */ +int +fatentry(function, pmp, cn, oldcontents, newcontents) + int function; + struct msdosfsmount *pmp; + u_long cn; + u_long *oldcontents; + u_long newcontents; +{ + int error; + u_long readcn; + u_long bn, bo, bsize, byteoffset; + struct buf *bp; + + /* + * printf("fatentry(func %d, pmp %08x, clust %d, oldcon %08x, newcon %d)\n", + * function, pmp, cluster, oldcontents, newcontents); + */ + +#ifdef DIAGNOSTIC + /* + * Be sure they asked us to do something. + */ + if ((function & (FAT_SET | FAT_GET)) == 0) { + printf("fatentry(): function code doesn't specify get or set\n"); + return EINVAL; + } + + /* + * If they asked us to return a cluster number but didn't tell us + * where to put it, give them an error. + */ + if ((function & FAT_GET) && oldcontents == NULL) { + printf("fatentry(): get function with no place to put result\n"); + return EINVAL; + } +#endif + + /* + * Be sure the requested cluster is in the filesystem. + */ + if (cn < CLUST_FIRST || cn > pmp->pm_maxcluster) + return EINVAL; + + byteoffset = FATOFS(pmp, cn); + fatblock(pmp, byteoffset, &bn, &bsize, &bo); + if (error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp)) + return error; + + if (function & FAT_GET) { + readcn = getushort(&bp->b_data[bo]); + if (FAT12(pmp)) { + if (cn & 1) + readcn >>= 4; + readcn &= 0x0fff; + /* map certain 12 bit fat entries to 16 bit */ + if ((readcn & 0x0ff0) == 0x0ff0) + readcn |= 0xf000; + } + *oldcontents = readcn; + } + if (function & FAT_SET) { + if (FAT12(pmp)) { + readcn = getushort(&bp->b_data[bo]); + if (cn & 1) { + readcn &= 0x000f; + readcn |= newcontents << 4; + } else { + readcn &= 0xf000; + readcn |= newcontents & 0xfff; + } + putushort(&bp->b_data[bo], readcn); + } else + putushort(&bp->b_data[bo], newcontents); + updatefats(pmp, bp, bn); + bp = NULL; + pmp->pm_fmod = 1; + } + if (bp) + brelse(bp); + return 0; +} + +/* + * Update a contiguous cluster chain + * + * pmp - mount point + * start - first cluster of chain + * count - number of clusters in chain + * fillwith - what to write into fat entry of last cluster + */ +static int +fatchain(pmp, start, count, fillwith) + struct msdosfsmount *pmp; + u_long start; + u_long count; + u_long fillwith; +{ + int error; + u_long bn, bo, bsize, byteoffset, readcn, newc; + struct buf *bp; + +#ifdef MSDOSFS_DEBUG + printf("fatchain(pmp %08x, start %d, count %d, fillwith %d)\n", + pmp, start, count, fillwith); +#endif + /* + * Be sure the clusters are in the filesystem. + */ + if (start < CLUST_FIRST || start + count - 1 > pmp->pm_maxcluster) + return EINVAL; + + while (count > 0) { + byteoffset = FATOFS(pmp, start); + fatblock(pmp, byteoffset, &bn, &bsize, &bo); + if (error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp)) + return error; + while (count > 0) { + start++; + newc = --count > 0 ? start : fillwith; + if (FAT12(pmp)) { + readcn = getushort(&bp->b_data[bo]); + if (start & 1) { + readcn &= 0xf000; + readcn |= newc & 0xfff; + } else { + readcn &= 0x000f; + readcn |= newc << 4; + } + putushort(&bp->b_data[bo], readcn); + bo++; + if (!(start & 1)) + bo++; + } else { + putushort(&bp->b_data[bo], newc); + bo += 2; + } + if (bo >= bsize) + break; + } + updatefats(pmp, bp, bn); + } + pmp->pm_fmod = 1; + return 0; +} + +/* + * Check the length of a free cluster chain starting at start. + * + * pmp - mount point + * start - start of chain + * count - maximum interesting length + */ +int +chainlength(pmp, start, count) + struct msdosfsmount *pmp; + u_long start; + u_long count; +{ + u_long idx, max_idx; + u_int map; + u_long len; + + max_idx = pmp->pm_maxcluster / N_INUSEBITS; + idx = start / N_INUSEBITS; + start %= N_INUSEBITS; + map = pmp->pm_inusemap[idx]; + map &= ~((1 << start) - 1); + if (map) { + len = ffs(map) - 1 - start; + return len > count ? count : len; + } + len = N_INUSEBITS - start; + if (len >= count) + return count; + while (++idx <= max_idx) { + if (len >= count) + break; + if (map = pmp->pm_inusemap[idx]) { + len += ffs(map) - 1; + break; + } + len += N_INUSEBITS; + } + return len > count ? count : len; +} + +/* + * Allocate contigous free clusters. + * + * pmp - mount point. + * start - start of cluster chain. + * count - number of clusters to allocate. + * fillwith - put this value into the fat entry for the + * last allocated cluster. + * retcluster - put the first allocated cluster's number here. + * got - how many clusters were actually allocated. + */ +int +chainalloc(pmp, start, count, fillwith, retcluster, got) + struct msdosfsmount *pmp; + u_long start; + u_long count; + u_long fillwith; + u_long *retcluster; + u_long *got; +{ + int error; + + error = fatchain(pmp, start, count, fillwith); + if (error == 0) { +#ifdef MSDOSFS_DEBUG + printf("clusteralloc(): allocated cluster chain at %d (%d clusters)\n", + start, count); +#endif + if (retcluster) + *retcluster = start; + if (got) + *got = count; + while (count-- > 0) + usemap_alloc(pmp, start++); + } + return error; +} + +/* + * Allocate contiguous free clusters. + * + * pmp - mount point. + * start - preferred start of cluster chain. + * count - number of clusters requested. + * fillwith - put this value into the fat entry for the + * last allocated cluster. + * retcluster - put the first allocated cluster's number here. + * got - how many clusters were actually allocated. + */ +int +clusteralloc(pmp, start, count, fillwith, retcluster, got) + struct msdosfsmount *pmp; + u_long start; + u_long count; + u_long fillwith; + u_long *retcluster; + u_long *got; +{ + int error; + u_long idx; + u_long len, newst, foundcn, foundl, cn, l; + u_int map; + +#ifdef MSDOSFS_DEBUG + printf("clusteralloc(): find %d clusters\n",count); +#endif + if (start) { + if ((len = chainlength(pmp, start, count)) >= count) + return chainalloc(pmp, start, count, fillwith, retcluster, got); + } else { + /* + * This is a new file, initialize start + */ + struct timeval tv; + + microtime(&tv); + start = (tv.tv_usec >> 10)|tv.tv_usec; + len = 0; + } + + /* + * Start at a (pseudo) random place to maximize cluster runs + * under multiple writers. + */ + foundcn = newst = (start * 1103515245 + 12345) % (pmp->pm_maxcluster + 1); + foundl = 0; + + for (cn = newst; cn <= pmp->pm_maxcluster;) { + idx = cn / N_INUSEBITS; + map = pmp->pm_inusemap[idx]; + map |= (1 << (cn % N_INUSEBITS)) - 1; + if (map != (u_int)-1) { + cn = idx * N_INUSEBITS + ffs(map^(u_int)-1) - 1; + if ((l = chainlength(pmp, cn, count)) >= count) + return chainalloc(pmp, cn, count, fillwith, retcluster, got); + if (l > foundl) { + foundcn = cn; + foundl = l; + } + cn += l + 1; + continue; + } + cn += N_INUSEBITS - cn % N_INUSEBITS; + } + for (cn = 0; cn < newst;) { + idx = cn / N_INUSEBITS; + map = pmp->pm_inusemap[idx]; + map |= (1 << (cn % N_INUSEBITS)) - 1; + if (map != (u_int)-1) { + cn = idx * N_INUSEBITS + ffs(map^(u_int)-1) - 1; + if ((l = chainlength(pmp, cn, count)) >= count) + return chainalloc(pmp, cn, count, fillwith, retcluster, got); + if (l > foundl) { + foundcn = cn; + foundl = l; + } + cn += l + 1; + continue; + } + cn += N_INUSEBITS - cn % N_INUSEBITS; + } + + if (!foundl) + return ENOSPC; + + if (len) + return chainalloc(pmp, start, len, fillwith, retcluster, got); + else + return chainalloc(pmp, foundcn, foundl, fillwith, retcluster, got); +} + + +/* + * Free a chain of clusters. + * + * pmp - address of the msdosfs mount structure for the filesystem + * containing the cluster chain to be freed. + * startcluster - number of the 1st cluster in the chain of clusters to be + * freed. + */ +int +freeclusterchain(pmp, cluster) + struct msdosfsmount *pmp; + u_long cluster; +{ + int error = 0; + struct buf *bp = NULL; + u_long bn, bo, bsize, byteoffset; + u_long readcn, lbn = -1; + + while (cluster >= CLUST_FIRST && cluster <= pmp->pm_maxcluster) { + byteoffset = FATOFS(pmp, cluster); + fatblock(pmp, byteoffset, &bn, &bsize, &bo); + if (lbn != bn) { + if (bp) + updatefats(pmp, bp, bn); + if (error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp)) + return error; + lbn = bn; + } + usemap_free(pmp, cluster); + readcn = getushort(&bp->b_data[bo]); + if (FAT12(pmp)) { + if (cluster & 1) { + cluster = readcn >> 4; + readcn &= 0x000f; + readcn |= MSDOSFSFREE << 4; + } else { + cluster = readcn; + readcn &= 0xf000; + readcn |= MSDOSFSFREE & 0xfff; + } + putushort(&bp->b_data[bo], readcn); + cluster &= 0x0fff; + if ((cluster&0x0ff0) == 0x0ff0) + cluster |= 0xf000; + } else { + cluster = readcn; + putushort(&bp->b_data[bo], MSDOSFSFREE); + } + } + if (bp) + updatefats(pmp, bp, bn); + return error; +} + +/* + * Read in fat blocks looking for free clusters. For every free cluster + * found turn off its corresponding bit in the pm_inusemap. + */ +int +fillinusemap(pmp) + struct msdosfsmount *pmp; +{ + struct buf *bp = NULL; + u_long cn, readcn; + int error; + int fat12 = FAT12(pmp); + u_long bn, bo, bsize, byteoffset; + + /* + * Mark all clusters in use, we mark the free ones in the fat scan + * loop further down. + */ + for (cn = 0; cn < (pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS; cn++) + pmp->pm_inusemap[cn] = (u_int)-1; + + /* + * Figure how many free clusters are in the filesystem by ripping + * through the fat counting the number of entries whose content is + * zero. These represent free clusters. + */ + pmp->pm_freeclustercount = 0; + for (cn = CLUST_FIRST; cn <= pmp->pm_maxcluster; cn++) { + byteoffset = FATOFS(pmp, cn); + bo = byteoffset % pmp->pm_fatblocksize; + if (!bo || !bp) { + /* Read new FAT block */ + if (bp) + brelse(bp); + fatblock(pmp, byteoffset, &bn, &bsize, NULL); + error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); + if (error) + return error; + } + readcn = getushort(&bp->b_data[bo]); + if (fat12) { + if (cn & 1) + readcn >>= 4; + readcn &= 0x0fff; + } + + if (readcn == 0) + usemap_free(pmp, cn); + } + brelse(bp); + return 0; +} + +/* + * Allocate a new cluster and chain it onto the end of the file. + * + * dep - the file to extend + * count - number of clusters to allocate + * bpp - where to return the address of the buf header for the first new + * file block + * ncp - where to put cluster number of the first newly allocated cluster + * If this pointer is 0, do not return the cluster number. + * flags - see fat.h + * + * NOTE: This function is not responsible for turning on the DE_UPDATE bit of + * the de_flag field of the denode and it does not change the de_FileSize + * field. This is left for the caller to do. + */ +int +extendfile(dep, count, bpp, ncp, flags) + struct denode *dep; + u_long count; + struct buf **bpp; + u_long *ncp; + int flags; +{ + int error = 0; + u_long frcn; + u_long cn, got; + struct msdosfsmount *pmp = dep->de_pmp; + struct buf *bp; + + /* + * Don't try to extend the root directory + */ + if (DETOV(dep)->v_flag & VROOT) { + printf("extendfile(): attempt to extend root directory\n"); + return ENOSPC; + } + + /* + * If the "file's last cluster" cache entry is empty, and the file + * is not empty, then fill the cache entry by calling pcbmap(). + */ + fc_fileextends++; + if (dep->de_fc[FC_LASTFC].fc_frcn == FCE_EMPTY && + dep->de_StartCluster != 0) { + fc_lfcempty++; + error = pcbmap(dep, 0xffff, 0, &cn); + /* we expect it to return E2BIG */ + if (error != E2BIG) + return error; + error = 0; + } + + while (count > 0) { + /* + * Allocate a new cluster chain and cat onto the end of the file. + * If the file is empty we make de_StartCluster point to the new + * block. Note that de_StartCluster being 0 is sufficient to be + * sure the file is empty since we exclude attempts to extend the + * root directory above, and the root dir is the only file with a + * startcluster of 0 that has blocks allocated (sort of). + */ + if (dep->de_StartCluster == 0) + cn = 0; + else + cn = dep->de_fc[FC_LASTFC].fc_fsrcn + 1; + if (error = clusteralloc(pmp, cn, count, CLUST_EOFE, &cn, &got)) + return error; + + count -= got; + + /* + * Give them the filesystem relative cluster number if they want + * it. + */ + if (ncp) { + *ncp = cn; + ncp = NULL; + } + + if (dep->de_StartCluster == 0) { + dep->de_StartCluster = cn; + frcn = 0; + } else { + error = fatentry(FAT_SET, pmp, dep->de_fc[FC_LASTFC].fc_fsrcn, + 0, cn); + if (error) { + clusterfree(pmp, cn, NULL); + return error; + } + + frcn = dep->de_fc[FC_LASTFC].fc_frcn + 1; + } + + /* + * Update the "last cluster of the file" entry in the denode's fat + * cache. + */ + fc_setcache(dep, FC_LASTFC, frcn + got - 1, cn + got - 1); + + if (flags & DE_CLEAR) { + while (got-- > 0) { + /* + * Get the buf header for the new block of the file. + */ + if (dep->de_Attributes & ATTR_DIRECTORY) + bp = getblk(pmp->pm_devvp, cntobn(pmp, cn++), + pmp->pm_bpcluster, 0, 0); + else { + bp = getblk(DETOV(dep), frcn++, pmp->pm_bpcluster, 0, 0); + /* + * Do the bmap now, as in msdosfs_write + */ + if (pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0)) + bp->b_blkno = -1; + if (bp->b_blkno == -1) + panic("extendfile: pcbmap"); + } + clrbuf(bp); + if (bpp) { + *bpp = bp; + bpp = NULL; + } else { + bp->b_flags |= B_AGE; + bawrite(bp); + } + } + } + } + + return 0; +} diff --git a/sys/msdosfs/msdosfs_lookup.c b/sys/msdosfs/msdosfs_lookup.c new file mode 100644 index 0000000..c67f888 --- /dev/null +++ b/sys/msdosfs/msdosfs_lookup.c @@ -0,0 +1,763 @@ +/* $Id$ */ +/* $NetBSD: msdosfs_lookup.c,v 1.14 1994/08/21 18:44:07 ws Exp $ */ + +/*- + * Copyright (C) 1994 Wolfgang Solfrank. + * Copyright (C) 1994 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 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. + * + * October 1992 + */ + +#include <sys/param.h> +#include <sys/namei.h> +#include <sys/buf.h> +#include <sys/vnode.h> +#include <sys/mount.h> + +#include <msdosfs/bpb.h> +#include <msdosfs/direntry.h> +#include <msdosfs/denode.h> +#include <msdosfs/msdosfsmount.h> +#include <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. + */ +int +msdosfs_lookup(ap) + struct vop_lookup_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + } */ *ap; +{ + struct vnode *vdp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct componentname *cnp = ap->a_cnp; + daddr_t bn; + int error; + int lockparent; + int wantparent; + int slotstatus; + +#define NONE 0 +#define FOUND 1 + int slotoffset = -1; + int slotcluster = -1; + int frcn; + u_long cluster; + int rootreloff; + int diroff; + int isadir; /* ~0 if found direntry is a directory */ + u_long scn; /* starting cluster number */ + struct vnode *pdp; + struct denode *dp; + struct denode *tdp; + struct msdosfsmount *pmp; + struct buf *bp = 0; + struct direntry *dep = NULL; + u_char dosfilename[12]; + int flags = cnp->cn_flags; + int nameiop = cnp->cn_nameiop; + +#ifdef MSDOSFS_DEBUG + printf("msdosfs_lookup(): looking for %s\n", cnp->cn_nameptr); +#endif + dp = VTODE(vdp); + pmp = dp->de_pmp; + *vpp = NULL; + lockparent = flags & LOCKPARENT; + wantparent = flags & (LOCKPARENT | WANTPARENT); +#ifdef MSDOSFS_DEBUG + printf("msdosfs_lookup(): vdp %08x, dp %08x, Attr %02x\n", + vdp, dp, dp->de_Attributes); +#endif + + /* + * 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) + return ENOTDIR; + + /* + * 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(vdp, vpp, cnp)) { + int vpid; + + if (error == ENOENT) + return error; + pdp = vdp; + vdp = *vpp; + dp = VTODE(vdp); + vpid = vdp->v_id; + if (pdp == vdp) { + VREF(vdp); + error = 0; + } else if (flags & ISDOTDOT) { + VOP_UNLOCK(pdp); + error = vget(vdp, 1); + if (!error && lockparent && (flags & ISLASTCN)) + error = VOP_LOCK(pdp); + } else { + error = vget(vdp, 1); + if (!lockparent || error || !(flags & ISLASTCN)) + VOP_UNLOCK(pdp); + } + + if (!error) { + if (vpid == vdp->v_id) { +#ifdef MSDOSFS_DEBUG + printf("msdosfs_lookup(): cache hit, vnode %08x, file %s\n", + vdp, dp->de_Name); +#endif + return 0; + } + vput(vdp); + if (lockparent && pdp != vdp && (flags & ISLASTCN)) + VOP_UNLOCK(pdp); + } + if (error = VOP_LOCK(pdp)) + return error; + vdp = pdp; + dp = VTODE(vdp); + *vpp = NULL; + } + + /* + * 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) && cnp->cn_nameptr[0] == '.' && + (cnp->cn_namelen == 1 || + (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.'))) { + isadir = ATTR_DIRECTORY; + scn = MSDOSFSROOT; +#ifdef MSDOSFS_DEBUG + printf("msdosfs_lookup(): looking for . or .. in root directory\n"); +#endif + cluster = MSDOSFSROOT; + diroff = MSDOSFSROOT_OFS; + goto foundroot; + } + + /* + * Don't search for free slots unless we are creating a filename + * and we are at the end of the pathname. + */ + slotstatus = FOUND; + if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN)) { + slotstatus = NONE; + slotoffset = -1; + } + + unix2dosfn((u_char *) cnp->cn_nameptr, dosfilename, cnp->cn_namelen); + dosfilename[11] = 0; +#ifdef MSDOSFS_DEBUG + printf("msdosfs_lookup(): dos version of filename %s, length %d\n", + dosfilename, cnp->cn_namelen); +#endif + /* + * Search the directory pointed at by vdp for the name pointed at + * by cnp->cn_nameptr. + */ + tdp = NULL; + /* + * 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. + */ + rootreloff = 0; + for (frcn = 0;; frcn++) { + if (error = pcbmap(dp, frcn, &bn, &cluster)) { + if (error == E2BIG) + break; + return error; + } + if (error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp)) + return error; + for (diroff = 0; diroff < pmp->pm_depclust; diroff++) { + dep = (struct direntry *) bp->b_data + 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) { + slotstatus = FOUND; + if (cluster == MSDOSFSROOT) + slotoffset = rootreloff; + else + slotoffset = diroff; + slotcluster = cluster; + } + if (dep->deName[0] == SLOT_EMPTY) { + brelse(bp); + goto notfound; + } + } else { + /* + * Ignore volume labels (anywhere, not just + * the root directory). + */ + if ((dep->deAttributes & ATTR_VOLUME) == 0 && + bcmp(dosfilename, dep->deName, 11) == 0) { +#ifdef MSDOSFS_DEBUG + printf("msdosfs_lookup(): match diroff %d, rootreloff %d\n", + diroff, rootreloff); +#endif + /* + * 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) + diroff = rootreloff; + dp->de_fndoffset = diroff; + dp->de_fndclust = cluster; + goto found; + } + } + rootreloff++; + } /* for (diroff = 0; .... */ + /* + * Release the buffer holding the directory cluster just + * searched. + */ + brelse(bp); + } /* for (frcn = 0; ; frcn++) */ +notfound:; + /* + * 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. + */ +#ifdef MSDOSFS_DEBUG + printf("msdosfs_lookup(): op %d, refcnt %d, slotstatus %d\n", + nameiop, dp->de_refcnt, slotstatus); + printf(" slotoffset %d, slotcluster %d\n", + slotoffset, slotcluster); +#endif + if ((nameiop == CREATE || nameiop == RENAME) && + (flags & ISLASTCN) && dp->de_refcnt != 0) { + if (slotstatus == NONE) { + dp->de_fndoffset = (u_long)-1; + dp->de_fndclust = (u_long)-1; + } else { +#ifdef MSDOSFS_DEBUG + printf("msdosfs_lookup(): saving empty slot location\n"); +#endif + dp->de_fndoffset = slotoffset; + dp->de_fndclust = slotcluster; + } + /* dp->de_flag |= DE_UPDATE; never update dos directories */ + cnp->cn_flags |= SAVENAME; + if (!lockparent)/* leave searched dir locked? */ + VOP_UNLOCK(vdp); + return EJUSTRETURN; + } + /* + * Insert name in cache as non-existant if not trying to create it. + */ + if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) + cache_enter(vdp, *vpp, cnp); + return ENOENT; + +found: ; + /* + * NOTE: We still have the buffer with matched directory entry at + * this point. + */ + isadir = dep->deAttributes & ATTR_DIRECTORY; + scn = getushort(dep->deStartCluster); + +foundroot:; + /* + * 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 entry. + */ + if (nameiop == DELETE && (flags & ISLASTCN)) { + if (dp->de_StartCluster == scn && isadir) { /* "." */ + VREF(vdp); + *vpp = vdp; + if (bp) + brelse(bp); + return 0; + } + error = deget(pmp, cluster, diroff, dep, &tdp); + if (error) { + if (bp) + brelse(bp); + return error; + } + *vpp = DETOV(tdp); + if (!lockparent) + VOP_UNLOCK(vdp); + if (bp) + brelse(bp); + return 0; + } + + /* + * If renaming. + */ + if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) { + if (dp->de_StartCluster == scn && isadir) { + if (bp) + brelse(bp); + return EISDIR; + } + error = deget(pmp, cluster, diroff, dep, &tdp); + if (error) { + if (bp) + brelse(bp); + return error; + } + *vpp = DETOV(tdp); + cnp->cn_flags |= SAVENAME; + if (!lockparent) + VOP_UNLOCK(vdp); + if (bp) + brelse(bp); + return 0; + } + + /* + * ? + */ + pdp = vdp; + if (flags & ISDOTDOT) { + VOP_UNLOCK(pdp); + error = deget(pmp, cluster, diroff, dep, &tdp); + if (error) { + VOP_LOCK(pdp); + if (bp) + brelse(bp); + return error; + } + if (lockparent && (flags & ISLASTCN) + && (error = VOP_LOCK(pdp))) { + vput(DETOV(tdp)); + return error; + } + *vpp = DETOV(tdp); + } else if (dp->de_StartCluster == scn && isadir) { /* "." */ + VREF(vdp); + *vpp = vdp; + } else { + error = deget(pmp, cluster, diroff, dep, &tdp); + if (error) { + if (bp) + brelse(bp); + return error; + } + if (!lockparent || !(flags & ISLASTCN)) + VOP_UNLOCK(pdp); + *vpp = DETOV(tdp); + } + if (bp) + brelse(bp); + + /* + * Insert name in cache if wanted. + */ + if (cnp->cn_flags & MAKEENTRY) + cache_enter(vdp, *vpp, cnp); + return 0; +} + +/* + * dep - directory entry to copy into the directory + * ddep - directory to add to + * depp - return the address of the denode for the created directory entry + * if depp != 0 + */ +int +createde(dep, ddep, depp) + struct denode *dep; + struct denode *ddep; + struct denode **depp; +{ + int bn; + int error; + u_long dirclust, diroffset; + struct direntry *ndep; + struct msdosfsmount *pmp = ddep->de_pmp; + struct buf *bp; + +#ifdef MSDOSFS_DEBUG + printf("createde(dep %08x, ddep %08x, depp %08x)\n", dep, ddep, depp); +#endif + + /* + * 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 + * case. + */ + if (ddep->de_fndclust == (u_long)-1) { + if (error = extendfile(ddep, 1, &bp, &dirclust, DE_CLEAR)) + return error; + ndep = (struct direntry *) bp->b_data; + /* + * Let caller know where we put the directory entry. + */ + ddep->de_fndclust = dirclust; + ddep->de_fndoffset = diroffset = 0; + /* + * Update the size of the directory + */ + ddep->de_FileSize += pmp->pm_bpcluster; + } else { + /* + * 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 = ddep->de_fndclust; + diroffset = ddep->de_fndoffset; + + error = readep(pmp, dirclust, diroffset, &bp, &ndep); + if (error) + return error; + } + DE_EXTERNALIZE(ndep, dep); + + /* + * If they want us to return with the denode gotten. + */ + if (depp) { + error = deget(pmp, dirclust, diroffset, ndep, depp); + if (error) + return error; + } + if (error = bwrite(bp)) { + vput(DETOV(*depp)); /* free the vnode we got on error */ + return error; + } + return 0; +} + +/* + * Read in a directory entry and mark it as being deleted. + */ +int +markdeleted(pmp, dirclust, diroffset) + struct msdosfsmount *pmp; + u_long dirclust; + u_long diroffset; +{ + int error; + struct direntry *ep; + struct buf *bp; + + error = readep(pmp, dirclust, diroffset, &bp, &ep); + if (error) + return error; + ep->deName[0] = SLOT_DELETED; + return bwrite(bp); +} + +/* + * 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. + */ +int +removede(pdep,dep) + struct denode *pdep; /* directory where the entry is removed */ + struct denode *dep; /* file to be removed */ +{ + struct msdosfsmount *pmp = pdep->de_pmp; + int error; + +#ifdef MSDOSFS_DEBUG + printf("removede(): filename %s\n", dep->de_Name); + printf("removede(): dep %08x, ndpcluster %d, ndpoffset %d\n", + dep, pdep->de_fndclust, pdep->de_fndoffset); +#endif + + /* + * 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, pdep->de_fndclust, pdep->de_fndoffset); + + if (error == 0) + dep->de_refcnt--; + return error; +} + +/* + * Be sure a directory is empty except for "." and "..". Return 1 if empty, + * return 0 if not empty or error. + */ +int +dosdirempty(dep) + struct denode *dep; +{ + int dei; + int error; + u_long cn; + daddr_t bn; + struct buf *bp; + struct msdosfsmount *pmp = dep->de_pmp; + struct direntry *dentp; + + /* + * 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. + */ + for (cn = 0;; cn++) { + error = pcbmap(dep, cn, &bn, 0); + if (error == E2BIG) + return 1; /* it's empty */ + error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, + &bp); + if (error) + return error; + dentp = (struct direntry *) bp->b_data; + 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) { + brelse(bp); + return 1; + } + /* + * Any names other than "." and ".." in a + * directory mean it is not empty. + */ + if (bcmp(dentp->deName, ". ", 11) && + bcmp(dentp->deName, ".. ", 11)) { + brelse(bp); +#ifdef MSDOSFS_DEBUG + printf("dosdirempty(): entry %d found %02x, %02x\n", + dei, dentp->deName[0], dentp->deName[1]); +#endif + return 0; /* not empty */ + } + } + dentp++; + } + brelse(bp); + } + /* NOTREACHED */ +} + +/* + * 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. + */ +int +doscheckpath(source, target) + struct denode *source; + struct denode *target; +{ + daddr_t scn; + struct denode dummy; + struct msdosfsmount *pmp; + struct direntry *ep; + struct denode *dep; + struct buf *bp = NULL; + int error = 0; + + dep = target; + if ((target->de_Attributes & ATTR_DIRECTORY) == 0 || + (source->de_Attributes & ATTR_DIRECTORY) == 0) { + error = ENOTDIR; + goto out; + } + if (dep->de_StartCluster == source->de_StartCluster) { + error = EEXIST; + goto out; + } + if (dep->de_StartCluster == MSDOSFSROOT) + goto out; + for (;;) { + if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) { + error = ENOTDIR; + goto out; + } + pmp = dep->de_pmp; + scn = dep->de_StartCluster; + error = bread(pmp->pm_devvp, cntobn(pmp, scn), + pmp->pm_bpcluster, NOCRED, &bp); + if (error) { + break; + } + ep = (struct direntry *) bp->b_data + 1; + if ((ep->deAttributes & ATTR_DIRECTORY) == 0 || + bcmp(ep->deName, ".. ", 11) != 0) { + error = ENOTDIR; + break; + } + scn = getushort(ep->deStartCluster); + if (scn == source->de_StartCluster) { + error = EINVAL; + break; + } + if (scn == MSDOSFSROOT) + break; + vput(DETOV(dep)); + /* NOTE: deget() clears dep on error */ + error = deget(pmp, scn, 0, ep, &dep); + brelse(bp); + bp = NULL; + if (error) + break; + } +out: ; + if (bp) + brelse(bp); + if (error == ENOTDIR) + printf("doscheckpath(): .. not a directory?\n"); + if (dep != NULL) + vput(DETOV(dep)); + return error; +} + +/* + * 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. + */ +int +readep(pmp, dirclu, dirofs, bpp, epp) + struct msdosfsmount *pmp; + u_long dirclu, dirofs; + struct buf **bpp; + struct direntry **epp; +{ + int error; + daddr_t bn; + + bn = detobn(pmp, dirclu, dirofs); + if (error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, bpp)) { + *bpp = NULL; + return error; + } + if (epp) + *epp = bptoep(pmp, *bpp, dirofs); + return 0; +} + + +/* + * 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. + */ +int +readde(dep, bpp, epp) + struct denode *dep; + struct buf **bpp; + struct direntry **epp; +{ + return readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset, + bpp, epp); +} diff --git a/sys/msdosfs/msdosfs_vfsops.c b/sys/msdosfs/msdosfs_vfsops.c new file mode 100644 index 0000000..ac9c284 --- /dev/null +++ b/sys/msdosfs/msdosfs_vfsops.c @@ -0,0 +1,733 @@ +/* $Id$ */ +/* $NetBSD: msdosfs_vfsops.c,v 1.19 1994/08/21 18:44:10 ws Exp $ */ + +/*- + * Copyright (C) 1994 Wolfgang Solfrank. + * Copyright (C) 1994 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 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. + * + * October 1992 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/namei.h> +#include <sys/proc.h> +#include <sys/kernel.h> +#include <sys/vnode.h> +#include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */ +#include <sys/mount.h> +#include <sys/buf.h> +#include <sys/file.h> +#include <sys/malloc.h> + +#include <msdosfs/bpb.h> +#include <msdosfs/bootsect.h> +#include <msdosfs/direntry.h> +#include <msdosfs/denode.h> +#include <msdosfs/msdosfsmount.h> +#include <msdosfs/fat.h> + +int msdosfsdoforce = 1; /* 1 = force unmount */ + +/* + * mp - path - addr in user space of mount point (ie /usr or whatever) + * data - addr in user space of mount params including the name of the block + * special file to treat as a filesystem. + */ +int +msdosfs_mount(mp, path, data, ndp, p) + struct mount *mp; + char *path; + caddr_t data; + struct nameidata *ndp; + struct proc *p; +{ + struct vnode *devvp; /* vnode for blk device to mount */ + struct msdosfs_args args; /* will hold data from mount request */ + struct msdosfsmount *pmp; /* msdosfs specific mount control block */ + int error, flags; + u_int size; + struct ucred *cred, *scred; + struct vattr va; + + /* + * Copy in the args for the mount request. + */ + if (error = copyin(data, (caddr_t) & args, sizeof(struct msdosfs_args))) + return error; + + /* + * If they just want to update then be sure we can do what is + * asked. Can't change a filesystem from read/write to read only. + * Why? And if they've supplied a new device file name then we + * continue, otherwise return. + */ + if (mp->mnt_flag & MNT_UPDATE) { + pmp = (struct msdosfsmount *) mp->mnt_data; + error = 0; + if (pmp->pm_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) { + flags = WRITECLOSE; + if (mp->mnt_flag & MNT_FORCE) + flags |= FORCECLOSE; + if (vfs_busy(mp)) + return EBUSY; + error = vflush(mp, NULLVP, flags); + vfs_unbusy(mp); + } + if (!error && (mp->mnt_flag & MNT_RELOAD)) + /* not yet implemented */ + error = EINVAL; + if (error) + return error; + if (pmp->pm_ronly && (mp->mnt_flag & MNT_RDONLY) == 0) + pmp->pm_ronly = 0; + if (args.fspec == 0) { + /* + * Process export requests. + */ + return vfs_export(mp, &pmp->pm_export, &args.export); + } + } else + pmp = NULL; + + /* + * check to see that the user in owns the target directory. + * Note the very XXX trick to make sure we're checking as the + * real user -- were mount() executable by anyone, this wouldn't + * be a problem. + * + * XXX there should be one consistent error out. + */ + cred = crdup(p->p_ucred); /* XXX */ + cred->cr_uid = p->p_cred->p_ruid; /* XXX */ + error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p); + if (error) { + crfree(cred); /* XXX */ + return error; + } + if (cred->cr_uid != 0) { + if (va.va_uid != cred->cr_uid) { + error = EACCES; + crfree(cred); /* XXX */ + return error; + } + + /* a user mounted it; we'll verify permissions when unmounting */ + mp->mnt_flag |= MNT_USER; + } + + /* + * Now, lookup the name of the block device this mount or name + * update request is to apply to. + */ + NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p); + scred = p->p_ucred; /* XXX */ + p->p_ucred = cred; /* XXX */ + error = namei(ndp); + p->p_ucred = scred; /* XXX */ + crfree(cred); /* XXX */ + if (error != 0) + return error; + + /* + * Be sure they've given us a block device to treat as a + * filesystem. And, that its major number is within the bdevsw + * table. + */ + devvp = ndp->ni_vp; + if (devvp->v_type != VBLK) { + vrele(devvp); + return ENOTBLK; + } + if (major(devvp->v_rdev) >= nblkdev) { + vrele(devvp); + return ENXIO; + } + + /* + * If this is an update, then make sure the vnode for the block + * special device is the same as the one our filesystem is in. + */ + if (mp->mnt_flag & MNT_UPDATE) { + if (devvp != pmp->pm_devvp) + error = EINVAL; + else + vrele(devvp); + } else { + + /* + * Well, it's not an update, it's a real mount request. + * Time to get dirty. + */ + error = mountmsdosfs(devvp, mp, p); + } + if (error) { + vrele(devvp); + return error; + } + + /* + * Copy in the name of the directory the filesystem is to be + * mounted on. Then copy in the name of the block special file + * representing the filesystem being mounted. And we clear the + * remainder of the character strings to be tidy. Set up the + * user id/group id/mask as specified by the user. Then, we try to + * fill in the filesystem stats structure as best we can with + * whatever applies from a dos file system. + */ + pmp = (struct msdosfsmount *) mp->mnt_data; + copyinstr(path, (caddr_t) mp->mnt_stat.f_mntonname, + sizeof(mp->mnt_stat.f_mntonname) - 1, &size); + bzero(mp->mnt_stat.f_mntonname + size, + sizeof(mp->mnt_stat.f_mntonname) - size); + copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); + bzero(mp->mnt_stat.f_mntfromname + size, + MNAMELEN - size); + pmp->pm_mounter = p->p_cred->p_ruid; + pmp->pm_gid = args.gid; + pmp->pm_uid = args.uid; + pmp->pm_mask = args.mask; + (void) msdosfs_statfs(mp, &mp->mnt_stat, p); +#ifdef MSDOSFS_DEBUG + printf("msdosfs_mount(): mp %x, pmp %x, inusemap %x\n", mp, pmp, pmp->pm_inusemap); +#endif + return 0; +} + +int +mountmsdosfs(devvp, mp, p) + struct vnode *devvp; + struct mount *mp; + struct proc *p; +{ + int i; + int bpc; + int bit; + int error; + int needclose; + int ronly = (mp->mnt_flag & MNT_RDONLY) != 0; + dev_t dev = devvp->v_rdev; + union bootsector *bsp; + struct msdosfsmount *pmp = NULL; + struct buf *bp0 = NULL; + struct byte_bpb33 *b33; + struct byte_bpb50 *b50; + + /* + * Multiple mounts of the same block special file aren't allowed. + * Make sure no one else has the special file open. And flush any + * old buffers from this filesystem. Presumably this prevents us + * from running into buffers that are the wrong blocksize. + */ + if (error = vfs_mountedon(devvp)) + return error; + if (vcount(devvp) > 1) + return EBUSY; + if (error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0)) + return error; + + /* + * Now open the block special file. + */ + if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD | FWRITE, FSCRED, p)) + return error; + needclose = 1; +#ifdef HDSUPPORT + /* + * Put this in when we support reading dos filesystems from + * partitioned harddisks. + */ + if (VOP_IOCTL(devvp, DIOCGPART, &msdosfspart, FREAD, NOCRED, p) == 0) { + } +#endif + + /* + * Read the boot sector of the filesystem, and then check the boot + * signature. If not a dos boot sector then error out. We could + * also add some checking on the bsOemName field. So far I've seen + * the following values: "IBM 3.3" "MSDOS3.3" "MSDOS5.0" + */ + if (error = bread(devvp, 0, 512, NOCRED, &bp0)) + goto error_exit; + bp0->b_flags |= B_AGE; + bsp = (union bootsector *) bp0->b_data; + b33 = (struct byte_bpb33 *) bsp->bs33.bsBPB; + b50 = (struct byte_bpb50 *) bsp->bs50.bsBPB; +#ifdef MSDOSFS_CHECKSIG + if (bsp->bs50.bsBootSectSig != BOOTSIG) { + error = EINVAL; + goto error_exit; + } +#endif + if ( bsp->bs50.bsJump[0] != 0xe9 && + (bsp->bs50.bsJump[0] != 0xeb || bsp->bs50.bsJump[2] != 0x90)) { + error = EINVAL; + goto error_exit; + } + + pmp = malloc(sizeof *pmp, M_MSDOSFSMNT, M_WAITOK); + bzero((caddr_t)pmp, sizeof *pmp); + pmp->pm_mountp = mp; + + /* + * Compute several useful quantities from the bpb in the + * bootsector. Copy in the dos 5 variant of the bpb then fix up + * the fields that are different between dos 5 and dos 3.3. + */ + pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec); + pmp->pm_SectPerClust = b50->bpbSecPerClust; + pmp->pm_ResSectors = getushort(b50->bpbResSectors); + pmp->pm_FATs = b50->bpbFATs; + pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts); + pmp->pm_Sectors = getushort(b50->bpbSectors); + pmp->pm_Media = b50->bpbMedia; + pmp->pm_FATsecs = getushort(b50->bpbFATsecs); + pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack); + pmp->pm_Heads = getushort(b50->bpbHeads); + + /* XXX - We should probably check more values here */ + if (!pmp->pm_BytesPerSec || !pmp->pm_SectPerClust || + !pmp->pm_Heads || pmp->pm_Heads > 255 || + !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) { + error = EINVAL; + goto error_exit; + } + + if (pmp->pm_Sectors == 0) { + pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); + pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors); + } else { + pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs); + pmp->pm_HugeSectors = pmp->pm_Sectors; + } + pmp->pm_fatblk = pmp->pm_ResSectors; + pmp->pm_rootdirblk = pmp->pm_fatblk + + (pmp->pm_FATs * pmp->pm_FATsecs); + pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)) + / + pmp->pm_BytesPerSec;/* in sectors */ + pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; + pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) / + pmp->pm_SectPerClust; + pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1; + pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec; + if (FAT12(pmp)) + /* + * This will usually be a floppy disk. This size makes sure + * that one fat entry will not be split across multiple + * blocks. + */ + pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec; + else + /* + * This will usually be a hard disk. Reading or writing one + * block should be quite fast. + */ + pmp->pm_fatblocksize = MAXBSIZE; + pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec; + + + if ((pmp->pm_rootdirsize % pmp->pm_SectPerClust) != 0) + printf("mountmsdosfs(): root directory is not a multiple of the clustersize in length\n"); + + /* + * Compute mask and shift value for isolating cluster relative byte + * offsets and cluster numbers from a file offset. + */ + bpc = pmp->pm_SectPerClust * pmp->pm_BytesPerSec; + pmp->pm_bpcluster = bpc; + pmp->pm_depclust = bpc / sizeof(struct direntry); + pmp->pm_crbomask = bpc - 1; + if (bpc == 0) { + error = EINVAL; + goto error_exit; + } + bit = 1; + for (i = 0; i < 32; i++) { + if (bit & bpc) { + if (bit ^ bpc) { + error = EINVAL; + goto error_exit; + } + pmp->pm_cnshift = i; + break; + } + bit <<= 1; + } + + pmp->pm_brbomask = 0x01ff; /* 512 byte blocks only (so far) */ + pmp->pm_bnshift = 9; /* shift right 9 bits to get bn */ + + /* + * Release the bootsector buffer. + */ + brelse(bp0); + bp0 = NULL; + + /* + * Allocate memory for the bitmap of allocated clusters, and then + * fill it in. + */ + pmp->pm_inusemap = malloc(((pmp->pm_maxcluster + N_INUSEBITS - 1) + / N_INUSEBITS) + * sizeof(*pmp->pm_inusemap), + M_MSDOSFSFAT, M_WAITOK); + + /* + * fillinusemap() needs pm_devvp. + */ + pmp->pm_dev = dev; + pmp->pm_devvp = devvp; + + /* + * Have the inuse map filled in. + */ + error = fillinusemap(pmp); + if (error) + goto error_exit; + + /* + * If they want fat updates to be synchronous then let them suffer + * the performance degradation in exchange for the on disk copy of + * the fat being correct just about all the time. I suppose this + * would be a good thing to turn on if the kernel is still flakey. + */ + pmp->pm_waitonfat = mp->mnt_flag & MNT_SYNCHRONOUS; + + /* + * Finish up. + */ + pmp->pm_ronly = ronly; + if (ronly == 0) + pmp->pm_fmod = 1; + mp->mnt_data = (qaddr_t) pmp; + mp->mnt_stat.f_fsid.val[0] = (long)dev; + mp->mnt_stat.f_fsid.val[1] = MOUNT_MSDOS; + mp->mnt_flag |= MNT_LOCAL; +#ifdef QUOTA + /* + * If we ever do quotas for DOS filesystems this would be a place + * to fill in the info in the msdosfsmount structure. You dolt, + * quotas on dos filesystems make no sense because files have no + * owners on dos filesystems. of course there is some empty space + * in the directory entry where we could put uid's and gid's. + */ +#endif + devvp->v_specflags |= SI_MOUNTEDON; + + return 0; + +error_exit:; + if (bp0) + brelse(bp0); + if (needclose) + (void) VOP_CLOSE(devvp, ronly ? FREAD : FREAD | FWRITE, + NOCRED, p); + if (pmp) { + if (pmp->pm_inusemap) + free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT); + free((caddr_t) pmp, M_MSDOSFSMNT); + mp->mnt_data = (qaddr_t) 0; + } + return error; +} + +int +msdosfs_start(mp, flags, p) + struct mount *mp; + int flags; + struct proc *p; +{ + return 0; +} + +/* + * Unmount the filesystem described by mp. + */ +int +msdosfs_unmount(mp, mntflags, p) + struct mount *mp; + int mntflags; + struct proc *p; +{ + int flags = 0; + int error; + struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; + struct vnode *vp = pmp->pm_devvp; + + /* only the mounter, or superuser can unmount */ + if ((p->p_cred->p_ruid != pmp->pm_mounter) && + (error = suser(p->p_ucred, &p->p_acflag))) + return error; + + if (mntflags & MNT_FORCE) { + if (!msdosfsdoforce) + return EINVAL; + flags |= FORCECLOSE; + } +#ifdef QUOTA +#endif + if (error = vflush(mp, NULLVP, flags)) + return error; + pmp->pm_devvp->v_specflags &= ~SI_MOUNTEDON; +#ifdef MSDOSFS_DEBUG + printf("msdosfs_umount(): just before calling VOP_CLOSE()\n"); + printf("flag %08x, usecount %d, writecount %d, holdcnt %d\n", + vp->v_flag, vp->v_usecount, vp->v_writecount, vp->v_holdcnt); + printf("lastr %d, id %d, mount %08x, op %08x\n", + vp->v_lastr, vp->v_id, vp->v_mount, vp->v_op); + printf("freef %08x, freeb %08x, mountf %08x, mountb %08x\n", + vp->v_freef, vp->v_freeb, vp->v_mountf, vp->v_mountb); + printf("cleanblkhd %08x, dirtyblkhd %08x, numoutput %d, type %d\n", + vp->v_cleanblkhd, vp->v_dirtyblkhd, vp->v_numoutput, vp->v_type); + printf("union %08x, tag %d, data[0] %08x, data[1] %08x\n", + vp->v_socket, vp->v_tag, vp->v_data[0], vp->v_data[1]); +#endif + error = VOP_CLOSE(pmp->pm_devvp, pmp->pm_ronly ? FREAD : FREAD | FWRITE, + NOCRED, p); + vrele(pmp->pm_devvp); + free((caddr_t) pmp->pm_inusemap, M_MSDOSFSFAT); + free((caddr_t) pmp, M_MSDOSFSMNT); + mp->mnt_data = (qaddr_t) 0; + mp->mnt_flag &= ~MNT_LOCAL; + return error; +} + +int +msdosfs_root(mp, vpp) + struct mount *mp; + struct vnode **vpp; +{ + struct denode *ndep; + struct msdosfsmount *pmp = (struct msdosfsmount *) (mp->mnt_data); + int error; + + error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, NULL, &ndep); +#ifdef MSDOSFS_DEBUG + printf("msdosfs_root(); mp %08x, pmp %08x, ndep %08x, vp %08x\n", + mp, pmp, ndep, DETOV(ndep)); +#endif + if (error == 0) + *vpp = DETOV(ndep); + return error; +} + +int +msdosfs_quotactl(mp, cmds, uid, arg, p) + struct mount *mp; + int cmds; + uid_t uid; + caddr_t arg; + struct proc *p; +{ +#ifdef QUOTA +#else + return EOPNOTSUPP; +#endif +} + +int +msdosfs_statfs(mp, sbp, p) + struct mount *mp; + struct statfs *sbp; + struct proc *p; +{ + struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; + + /* + * Fill in the stat block. + */ +#ifdef COMPAT_09 + sbp->f_type = 4; +#else + sbp->f_type = 0; +#endif + sbp->f_bsize = pmp->pm_bpcluster; + sbp->f_iosize = pmp->pm_bpcluster; + sbp->f_blocks = pmp->pm_nmbrofclusters; + sbp->f_bfree = pmp->pm_freeclustercount; + sbp->f_bavail = pmp->pm_freeclustercount; + sbp->f_files = pmp->pm_RootDirEnts; /* XXX */ + sbp->f_ffree = 0; /* what to put in here? */ + + /* + * Copy the mounted on and mounted from names into the passed in + * stat block, if it is not the one in the mount structure. + */ + if (sbp != &mp->mnt_stat) { + bcopy((caddr_t) mp->mnt_stat.f_mntonname, + (caddr_t) & sbp->f_mntonname[0], MNAMELEN); + bcopy((caddr_t) mp->mnt_stat.f_mntfromname, + (caddr_t) & sbp->f_mntfromname[0], MNAMELEN); + } +#if 0 + strncpy(&sbp->f_fstypename[0], mp->mnt_op->vfs_name, MFSNAMELEN); + sbp->f_fstypename[MFSNAMELEN] = '\0'; +#endif + return 0; +} + +int +msdosfs_sync(mp, waitfor, cred, p) + struct mount *mp; + int waitfor; + struct ucred *cred; + struct proc *p; +{ + struct vnode *vp; + struct denode *dep; + struct msdosfsmount *pmp; + int error; + int allerror = 0; + + pmp = (struct msdosfsmount *) mp->mnt_data; + + /* + * If we ever switch to not updating all of the fats all the time, + * this would be the place to update them from the first one. + */ + if (pmp->pm_fmod) + if (pmp->pm_ronly) + panic("msdosfs_sync: rofs mod"); + else { + /* update fats here */ + } + + /* + * Go thru in memory denodes and write them out along with + * unwritten file blocks. + */ +loop: + for (vp = mp->mnt_vnodelist.lh_first; vp; + vp = vp->v_mntvnodes.le_next) { + if (vp->v_mount != mp) /* not ours anymore */ + goto loop; + if (VOP_ISLOCKED(vp)) /* file is busy */ + continue; + dep = VTODE(vp); + if ((dep->de_flag & DE_UPDATE) == 0 && + vp->v_dirtyblkhd.lh_first == NULL) + continue; + if (vget(vp, 1)) /* not there anymore? */ + goto loop; + if (error = VOP_FSYNC(vp, cred, waitfor, p)) + allerror = error; + vput(vp); /* done with this one */ + } + + /* + * Flush filesystem control info. + */ + if (error = VOP_FSYNC(pmp->pm_devvp, cred, waitfor, p)) + allerror = error; + return allerror; +} + +int +msdosfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) + struct mount *mp; + struct fid *fhp; + struct mbuf *nam; + struct vnode **vpp; + int *exflagsp; + struct ucred **credanonp; +{ + struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; + struct defid *defhp = (struct defid *) fhp; + struct denode *dep; + struct netcred *np; + int error; + + np = vfs_export_lookup(mp, &pmp->pm_export, nam); + if (np == NULL) + return EACCES; + error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, + NULL, &dep); + if (error) { + *vpp = NULLVP; + return error; + } + *vpp = DETOV(dep); + *exflagsp = np->netc_exflags; + *credanonp = &np->netc_anon; + return 0; +} + + +int +msdosfs_vptofh(vp, fhp) + struct vnode *vp; + struct fid *fhp; +{ + struct denode *dep = VTODE(vp); + struct defid *defhp = (struct defid *) fhp; + + defhp->defid_len = sizeof(struct defid); + defhp->defid_dirclust = dep->de_dirclust; + defhp->defid_dirofs = dep->de_diroffset; + /* defhp->defid_gen = ip->i_gen; */ + return 0; +} + +int +msdosfs_vget(mp, ino, vpp) + struct mount *mp; + ino_t ino; + struct vnode **vpp; +{ + return EOPNOTSUPP; +} + +struct vfsops msdosfs_vfsops = { + msdosfs_mount, + msdosfs_start, + msdosfs_unmount, + msdosfs_root, + msdosfs_quotactl, + msdosfs_statfs, + msdosfs_sync, + msdosfs_vget, + msdosfs_fhtovp, + msdosfs_vptofh, + msdosfs_init +}; diff --git a/sys/msdosfs/msdosfs_vnops.c b/sys/msdosfs/msdosfs_vnops.c new file mode 100644 index 0000000..4294034 --- /dev/null +++ b/sys/msdosfs/msdosfs_vnops.c @@ -0,0 +1,1933 @@ +/* $Id$ */ +/* $NetBSD: msdosfs_vnops.c,v 1.20 1994/08/21 18:44:13 ws Exp $ */ + +/*- + * Copyright (C) 1994 Wolfgang Solfrank. + * Copyright (C) 1994 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 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. + * + * October 1992 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/namei.h> +#include <sys/resourcevar.h> /* defines plimit structure in proc struct */ +#include <sys/kernel.h> +#include <sys/file.h> /* define FWRITE ... */ +#include <sys/stat.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/mount.h> +#include <sys/vnode.h> +#include <miscfs/specfs/specdev.h> /* XXX */ /* defines v_rdev */ +#include <sys/malloc.h> +#include <sys/dir.h> /* defines dirent structure */ + +#include <msdosfs/bpb.h> +#include <msdosfs/direntry.h> +#include <msdosfs/denode.h> +#include <msdosfs/msdosfsmount.h> +#include <msdosfs/fat.h> +/* + * Some general notes: + * + * 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 cnp->cn_pnbuf, always on error, or + * only if the SAVESTART bit in cn_flags is clear on success. + */ +int +msdosfs_create(ap) + struct vop_create_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; +{ + struct componentname *cnp = ap->a_cnp; + struct denode ndirent; + struct denode *dep; + struct denode *pdep = VTODE(ap->a_dvp); + struct timespec ts; + int error; + +#ifdef MSDOSFS_DEBUG + printf("msdosfs_create(cnp %08x, vap %08x\n", cnp, ap->a_vap); +#endif + + /* + * 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. + */ +#ifdef DIAGNOSTIC + if ((cnp->cn_flags & SAVENAME) == 0) + panic("msdosfs_create: no name"); +#endif + bzero(&ndirent, sizeof(ndirent)); + TIMEVAL_TO_TIMESPEC(&time, &ts); + unix2dostime(&ts, &ndirent.de_Date, &ndirent.de_Time); + unix2dosfn((u_char *)cnp->cn_nameptr, ndirent.de_Name, cnp->cn_namelen); + ndirent.de_Attributes = (ap->a_vap->va_mode & VWRITE) ? 0 : ATTR_READONLY; + ndirent.de_StartCluster = 0; + ndirent.de_FileSize = 0; + ndirent.de_dev = pdep->de_dev; + ndirent.de_devvp = pdep->de_devvp; + if ((error = createde(&ndirent, pdep, &dep)) == 0) { + *ap->a_vpp = DETOV(dep); + if ((cnp->cn_flags & SAVESTART) == 0) + free(cnp->cn_pnbuf, M_NAMEI); + } else { + free(cnp->cn_pnbuf, M_NAMEI); + } + vput(ap->a_dvp); /* release parent dir */ + return error; +} + +int +msdosfs_mknod(ap) + struct vop_mknod_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; +{ + int error; + struct denode *pdep = VTODE(ap->a_dvp); + + switch (ap->a_vap->va_type) { + case VDIR: + error = msdosfs_mkdir((struct vop_mkdir_args *)ap); + break; + + case VREG: + error = msdosfs_create((struct vop_create_args *)ap); + break; + + default: + error = EINVAL; + free(ap->a_cnp->cn_pnbuf, M_NAMEI); + vput(ap->a_dvp); + break; + } + return error; +} + +int +msdosfs_open(ap) + struct vop_open_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + return 0; +} + +int +msdosfs_close(ap) + struct vop_close_args /* { + struct vnode *a_vp; + int a_fflag; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + struct denode *dep = VTODE(vp); + struct timespec ts; + + if (vp->v_usecount > 1 && !(dep->de_flag & DE_LOCKED)) { + TIMEVAL_TO_TIMESPEC(&time, &ts); + DE_TIMES(dep, &ts); + } + return 0; +} + +/* + * This routine will go into sys/kern/vfs_subr.c one day! + * + * Do the usual access checking. + * file_node, uid and gid are from the vnode in question, + * while acc_mode and cred are from the VOP_ACCESS parameter list. + */ +static int +vaccess(file_mode, uid, gid, acc_mode, cred) + mode_t file_mode; + uid_t uid; + gid_t gid; + mode_t acc_mode; + struct ucred *cred; +{ + mode_t mask; + int i; + register gid_t *gp; + + /* User id 0 always gets access. */ + if (cred->cr_uid == 0) + return 0; + + mask = 0; + + /* Otherwise, check the owner. */ + if (cred->cr_uid == uid) { + if (acc_mode & VEXEC) + mask |= S_IXUSR; + if (acc_mode & VREAD) + mask |= S_IRUSR; + if (acc_mode & VWRITE) + mask |= S_IWUSR; + return (file_mode & mask) == mask ? 0 : EACCES; + } + + /* Otherwise, check the groups. */ + for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) + if (gid == *gp) { + if (acc_mode & VEXEC) + mask |= S_IXGRP; + if (acc_mode & VREAD) + mask |= S_IRGRP; + if (acc_mode & VWRITE) + mask |= S_IWGRP; + return (file_mode & mask) == mask ? 0 : EACCES; + } + + /* Otherwise, check everyone else. */ + if (acc_mode & VEXEC) + mask |= S_IXOTH; + if (acc_mode & VREAD) + mask |= S_IROTH; + if (acc_mode & VWRITE) + mask |= S_IWOTH; + return (file_mode & mask) == mask ? 0 : EACCES; +} + +int +msdosfs_access(ap) + struct vop_access_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + mode_t dosmode; + struct denode *dep = VTODE(ap->a_vp); + struct msdosfsmount *pmp = dep->de_pmp; + + dosmode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) | + ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH)); + dosmode &= pmp->pm_mask; + + return vaccess(dosmode, pmp->pm_uid, pmp->pm_gid, ap->a_mode, ap->a_cred); +} + +int +msdosfs_getattr(ap) + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + u_int cn; + struct denode *dep = VTODE(ap->a_vp); + struct vattr *vap = ap->a_vap; + struct timespec ts; + + TIMEVAL_TO_TIMESPEC(&time, &ts); + DE_TIMES(dep, &ts); + 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 + * doesn't work. + */ + if (dep->de_Attributes & ATTR_DIRECTORY) { + if ((cn = dep->de_StartCluster) == MSDOSFSROOT) + cn = 1; + } else { + if ((cn = dep->de_dirclust) == MSDOSFSROOT) + cn = 1; + cn = (cn << 16) | (dep->de_diroffset & 0xffff); + } + vap->va_fileid = cn; + vap->va_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) | + ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH)); + vap->va_mode &= dep->de_pmp->pm_mask; + if (dep->de_Attributes & ATTR_DIRECTORY) + vap->va_mode |= S_IFDIR; + vap->va_nlink = 1; + vap->va_gid = dep->de_pmp->pm_gid; + vap->va_uid = dep->de_pmp->pm_uid; + vap->va_rdev = 0; + vap->va_size = dep->de_FileSize; + dos2unixtime(dep->de_Date, dep->de_Time, &vap->va_atime); + vap->va_mtime = vap->va_atime; +#ifndef MSDOSFS_NODIRMOD + if (vap->va_mode & S_IFDIR) + TIMEVAL_TO_TIMESPEC(&time, &vap->va_mtime); +#endif + vap->va_ctime = vap->va_atime; + vap->va_flags = dep->de_flag; + vap->va_gen = 0; + 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 = ap->a_vp->v_type; + return 0; +} + +int +msdosfs_setattr(ap) + struct vop_setattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + int error = 0; + struct denode *dep = VTODE(ap->a_vp); + struct vattr *vap = ap->a_vap; + struct ucred *cred = ap->a_cred; + +#ifdef MSDOSFS_DEBUG + printf("msdosfs_setattr(): vp %08x, vap %08x, cred %08x, p %08x\n", + ap->a_vp, vap, cred, ap->a_p); +#endif + 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 != VNOVAL) || + (vap->va_gid != VNOVAL)) { +#ifdef MSDOSFS_DEBUG + 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\n", + vap->va_uid, vap->va_gid); +#endif + return EINVAL; + } + + if (vap->va_size != VNOVAL) { + if (ap->a_vp->v_type == VDIR) + return EISDIR; + if (error = detrunc(dep, vap->va_size, 0, cred, ap->a_p)) + return error; + } + if (vap->va_mtime.ts_sec != VNOVAL) { + dep->de_flag |= DE_UPDATE; + if (error = deupdat(dep, &vap->va_mtime, 1)) + return error; + } + + /* + * 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; + else + dep->de_Attributes |= ATTR_READONLY; + dep->de_flag |= DE_UPDATE; + } + + if (vap->va_flags != VNOVAL) { + if (error = suser(cred, &ap->a_p->p_acflag)) + return error; + if (cred->cr_uid == 0) + dep->de_flag = vap->va_flags; + else { + dep->de_flag &= 0xffff0000; + dep->de_flag |= (vap->va_flags & 0xffff); + } + dep->de_flag |= DE_UPDATE; + } + return error; +} + +int +msdosfs_read(ap) + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; +{ + int error = 0; + int diff; + int isadir; + long n; + long on; + daddr_t bn; + daddr_t lbn; + daddr_t rablock; + int rasize; + struct buf *bp; + struct vnode *vp = ap->a_vp; + struct denode *dep = VTODE(vp); + struct msdosfsmount *pmp = dep->de_pmp; + struct uio *uio = ap->a_uio; + + /* + * If they didn't ask for any data, then we are done. + */ + if (uio->uio_resid == 0) + return 0; + if (uio->uio_offset < 0) + return EINVAL; + + isadir = dep->de_Attributes & ATTR_DIRECTORY; + do { + 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; + if (diff <= 0) + return 0; + /* convert cluster # to block # if a directory */ + if (isadir) { + error = pcbmap(dep, lbn, &lbn, 0); + if (error) + return error; + } + if (diff < n) + n = diff; + /* + * 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. + */ + if (isadir) { + error = bread(pmp->pm_devvp, lbn, pmp->pm_bpcluster, + NOCRED, &bp); + } else { + rablock = lbn + 1; + if (vp->v_lastr + 1 == lbn && + rablock * pmp->pm_bpcluster < dep->de_FileSize) { + rasize = pmp->pm_bpcluster; + error = breadn(vp, lbn, pmp->pm_bpcluster, + &rablock, &rasize, 1, + NOCRED, &bp); + } else { + error = bread(vp, lbn, pmp->pm_bpcluster, NOCRED, + &bp); + } + vp->v_lastr = lbn; + } + n = min(n, pmp->pm_bpcluster - bp->b_resid); + if (error) { + brelse(bp); + return error; + } + error = uiomove(bp->b_data + 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 0 + if (n + on == pmp->pm_bpcluster || + uio->uio_offset == dep->de_FileSize) + bp->b_flags |= B_AGE; +#endif + brelse(bp); + } while (error == 0 && uio->uio_resid > 0 && n != 0); + return error; +} + +/* + * Write data to a file or directory. + */ +int +msdosfs_write(ap) + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; +{ + int n; + int isadir; + int croffset; + int resid; + int osize; + int error = 0; + u_long count; + daddr_t bn, lastcn; + struct buf *bp; + int ioflag = ap->a_ioflag; + struct uio *uio = ap->a_uio; + struct proc *p = uio->uio_procp; + struct vnode *vp = ap->a_vp; + struct vnode *thisvp; + struct denode *dep = VTODE(vp); + struct msdosfsmount *pmp = dep->de_pmp; + struct ucred *cred = ap->a_cred; + +#ifdef MSDOSFS_DEBUG + printf("msdosfs_write(vp %08x, uio %08x, ioflag %08x, cred %08x\n", + vp, uio, ioflag, cred); + printf("msdosfs_write(): diroff %d, dirclust %d, startcluster %d\n", + dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster); +#endif + + switch (vp->v_type) { + case VREG: + if (ioflag & IO_APPEND) + uio->uio_offset = dep->de_FileSize; + isadir = 0; + thisvp = vp; + break; + + case VDIR: + if ((ioflag & IO_SYNC) == 0) + panic("msdosfs_write(): non-sync directory update"); + isadir = 1; + thisvp = pmp->pm_devvp; + break; + + default: + panic("msdosfs_write(): bad file type"); + break; + } + + if (uio->uio_offset < 0) + return EINVAL; + + if (uio->uio_resid == 0) + return 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)) { + psignal(p, SIGXFSZ); + return EFBIG; + } + + /* + * 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) + return ENOSPC; + + /* + * 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. + */ + if (uio->uio_offset > dep->de_FileSize) { + error = deextend(dep, uio->uio_offset, cred); + if (error) + return error; + } + + /* + * Remember some values in case the write fails. + */ + resid = uio->uio_resid; + osize = dep->de_FileSize; + + + /* + * If we write beyond the end of the file, extend it to its ultimate + * size ahead of the time to hopefully get a contiguous area. + */ + if (uio->uio_offset + resid > osize) { + count = de_clcount(pmp, uio->uio_offset + resid) - de_clcount(pmp, osize); + if ((error = extendfile(dep, count, NULL, NULL, 0)) + && (error != ENOSPC || (ioflag & IO_UNIT))) + goto errexit; + lastcn = dep->de_fc[FC_LASTFC].fc_frcn; + } else + lastcn = de_clcount(pmp, osize) - 1; + + do { + bn = de_blk(pmp, uio->uio_offset); + if (isadir) { + if (error = pcbmap(dep, bn, &bn, 0)) + break; + } else if (bn > lastcn) { + error = ENOSPC; + break; + } + + if ((uio->uio_offset & pmp->pm_crbomask) == 0 + && (de_blk(pmp, uio->uio_offset + uio->uio_resid) > de_blk(pmp, uio->uio_offset) + || uio->uio_offset + uio->uio_resid >= dep->de_FileSize)) { + /* + * If either the whole cluster gets written, + * or we write the cluster from its start beyond EOF, + * then no need to read data from disk. + */ + bp = getblk(thisvp, bn, pmp->pm_bpcluster, 0, 0); + clrbuf(bp); + /* + * Do the bmap now, since pcbmap needs buffers + * for the fat table. (see msdosfs_strategy) + */ + if (!isadir) { + if (bp->b_blkno == bp->b_lblkno) { + if (error = pcbmap(dep, bp->b_lblkno, + &bp->b_blkno, 0)) + bp->b_blkno = -1; + } + if (bp->b_blkno == -1) { + brelse(bp); + if (!error) + error = EIO; /* XXX */ + break; + } + } + } else { + /* + * The block we need to write into exists, so read it in. + */ + if (error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp)) + break; + } + + 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_data + 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 (ioflag & IO_SYNC) + (void) bwrite(bp); + else if (n + croffset == pmp->pm_bpcluster) { + bp->b_flags |= B_AGE; + bawrite(bp); + } else + bdwrite(bp); + dep->de_flag |= DE_UPDATE; + } 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. + */ +errexit: + if (error) { + if (ioflag & IO_UNIT) { + detrunc(dep, osize, ioflag & IO_SYNC, NOCRED, NULL); + uio->uio_offset -= resid - uio->uio_resid; + uio->uio_resid = resid; + } else { + detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, NOCRED, NULL); + if (uio->uio_resid != resid) + error = 0; + } + } else { + error = deupdat(dep, &time, 1); + } + return error; +} + +int +msdosfs_ioctl(ap) + struct vop_ioctl_args /* { + struct vnode *a_vp; + int a_command; + caddr_t a_data; + int a_fflag; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + return ENOTTY; +} + +int +msdosfs_select(ap) + struct vop_select_args /* { + struct vnode *a_vp; + int a_which; + int a_fflags; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + return 1; /* DOS filesystems never block? */ +} + +int +msdosfs_mmap(ap) + struct vop_mmap_args /* { + struct vnode *a_vp; + int a_fflags; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; +{ + return EINVAL; +} + +/* + * 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. + */ +int +msdosfs_fsync(ap) + struct vop_fsync_args /* { + struct vnode *a_vp; + struct ucred *a_cred; + int a_waitfor; + struct proc *a_p; + } */ *ap; +{ + struct vnode *vp = ap->a_vp; + int wait = ap->a_waitfor == MNT_WAIT; + int error; + +#if 0 + /* + * 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, wait ? B_SYNC : 0); +#endif + return deupdat(VTODE(vp), &time, wait); +} + +/* + * Now the whole work of extending a file is done in the write function. + * So nothing to do here. + */ +int +msdosfs_seek(ap) + struct vop_seek_args /* { + struct vnode *a_vp; + off_t a_oldoff; + off_t a_newoff; + struct ucred *a_cred; + } */ *ap; +{ + return 0; +} + +int +msdosfs_remove(ap) + struct vop_remove_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap; +{ + int error; + struct denode *dep = VTODE(ap->a_vp); + struct denode *ddep = VTODE(ap->a_dvp); + + error = removede(ddep,dep); +#ifdef MSDOSFS_DEBUG + printf("msdosfs_remove(), dep %08x, v_usecount %d\n", dep, ap->a_vp->v_usecount); +#endif + if (ddep == dep) + vrele(ap->a_vp); + else + vput(ap->a_vp); /* causes msdosfs_inactive() to be called + * via vrele() */ + vput(ap->a_dvp); + return error; +} + +/* + * 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. + */ +int +msdosfs_link(ap) + struct vop_link_args /* { + struct vnode *a_vp; + struct vnode *a_tdvp; + struct componentname *a_cnp; + } */ *ap; +{ + return VOP_ABORTOP(ap->a_vp, ap->a_cnp); +} + +/* + * 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 + * componentname structures is freed, there may be some memory bleeding + * for each rename done. + */ +int +msdosfs_rename(ap) + struct vop_rename_args /* { + struct vnode *a_fdvp; + struct vnode *a_fvp; + struct componentname *a_fcnp; + struct vnode *a_tdvp; + struct vnode *a_tvp; + struct componentname *a_tcnp; + } */ *ap; +{ + u_char toname[11]; + int error; + int newparent = 0; + int sourceisadirectory = 0; + u_long to_dirclust; + u_long to_diroffset; + u_long cn; + daddr_t bn; + struct vnode *tvp = ap->a_tvp; + 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; + struct direntry *ep; + struct buf *bp; + + fddep = VTODE(ap->a_fdvp); + fdep = VTODE(ap->a_fvp); + tddep = VTODE(ap->a_tdvp); + tdep = tvp ? VTODE(tvp) : NULL; + pmp = fddep->de_pmp; + +#ifdef __NetBSD__ + /* Check for cross-device rename */ + if ((ap->a_fvp->v_mount != ap->a_tdvp->v_mount) || + (tvp && (ap->a_fvp->v_mount != tvp->v_mount))) { + error = EXDEV; + goto bad; + } +#endif + + /* + * Convert the filename in tcnp into a dos filename. We copy this + * into the denode and directory entry for the destination + * file/directory. + */ + unix2dosfn((u_char *) ap->a_tcnp->cn_nameptr, + toname, ap->a_tcnp->cn_namelen); + + /* + * At this point this is the lock state of the denodes: + * fddep referenced + * fdep referenced + * tddep locked + * 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 ((ap->a_fcnp->cn_namelen == 1 + && ap->a_fcnp->cn_nameptr[0] == '.') + || fddep == fdep + || (ap->a_fcnp->cn_flags & ISDOTDOT)) { + VOP_ABORTOP(ap->a_tdvp, ap->a_tcnp); + vput(ap->a_tdvp); + if (tvp) + vput(tvp); + VOP_ABORTOP(ap->a_fdvp, ap->a_fcnp); + vrele(ap->a_fdvp); + vrele(ap->a_fvp); + return EINVAL; + } + sourceisadirectory = 1; + } + + /* + * 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) + newparent = 1; + if (sourceisadirectory && newparent) { + if (tdep) { + vput(ap->a_tvp); + tdep = NULL; + } + /* doscheckpath() vput()'s tddep */ + error = doscheckpath(fdep, tddep); + tddep = NULL; + if (error) { + goto bad; + } + if ((ap->a_tcnp->cn_flags & SAVESTART) == 0) + panic("msdosfs_rename(): lost to startdir"); + if (error = relookup(ap->a_tdvp, &tvp, ap->a_tcnp)) { + goto bad; + } + tddep = VTODE(ap->a_tdvp); + tdep = tvp ? VTODE(tvp) : 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) { + if (tdep->de_Attributes & ATTR_DIRECTORY) { + if (!sourceisadirectory) { + error = ENOTDIR; + goto bad; + } + if (!dosdirempty(tdep)) { + error = ENOTEMPTY; + goto bad; + } + } else { /* destination is file */ + if (sourceisadirectory) { + error = EISDIR; + goto bad; + } + } + to_dirclust = tdep->de_dirclust; + to_diroffset = tdep->de_diroffset; + if (error = removede(tddep,tdep)) { + goto bad; + } + vput(ap->a_tvp); + tdep = NULL; + + /* + * Remember where the slot was for createde(). + */ + tddep->de_fndclust = to_dirclust; + tddep->de_fndoffset = 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. + */ + if (newparent == 0) { + /* tddep and fddep point to the same denode here */ + VOP_LOCK(ap->a_fvp); /* ap->a_fdvp is already locked */ + if (error = readep(fddep->de_pmp, + fdep->de_dirclust, + fdep->de_diroffset, + &bp, &ep)) { + VOP_UNLOCK(ap->a_fvp); + goto bad; + } + bcopy(toname, ep->deName, 11); + if (error = bwrite(bp)) { + VOP_UNLOCK(ap->a_fvp); + goto bad; + } + bcopy(toname, fdep->de_Name, 11); /* update denode */ + /* + * fdep locked fddep and tddep point to the same denode + * which is locked tdep is NULL + */ + } else { + u_long dirsize = 0L; + + /* + * 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 + * filesize of zero. + */ + VOP_LOCK(ap->a_fvp); + bcopy(toname, fdep->de_Name, 11); /* update denode */ + if (fdep->de_Attributes & ATTR_DIRECTORY) { + dirsize = fdep->de_FileSize; + fdep->de_FileSize = 0; + } + error = createde(fdep, tddep, (struct denode **) 0); + if (fdep->de_Attributes & ATTR_DIRECTORY) { + fdep->de_FileSize = dirsize; + } + if (error) { + /* should put back filename */ + VOP_UNLOCK(ap->a_fvp); + goto bad; + } + VOP_LOCK(ap->a_fdvp); + if (error = readep(fddep->de_pmp, + fddep->de_fndclust, + fddep->de_fndoffset, + &bp, &ep)) { + VOP_UNLOCK(ap->a_fvp); + VOP_UNLOCK(ap->a_fdvp); + goto bad; + } + ep->deName[0] = SLOT_DELETED; + if (error = bwrite(bp)) { + VOP_UNLOCK(ap->a_fvp); + VOP_UNLOCK(ap->a_fdvp); + goto bad; + } + fdep->de_dirclust = tddep->de_fndclust; + fdep->de_diroffset = tddep->de_fndoffset; + reinsert(fdep); + VOP_UNLOCK(ap->a_fdvp); + } + /* 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; + if (cn == MSDOSFSROOT) { + /* this should never happen */ + panic("msdosfs_rename(): updating .. in root directory?\n"); + } else { + bn = cntobn(pmp, cn); + } + error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, + NOCRED, &bp); + if (error) { + /* should really panic here, fs is corrupt */ + VOP_UNLOCK(ap->a_fvp); + goto bad; + } + dotdotp = (struct direntry *) bp->b_data + 1; + putushort(dotdotp->deStartCluster, tddep->de_StartCluster); + error = bwrite(bp); + VOP_UNLOCK(ap->a_fvp); + if (error) { + /* should really panic here, fs is corrupt */ + goto bad; + } + } else + VOP_UNLOCK(ap->a_fvp); +bad: ; + vrele(DETOV(fdep)); + vrele(DETOV(fddep)); + if (tdep) + vput(DETOV(tdep)); + if (tddep) + vput(DETOV(tddep)); + return error; +} + +struct { + struct direntry dot; + struct direntry dotdot; +} dosdirtemplate = { + + ". ", " ", /* the . entry */ + ATTR_DIRECTORY, /* file attribute */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* resevered */ + 210, 4, 210, 4, /* time and date */ + 0, 0, /* startcluster */ + 0, 0, 0, 0, /* filesize */ + ".. ", " ", /* the .. entry */ + ATTR_DIRECTORY, /* file attribute */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* resevered */ + 210, 4, 210, 4, /* time and date */ + 0, 0, /* startcluster */ + 0, 0, 0, 0, /* filesize */ +}; + +int +msdosfs_mkdir(ap) + struct vop_mkdir_args /* { + struct vnode *a_dvp; + struvt vnode **a_vpp; + struvt componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; +{ + int bn; + int error; + u_long newcluster; + struct denode *pdep; + struct denode *ndep; + struct direntry *denp; + struct denode ndirent; + struct msdosfsmount *pmp; + struct buf *bp; + struct timespec ts; + u_short dDate, dTime; + + pdep = VTODE(ap->a_dvp); + + /* + * 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 + * change size. + */ + if (pdep->de_StartCluster == MSDOSFSROOT && pdep->de_fndclust == (u_long)-1) { + free(ap->a_cnp->cn_pnbuf, M_NAMEI); + vput(ap->a_dvp); + return ENOSPC; + } + + pmp = pdep->de_pmp; + + /* + * Allocate a cluster to hold the about to be created directory. + */ + if (error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL)) { + free(ap->a_cnp->cn_pnbuf, M_NAMEI); + vput(ap->a_dvp); + return error; + } + + /* + * 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); + /* always succeeds */ + bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0); + bzero(bp->b_data, pmp->pm_bpcluster); + bcopy(&dosdirtemplate, bp->b_data, sizeof dosdirtemplate); + denp = (struct direntry *) bp->b_data; + putushort(denp->deStartCluster, newcluster); + TIMEVAL_TO_TIMESPEC(&time, &ts); + unix2dostime(&ts, &dDate, &dTime); + putushort(denp->deDate, dDate); + putushort(denp->deTime, dTime); + denp++; + putushort(denp->deStartCluster, pdep->de_StartCluster); + putushort(denp->deDate, dDate); + putushort(denp->deTime, dTime); + if (error = bwrite(bp)) { + clusterfree(pmp, newcluster, NULL); + free(ap->a_cnp->cn_pnbuf, M_NAMEI); + vput(ap->a_dvp); + return error; + } + + /* + * Now build up a directory entry pointing to the newly allocated + * cluster. This will be written to an empty slot in the parent + * directory. + */ + ndep = &ndirent; + bzero(ndep, sizeof(*ndep)); + unix2dosfn((u_char *)ap->a_cnp->cn_nameptr, + ndep->de_Name, ap->a_cnp->cn_namelen); + TIMEVAL_TO_TIMESPEC(&time, &ts); + unix2dostime(&ts, &ndep->de_Date, &ndep->de_Time); + ndep->de_StartCluster = newcluster; + ndep->de_Attributes = ATTR_DIRECTORY; + + error = createde(ndep, pdep, &ndep); + if (error) { + clusterfree(pmp, newcluster, NULL); + } else { + *ap->a_vpp = DETOV(ndep); + } + free(ap->a_cnp->cn_pnbuf, M_NAMEI); +#ifdef MSDOSFS_DEBUG + printf("msdosfs_mkdir(): vput(%08x)\n", ap->a_dvp); +#endif + vput(ap->a_dvp); + return error; +} + +int +msdosfs_rmdir(ap) + struct vop_rmdir_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap; +{ + struct denode *ddep; + struct denode *dep; + int error = 0; + + ddep = VTODE(ap->a_dvp); /* parent dir of dir to delete */ + dep = VTODE(ap->a_vp);/* directory to delete */ + + /* + * Don't let "rmdir ." go thru. + */ + if (ddep == dep) { + vrele(ap->a_vp); + vput(ap->a_vp); + return EINVAL; + } + + /* + * Be sure the directory being deleted is empty. + */ + if (dosdirempty(dep) == 0) { + error = ENOTEMPTY; + goto out; + } + + /* + * 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 vput() 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(ddep,dep)) + goto out; + + /* + * 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)); + vput(ap->a_dvp); + ap->a_dvp = NULL; + + /* + * Truncate the directory that is being deleted. + */ + error = detrunc(dep, (u_long) 0, IO_SYNC); + cache_purge(DETOV(dep)); + +out: ; + if (ap->a_dvp) + vput(ap->a_dvp); + vput(ap->a_vp); + return error; +} + +/* + * DOS filesystems don't know what symlinks are. + */ +int +msdosfs_symlink(ap) + struct vop_symlink_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + char *a_target; + } */ *ap; +{ + struct denode *pdep = VTODE(ap->a_dvp); + + free(ap->a_cnp->cn_pnbuf, M_NAMEI); + vput(ap->a_dvp); + return EINVAL; +} + +/* + * 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). + */ +struct dos_dirent { + u_long d_fileno; + u_short d_reclen; + u_char d_type; + u_char d_namlen; + u_char d_name[24]; +} rootdots[2] = { + + { + 1, /* d_fileno */ + sizeof(struct direntry), /* d_reclen */ + DT_DIR, /* d_type */ + 1, /* d_namlen */ + "." /* d_name */ + }, + { + 1, /* d_fileno */ + sizeof(struct direntry), /* d_reclen */ + DT_DIR, /* d_type */ + 2, /* d_namlen */ + ".." /* d_name */ + } +}; + +int +msdosfs_readdir(ap) + struct vop_readdir_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + int *a_eofflag; + u_int *a_cookies; + int a_ncookies; + } */ *ap; +{ + int error = 0; + int diff; + char pushout; + long n; + long on; + long lost; + long count; + u_long cn; + u_long fileno; + long bias = 0; + daddr_t bn; + daddr_t lbn; + struct buf *bp; + struct denode *dep = VTODE(ap->a_vp); + struct msdosfsmount *pmp = dep->de_pmp; + struct direntry *dentp; + struct dirent *prev; + struct dirent *crnt; + u_char dirbuf[512]; /* holds converted dos directories */ + int i = 0; + struct uio *uio = ap->a_uio; + int ncookies = 1; + u_int* cookies = NULL; + +#ifdef MSDOSFS_DEBUG + printf("msdosfs_readdir(): vp %08x, uio %08x, cred %08x, eofflagp %08x\n", + ap->a_vp, uio, ap->a_cred, ap->a_eofflag); +#endif + +#if 0 + if (!ap->a_cookies) + ncookies = 1; +#endif + + /* + * 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) + return ENOTDIR; + + /* + * 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))) + return EINVAL; + uio->uio_resid = count; + 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)) { + if (uio->uio_offset + && uio->uio_offset != sizeof(struct direntry)) { + error = EINVAL; + goto out; + } + n = 1; + if (!uio->uio_offset) { + n = 2; + if (cookies) { + *cookies++ = sizeof(struct direntry); + ncookies--; + } + } + if (cookies) { + if (ncookies-- <= 0) + n--; + else + *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); + if (diff <= 0) + return 0; + if (diff < n) + n = diff; + error = pcbmap(dep, lbn, &bn, &cn); + if (error) + break; + error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp); + n = min(n, pmp->pm_bpcluster - bp->b_resid); + if (error) { + brelse(bp); + return error; + } + + /* + * code to convert from dos directory entries to ufs + * directory entries + */ + pushout = 0; + dentp = (struct direntry *)(bp->b_data + on); + prev = 0; + crnt = (struct dirent *) dirbuf; + while ((char *) dentp < bp->b_data + 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)) { + if (prev) { + prev->d_reclen += sizeof(struct direntry); + if (cookies) { + ncookies++; + cookies--; + } + } else { + prev = crnt; + prev->d_fileno = 0; + prev->d_reclen = sizeof(struct direntry); + prev->d_type = DT_UNKNOWN; + prev->d_namlen = 0; + prev->d_name[0] = 0; + } + } else { + /* + * this computation of d_fileno must match + * the computation of va_fileid in + * msdosfs_getattr + */ + if (dentp->deAttributes & ATTR_DIRECTORY) { + /* if this is the root directory */ + fileno = getushort(dentp->deStartCluster); + if (fileno == MSDOSFSROOT) + fileno = 1; + } else { + /* + * if the file's dirent lives in + * root dir + */ + if ((fileno = cn) == MSDOSFSROOT) + fileno = 1; + fileno = (fileno << 16) | + ((dentp - (struct direntry *) bp->b_data) & 0xffff); + } + crnt->d_fileno = fileno; + crnt->d_reclen = sizeof(struct direntry); + crnt->d_type = (dentp->deAttributes & ATTR_DIRECTORY) + ? DT_DIR : DT_REG; + 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); + */ + prev = crnt; + } + dentp++; + if (cookies) { + *cookies++ = (u_int)((char *)dentp - bp->b_data - on) + + uio->uio_offset; + ncookies--; + } + + crnt = (struct dirent *) ((char *) crnt + sizeof(struct direntry)); + pushout = 1; + + /* + * 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]) { + pushout = 0; + error = uiomove(dirbuf, sizeof(dirbuf), uio); + if (error) + break; + prev = 0; + crnt = (struct dirent *) dirbuf; + } + if (ncookies <= 0) + break; + } + if (pushout) { + pushout = 0; + error = uiomove(dirbuf, (char *) crnt - (char *) dirbuf, + uio); + } + +#if 0 + /* + * 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) + bp->b_flags |= B_AGE; +#endif /* if 0 */ + brelse(bp); + if (n == 0) + break; + } +out: ; + uio->uio_resid += lost; + +#if 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. + * (because NFS uses it in nfs_serv.c -- JMP) + */ + if (dep->de_FileSize - uio->uio_offset - bias <= 0) + *ap->a_eofflag = 1; + else + *ap->a_eofflag = 0; +#endif + + return error; +} + +/* + * DOS filesystems don't know what symlinks are. + */ +int +msdosfs_readlink(ap) + struct vop_readlink_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + } */ *ap; +{ + return EINVAL; +} + +int +msdosfs_abortop(ap) + struct vop_abortop_args /* { + struct vnode *a_dvp; + struct componentname *a_cnp; + } */ *ap; +{ + if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) + FREE(ap->a_cnp->cn_pnbuf, M_NAMEI); + return 0; +} + +int +msdosfs_lock(ap) + struct vop_lock_args /* { + struct vnode *a_vp; + } */ *ap; +{ + struct denode *dep = VTODE(ap->a_vp); + + while (dep->de_flag & DE_LOCKED) { + dep->de_flag |= DE_WANTED; + if (dep->de_lockholder == curproc->p_pid) + panic("msdosfs_lock: locking against myself"); + dep->de_lockwaiter = curproc->p_pid; + (void) sleep((caddr_t) dep, PINOD); + } + dep->de_lockwaiter = 0; + dep->de_lockholder = curproc->p_pid; + dep->de_flag |= DE_LOCKED; + return 0; +} + +int +msdosfs_unlock(ap) + struct vop_unlock_args /* { + struct vnode *vp; + } */ *ap; +{ + struct denode *dep = VTODE(ap->a_vp); + + if (!(dep->de_flag & DE_LOCKED)) + panic("msdosfs_unlock: denode not locked"); + dep->de_lockholder = 0; + dep->de_flag &= ~DE_LOCKED; + if (dep->de_flag & DE_WANTED) { + dep->de_flag &= ~DE_WANTED; + wakeup((caddr_t) dep); + } + return 0; +} + +int +msdosfs_islocked(ap) + struct vop_islocked_args /* { + struct vnode *a_vp; + } */ *ap; +{ + return VTODE(ap->a_vp)->de_flag & DE_LOCKED ? 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 number + */ +int +msdosfs_bmap(ap) + struct vop_bmap_args /* { + struct vnode *a_vp; + daddr_t a_bn; + struct vnode **a_vpp; + daddr_t *a_bnp; + int *a_runp; + } */ *ap; +{ + struct denode *dep = VTODE(ap->a_vp); + struct msdosfsmount *pmp = dep->de_pmp; + + if (ap->a_vpp != NULL) + *ap->a_vpp = dep->de_devvp; + if (ap->a_bnp == NULL) + return 0; + if (ap->a_runp) { + /* + * Sequential clusters should be counted here. + */ + *ap->a_runp = 0; + } + return pcbmap(dep, ap->a_bn, ap->a_bnp, 0); +} + +int msdosfs_reallocblks(ap) + struct vop_reallocblks_args /* { + struct vnode *a_vp; + struct cluster_save *a_buflist; + } */ *ap; +{ + /* Currently no support for clustering */ /* XXX */ + return ENOSPC; +} + +int +msdosfs_strategy(ap) + struct vop_strategy_args /* { + struct buf *a_bp; + } */ *ap; +{ + struct buf *bp = ap->a_bp; + struct denode *dep = VTODE(bp->b_vp); + struct msdosfsmount *pmp = dep->de_pmp; + struct vnode *vp; + int error = 0; + + 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)) + bp->b_blkno = -1; + if (bp->b_blkno == -1) + clrbuf(bp); + } + if (bp->b_blkno == -1) { + biodone(bp); + return error; + } +#ifdef DIAGNOSTIC +#endif + /* + * Read/write the block from/to the disk that contains the desired + * file block. + */ + vp = dep->de_devvp; + bp->b_dev = vp->v_rdev; + VOCALL(vp->v_op, VOFFSET(vop_strategy), ap); + return 0; +} + +int +msdosfs_print(ap) + struct vop_print_args /* { + struct vnode *vp; + } */ *ap; +{ + struct denode *dep = VTODE(ap->a_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 & DE_LOCKED ? "(LOCKED)" : ""); + if (dep->de_lockholder) { + printf(" owner pid %d", dep->de_lockholder); + if (dep->de_lockwaiter) + printf(" waiting pid %d", dep->de_lockwaiter); + printf("\n"); + } + return 0; +} + +int +msdosfs_advlock(ap) + struct vop_advlock_args /* { + struct vnode *a_vp; + caddr_t a_id; + int a_op; + struct flock *a_fl; + int a_flags; + } */ *ap; +{ + return EINVAL; /* we don't do locking yet */ +} + +int +msdosfs_pathconf(ap) + struct vop_pathconf_args /* { + struct vnode *a_vp; + int a_name; + int *a_retval; + } */ *ap; +{ + switch (ap->a_name) { + case _PC_LINK_MAX: + *ap->a_retval = 1; + return 0; + case _PC_NAME_MAX: + *ap->a_retval = 12; + return 0; + case _PC_PATH_MAX: + *ap->a_retval = PATH_MAX; /* 255? */ + return 0; + case _PC_CHOWN_RESTRICTED: + *ap->a_retval = 1; + return 0; + case _PC_NO_TRUNC: + *ap->a_retval = 0; + return 0; + default: + return EINVAL; + } +} + +/* Global vfs data structures for msdosfs */ +int (**msdosfs_vnodeop_p)(); +struct vnodeopv_entry_desc msdosfs_vnodeop_entries[] = { + { &vop_default_desc, vn_default_error }, + { &vop_lookup_desc, msdosfs_lookup }, /* lookup */ + { &vop_create_desc, msdosfs_create }, /* create */ + { &vop_mknod_desc, msdosfs_mknod }, /* mknod */ + { &vop_open_desc, msdosfs_open }, /* open */ + { &vop_close_desc, msdosfs_close }, /* close */ + { &vop_access_desc, msdosfs_access }, /* access */ + { &vop_getattr_desc, msdosfs_getattr }, /* getattr */ + { &vop_setattr_desc, msdosfs_setattr }, /* setattr */ + { &vop_read_desc, msdosfs_read }, /* read */ + { &vop_write_desc, msdosfs_write }, /* write */ + { &vop_ioctl_desc, msdosfs_ioctl }, /* ioctl */ + { &vop_select_desc, msdosfs_select }, /* select */ + { &vop_mmap_desc, msdosfs_mmap }, /* mmap */ + { &vop_fsync_desc, msdosfs_fsync }, /* fsync */ + { &vop_seek_desc, msdosfs_seek }, /* seek */ + { &vop_remove_desc, msdosfs_remove }, /* remove */ + { &vop_link_desc, msdosfs_link }, /* link */ + { &vop_rename_desc, msdosfs_rename }, /* rename */ + { &vop_mkdir_desc, msdosfs_mkdir }, /* mkdir */ + { &vop_rmdir_desc, msdosfs_rmdir }, /* rmdir */ + { &vop_symlink_desc, msdosfs_symlink }, /* symlink */ + { &vop_readdir_desc, msdosfs_readdir }, /* readdir */ + { &vop_readlink_desc, msdosfs_readlink }, /* readlink */ + { &vop_abortop_desc, msdosfs_abortop }, /* abortop */ + { &vop_inactive_desc, msdosfs_inactive }, /* inactive */ + { &vop_reclaim_desc, msdosfs_reclaim }, /* reclaim */ + { &vop_lock_desc, msdosfs_lock }, /* lock */ + { &vop_unlock_desc, msdosfs_unlock }, /* unlock */ + { &vop_bmap_desc, msdosfs_bmap }, /* bmap */ + { &vop_strategy_desc, msdosfs_strategy }, /* strategy */ + { &vop_print_desc, msdosfs_print }, /* print */ + { &vop_islocked_desc, msdosfs_islocked }, /* islocked */ + { &vop_pathconf_desc, msdosfs_pathconf }, /* pathconf */ + { &vop_advlock_desc, msdosfs_advlock }, /* advlock */ + { &vop_reallocblks_desc, msdosfs_reallocblks }, /* reallocblks */ + { &vop_bwrite_desc, vn_bwrite }, + { (struct vnodeop_desc *)NULL, (int (*)())NULL } +}; +struct vnodeopv_desc msdosfs_vnodeop_opv_desc = + { &msdosfs_vnodeop_p, msdosfs_vnodeop_entries }; diff --git a/sys/msdosfs/msdosfsmount.h b/sys/msdosfs/msdosfsmount.h new file mode 100644 index 0000000..62853c5 --- /dev/null +++ b/sys/msdosfs/msdosfsmount.h @@ -0,0 +1,183 @@ +/* $Id$ */ +/* $NetBSD: msdosfsmount.h,v 1.7 1994/08/21 18:44:17 ws Exp $ */ + +/*- + * Copyright (C) 1994 Wolfgang Solfrank. + * Copyright (C) 1994 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 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. + * + * October 1992 + */ + +/* + * Layout of the mount control block for a msdos file system. + */ +struct msdosfsmount { + struct mount *pm_mountp;/* vfs mount struct for this fs */ + dev_t pm_dev; /* block special device mounted */ + uid_t pm_mounter; /* uid of the user who mounted the FS */ + uid_t pm_uid; /* uid to set as owner of the files */ + gid_t pm_gid; /* gid to set as owner of the files */ + mode_t pm_mask; /* mask to and with file protection bits */ + struct vnode *pm_devvp; /* vnode for block device mntd */ + struct bpb50 pm_bpb; /* BIOS parameter blk for this fs */ + u_long pm_fatblk; /* block # of first FAT */ + u_long pm_rootdirblk; /* block # of root directory */ + u_long pm_rootdirsize; /* size in blocks (not clusters) */ + u_long pm_firstcluster; /* block number of first cluster */ + u_long pm_nmbrofclusters; /* # of clusters in filesystem */ + u_long pm_maxcluster; /* maximum cluster number */ + u_long pm_freeclustercount; /* number of free clusters */ + u_long pm_bnshift; /* shift file offset right this amount to get a block number */ + u_long pm_brbomask; /* and a file offset with this mask to get block rel offset */ + u_long pm_cnshift; /* shift file offset right this amount to get a cluster number */ + u_long pm_crbomask; /* and a file offset with this mask to get cluster rel offset */ + u_long pm_bpcluster; /* bytes per cluster */ + u_long pm_depclust; /* directory entries per cluster */ + u_long pm_fmod; /* ~0 if fs is modified, this can rollover to 0 */ + u_long pm_fatblocksize; /* size of fat blocks in bytes */ + u_long pm_fatblocksec; /* size of fat blocks in sectors */ + u_long pm_fatsize; /* size of fat in bytes */ + u_int *pm_inusemap; /* ptr to bitmap of in-use clusters */ + char pm_ronly; /* read only if non-zero */ + char pm_waitonfat; /* wait for writes of the fat to complt, when 0 use bdwrite, else use bwrite */ + struct netexport pm_export; /* export information */ +}; + +/* Number of bits in one pm_inusemap item: */ +#define N_INUSEBITS (8 * sizeof(u_int)) + +/* + * How to compute pm_cnshift and pm_crbomask. + * + * pm_crbomask = (pm_SectPerClust * pm_BytesPerSect) - 1 + * if (bytesperclust == * 0) + * return EBADBLKSZ; + * bit = 1; + * for (i = 0; i < 32; i++) { + * if (bit & bytesperclust) { + * if (bit ^ bytesperclust) + * return EBADBLKSZ; + * pm_cnshift = * i; + * break; + * } + * bit <<= 1; + * } + */ + +/* + * Shorthand for fields in the bpb contained in the msdosfsmount structure. + */ +#define pm_BytesPerSec pm_bpb.bpbBytesPerSec +#define pm_SectPerClust pm_bpb.bpbSecPerClust +#define pm_ResSectors pm_bpb.bpbResSectors +#define pm_FATs pm_bpb.bpbFATs +#define pm_RootDirEnts pm_bpb.bpbRootDirEnts +#define pm_Sectors pm_bpb.bpbSectors +#define pm_Media pm_bpb.bpbMedia +#define pm_FATsecs pm_bpb.bpbFATsecs +#define pm_SecPerTrack pm_bpb.bpbSecPerTrack +#define pm_Heads pm_bpb.bpbHeads +#define pm_HiddenSects pm_bpb.bpbHiddenSecs +#define pm_HugeSectors pm_bpb.bpbHugeSectors + +/* + * Map a cluster number into a filesystem relative block number. + */ +#define cntobn(pmp, cn) \ + ((((cn)-CLUST_FIRST) * (pmp)->pm_SectPerClust) + (pmp)->pm_firstcluster) + +/* + * Map a filesystem relative block number back into a cluster number. + */ +#define bntocn(pmp, bn) \ + ((((bn) - pmp->pm_firstcluster)/ (pmp)->pm_SectPerClust) + CLUST_FIRST) + +/* + * Calculate block number for directory entry in root dir, offset dirofs + */ +#define roottobn(pmp, dirofs) \ + (((dirofs) / (pmp)->pm_depclust) * (pmp)->pm_SectPerClust \ + + (pmp)->pm_rootdirblk) + +/* + * Calculate block number for directory entry at cluster dirclu, offset + * dirofs + */ +#define detobn(pmp, dirclu, dirofs) \ + ((dirclu) == MSDOSFSROOT \ + ? roottobn((pmp), (dirofs)) \ + : cntobn((pmp), (dirclu))) + +/* + * Convert pointer to buffer -> pointer to direntry + */ +#define bptoep(pmp, bp, dirofs) \ + ((struct direntry *)((bp)->b_data) \ + + (dirofs) % (pmp)->pm_depclust) + + +/* + * Convert filesize to block number + */ +#define de_blk(pmp, off) \ + ((off) >> (pmp)->pm_cnshift) + +/* + * Clusters required to hold size bytes + */ +#define de_clcount(pmp, size) \ + (((size) + (pmp)->pm_bpcluster - 1) >> (pmp)->pm_cnshift) + +/* + * Prototypes for MSDOSFS virtual filesystem operations + */ +int msdosfs_mount __P((struct mount *, char *, caddr_t, struct nameidata *, struct proc *)); +int msdosfs_start __P((struct mount *, int, struct proc *)); +int msdosfs_unmount __P((struct mount *, int, struct proc *)); +int msdosfs_root __P((struct mount *, struct vnode **)); +int msdosfs_quotactl __P((struct mount *, int, uid_t, caddr_t, struct proc *)); +int msdosfs_statfs __P((struct mount *, struct statfs *, struct proc *)); +int msdosfs_sync __P((struct mount *, int, struct ucred *, struct proc *)); +int msdosfs_fhtovp __P((struct mount *, struct fid *, struct mbuf *, struct vnode **, int *, struct ucred **)); +int msdosfs_vptofh __P((struct vnode *, struct fid *)); +int msdosfs_init __P(()); diff --git a/sys/sys/malloc.h b/sys/sys/malloc.h index 46c7df7..3d190ed 100644 --- a/sys/sys/malloc.h +++ b/sys/sys/malloc.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)malloc.h 8.3 (Berkeley) 1/12/94 - * $Id$ + * $Id: malloc.h,v 1.3 1994/08/02 07:53:10 davidg Exp $ */ #ifndef _SYS_MALLOC_H_ @@ -107,6 +107,9 @@ #define M_MRTABLE 56 /* multicast routing tables */ #define M_ISOFSMNT 57 /* ISOFS mount structure */ #define M_ISOFSNODE 58 /* ISOFS vnode private part */ +#define M_MSDOSFSMNT 59 /* MSDOSFS mount structure */ +#define M_MSDOSFSNODE 60 /* MSDOSFS vnode private part */ +#define M_MSDOSFSFAT 61 /* MSDOSFS file allocation table */ #define M_TEMP 74 /* misc temporary data buffers */ #define M_TTYS 75 /* tty data structures */ #define M_LAST 76 /* Must be last type + 1 */ @@ -171,9 +174,12 @@ "mrt", /* 56 M_MRTABLE */ \ "ISOFS mount", /* 57 M_ISOFSMNT */ \ "ISOFS node", /* 58 M_ISOFSNODE */ \ + "MSDOSFS mount",/* 59 M_MSDOSFSMNT */ \ + "MSDOSFS node", /* 60 M_MSDOSFSNODE */ \ + "MSDOSFS FAT", /* 61 M_MSDOSFSFAR */ \ NULL, NULL, NULL, NULL, NULL, \ NULL, NULL, NULL, NULL, NULL, \ - NULL, NULL, NULL, NULL, NULL, \ + NULL, NULL, \ "temp", /* 74 M_TEMP */ \ "ttys", /* 75 M_TTYS */ \ } diff --git a/sys/sys/mount.h b/sys/sys/mount.h index cae5442..1136343 100644 --- a/sys/sys/mount.h +++ b/sys/sys/mount.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)mount.h 8.13 (Berkeley) 3/27/94 - * $Id: mount.h,v 1.5 1994/08/22 14:09:51 bde Exp $ + * $Id: mount.h,v 1.6 1994/09/15 20:24:26 bde Exp $ */ #ifndef _SYS_MOUNT_H_ @@ -308,6 +308,19 @@ struct mfs_args { }; #endif /* MFS */ +#ifdef MSDOSFS +/* + * Arguments to mount MSDOS filesystems. + */ +struct msdosfs_args { + char *fspec; /* blocks special holding the fs to mount */ + struct export_args export; /* network export information */ + uid_t uid; /* uid that owns msdosfs files */ + gid_t gid; /* gid that owns msdosfs files */ + mode_t mask; /* mask to be applied for msdosfs perms */ +}; +#endif + #ifdef CD9660 /* * Arguments to mount ISO 9660 filesystems. diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index 89c8052..e20b71c 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)vnode.h 8.7 (Berkeley) 2/4/94 - * $Id: vnode.h,v 1.4 1994/08/29 06:09:11 davidg Exp $ + * $Id: vnode.h,v 1.5 1994/09/15 20:24:29 bde Exp $ */ #ifndef _SYS_VNODE_H_ @@ -58,7 +58,7 @@ enum vtype { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO, VBAD }; enum vtagtype { VT_NON, VT_UFS, VT_NFS, VT_MFS, VT_PC, VT_LFS, VT_LOFS, VT_FDESC, VT_PORTAL, VT_NULL, VT_UMAP, VT_KERNFS, VT_PROCFS, VT_AFS, VT_ISOFS, - VT_UNION + VT_UNION, VT_MSDOSFS }; /* |