Added msdosfs.
Obtained from: NetBSD
This commit is contained in:
parent
8f612a6f51
commit
418c49b77a
@ -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
|
||||
|
70
sys/fs/msdosfs/bootsect.h
Normal file
70
sys/fs/msdosfs/bootsect.h
Normal file
@ -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
|
120
sys/fs/msdosfs/bpb.h
Normal file
120
sys/fs/msdosfs/bpb.h
Normal file
@ -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 */
|
||||
};
|
267
sys/fs/msdosfs/denode.h
Normal file
267
sys/fs/msdosfs/denode.h
Normal file
@ -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 */
|
104
sys/fs/msdosfs/direntry.h
Normal file
104
sys/fs/msdosfs/direntry.h
Normal file
@ -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 */
|
97
sys/fs/msdosfs/fat.h
Normal file
97
sys/fs/msdosfs/fat.h
Normal file
@ -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 */
|
358
sys/fs/msdosfs/msdosfs_conv.c
Normal file
358
sys/fs/msdosfs/msdosfs_conv.c
Normal file
@ -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
|
689
sys/fs/msdosfs/msdosfs_denode.c
Normal file
689
sys/fs/msdosfs/msdosfs_denode.c
Normal file
@ -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;
|
||||
}
|
984
sys/fs/msdosfs/msdosfs_fat.c
Normal file
984
sys/fs/msdosfs/msdosfs_fat.c
Normal file
@ -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;
|
||||
}
|
763
sys/fs/msdosfs/msdosfs_lookup.c
Normal file
763
sys/fs/msdosfs/msdosfs_lookup.c
Normal file
@ -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);
|
||||
}
|
733
sys/fs/msdosfs/msdosfs_vfsops.c
Normal file
733
sys/fs/msdosfs/msdosfs_vfsops.c
Normal file
@ -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
|
||||
};
|
1933
sys/fs/msdosfs/msdosfs_vnops.c
Normal file
1933
sys/fs/msdosfs/msdosfs_vnops.c
Normal file
File diff suppressed because it is too large
Load Diff
183
sys/fs/msdosfs/msdosfsmount.h
Normal file
183
sys/fs/msdosfs/msdosfsmount.h
Normal file
@ -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(());
|
@ -48,6 +48,7 @@
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/timex.h>
|
||||
#include <vm/vm.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
70
sys/msdosfs/bootsect.h
Normal file
70
sys/msdosfs/bootsect.h
Normal file
@ -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
|
120
sys/msdosfs/bpb.h
Normal file
120
sys/msdosfs/bpb.h
Normal file
@ -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 */
|
||||
};
|
267
sys/msdosfs/denode.h
Normal file
267
sys/msdosfs/denode.h
Normal file
@ -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 */
|
104
sys/msdosfs/direntry.h
Normal file
104
sys/msdosfs/direntry.h
Normal file
@ -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 */
|
97
sys/msdosfs/fat.h
Normal file
97
sys/msdosfs/fat.h
Normal file
@ -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 */
|
358
sys/msdosfs/msdosfs_conv.c
Normal file
358
sys/msdosfs/msdosfs_conv.c
Normal file
@ -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
|
689
sys/msdosfs/msdosfs_denode.c
Normal file
689
sys/msdosfs/msdosfs_denode.c
Normal file
@ -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;
|
||||
}
|
984
sys/msdosfs/msdosfs_fat.c
Normal file
984
sys/msdosfs/msdosfs_fat.c
Normal file
@ -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;
|
||||
}
|
763
sys/msdosfs/msdosfs_lookup.c
Normal file
763
sys/msdosfs/msdosfs_lookup.c
Normal file
@ -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);
|
||||
}
|
733
sys/msdosfs/msdosfs_vfsops.c
Normal file
733
sys/msdosfs/msdosfs_vfsops.c
Normal file
@ -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
|
||||
};
|
1933
sys/msdosfs/msdosfs_vnops.c
Normal file
1933
sys/msdosfs/msdosfs_vnops.c
Normal file
File diff suppressed because it is too large
Load Diff
183
sys/msdosfs/msdosfsmount.h
Normal file
183
sys/msdosfs/msdosfsmount.h
Normal file
@ -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(());
|
@ -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 */ \
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user