diff --git a/sys/fs/msdosfs/bootsect.h b/sys/fs/msdosfs/bootsect.h index 86fc415770c0..11b93371a534 100644 --- a/sys/fs/msdosfs/bootsect.h +++ b/sys/fs/msdosfs/bootsect.h @@ -1,5 +1,5 @@ -/* $Id$ */ -/* $NetBSD: bootsect.h,v 1.4 1994/06/29 06:35:28 cgd Exp $ */ +/* $Id: bootsect.h,v 1.5 1997/02/22 09:40:43 peter Exp $ */ +/* $NetBSD: bootsect.h,v 1.9 1997/11/17 15:36:17 ws Exp $ */ /* * Written by Paul Popelka (paulp@uts.amdahl.com) @@ -23,36 +23,78 @@ * 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[479]; /* pad so structure is 512 bytes long */ - u_short bsBootSectSig; -#define BOOTSIG 0xaa55 + u_int8_t bsJump[3]; /* jump inst E9xxxx or EBxx90 */ + int8_t bsOemName[8]; /* OEM name and version */ + int8_t bsBPB[19]; /* BIOS parameter block */ + int8_t bsDriveNumber; /* drive number (0x80) */ + int8_t bsBootCode[479]; /* pad so struct is 512b */ + u_int8_t bsBootSectSig0; + u_int8_t bsBootSectSig1; +#define BOOTSIG0 0x55 +#define BOOTSIG1 0xaa +}; + +struct extboot { + int8_t exDriveNumber; /* drive number (0x80) */ + int8_t exReserved1; /* reserved */ + int8_t exBootSignature; /* ext. boot signature (0x29) */ +#define EXBOOTSIG 0x29 + int8_t exVolumeID[4]; /* volume ID number */ + int8_t exVolumeLabel[11]; /* volume label */ + int8_t exFileSysType[8]; /* fs type (FAT12 or FAT16) */ }; 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 + u_int8_t bsJump[3]; /* jump inst E9xxxx or EBxx90 */ + int8_t bsOemName[8]; /* OEM name and version */ + int8_t bsBPB[25]; /* BIOS parameter block */ + int8_t bsExt[26]; /* Bootsector Extension */ + int8_t bsBootCode[448]; /* pad so structure is 512b */ + u_int8_t bsBootSectSig0; + u_int8_t bsBootSectSig1; +#define BOOTSIG0 0x55 +#define BOOTSIG1 0xaa }; +struct bootsector710 { + u_int8_t bsJump[3]; /* jump inst E9xxxx or EBxx90 */ + int8_t bsOEMName[8]; /* OEM name and version */ + int8_t bsPBP[53]; /* BIOS parameter block */ + int8_t bsExt[26]; /* Bootsector Extension */ + int8_t bsBootCode[418]; /* pad so structure is 512b */ + u_int8_t bsBootSectSig2; /* 2 & 3 are only defined for FAT32? */ + u_int8_t bsBootSectSig3; + u_int8_t bsBootSectSig0; + u_int8_t bsBootSectSig1; +#define BOOTSIG0 0x55 +#define BOOTSIG1 0xaa +#define BOOTSIG2 0 +#define BOOTSIG3 0 +}; +#ifdef atari +/* + * The boot sector on a gemdos fs is a little bit different from the msdos fs + * format. Currently there is no need to declare a seperate structure, the + * bootsector33 struct will do. + */ +#if 0 +struct bootsec_atari { + u_int8_t bsBranch[2]; /* branch inst if auto-boot */ + int8_t bsFiller[6]; /* anything or nothing */ + int8_t bsSerial[3]; /* serial no. for mediachange */ + int8_t bsBPB[19]; /* BIOS parameter block */ + int8_t bsBootCode[482]; /* pad so struct is 512b */ +}; +#endif +#endif /* atari */ + union bootsector { struct bootsector33 bs33; struct bootsector50 bs50; + struct bootsector710 bs710; }; +#if 0 /* * Shorthand for fields in the bpb. */ @@ -68,3 +110,4 @@ union bootsector { #define bsHeads bsBPB.bpbHeads #define bsHiddenSecs bsBPB.bpbHiddenSecs #define bsHugeSectors bsBPB.bpbHugeSectors +#endif diff --git a/sys/fs/msdosfs/bpb.h b/sys/fs/msdosfs/bpb.h index 33e1eb69e00c..bc00a758de51 100644 --- a/sys/fs/msdosfs/bpb.h +++ b/sys/fs/msdosfs/bpb.h @@ -1,5 +1,5 @@ -/* $Id$ */ -/* $NetBSD: bpb.h,v 1.3 1994/06/29 06:35:29 cgd Exp $ */ +/* $Id: bpb.h,v 1.5 1997/02/22 09:40:44 peter Exp $ */ +/* $NetBSD: bpb.h,v 1.7 1997/11/17 15:36:24 ws Exp $ */ /* * Written by Paul Popelka (paulp@uts.amdahl.com) @@ -21,17 +21,17 @@ * 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 */ + u_int16_t bpbBytesPerSec; /* bytes per sector */ + u_int8_t bpbSecPerClust; /* sectors per cluster */ + u_int16_t bpbResSectors; /* number of reserved sectors */ + u_int8_t bpbFATs; /* number of FATs */ + u_int16_t bpbRootDirEnts; /* number of root directory entries */ + u_int16_t bpbSectors; /* total number of sectors */ + u_int8_t bpbMedia; /* media descriptor */ + u_int16_t bpbFATsecs; /* number of sectors per FAT */ + u_int16_t bpbSecPerTrack; /* sectors per track */ + u_int16_t bpbHeads; /* number of heads */ + u_int16_t bpbHiddenSecs; /* number of hidden sectors */ }; /* @@ -39,20 +39,70 @@ struct bpb33 { * 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 sectors if bpbSectors == 0 */ + u_int16_t bpbBytesPerSec; /* bytes per sector */ + u_int8_t bpbSecPerClust; /* sectors per cluster */ + u_int16_t bpbResSectors; /* number of reserved sectors */ + u_int8_t bpbFATs; /* number of FATs */ + u_int16_t bpbRootDirEnts; /* number of root directory entries */ + u_int16_t bpbSectors; /* total number of sectors */ + u_int8_t bpbMedia; /* media descriptor */ + u_int16_t bpbFATsecs; /* number of sectors per FAT */ + u_int16_t bpbSecPerTrack; /* sectors per track */ + u_int16_t bpbHeads; /* number of heads */ + u_int32_t bpbHiddenSecs; /* # of hidden sectors */ + u_int32_t bpbHugeSectors; /* # of sectors if bpbSectors == 0 */ }; +/* + * BPB for DOS 7.10 (FAT32). This one has a few extensions to bpb50. + */ +struct bpb710 { + u_int16_t bpbBytesPerSec; /* bytes per sector */ + u_int8_t bpbSecPerClust; /* sectors per cluster */ + u_int16_t bpbResSectors; /* number of reserved sectors */ + u_int8_t bpbFATs; /* number of FATs */ + u_int16_t bpbRootDirEnts; /* number of root directory entries */ + u_int16_t bpbSectors; /* total number of sectors */ + u_int8_t bpbMedia; /* media descriptor */ + u_int16_t bpbFATsecs; /* number of sectors per FAT */ + u_int16_t bpbSecPerTrack; /* sectors per track */ + u_int16_t bpbHeads; /* number of heads */ + u_int32_t bpbHiddenSecs; /* # of hidden sectors */ + u_int32_t bpbHugeSectors; /* # of sectors if bpbSectors == 0 */ + u_int32_t bpbBigFATsecs; /* like bpbFATsecs for FAT32 */ + u_int16_t bpbExtFlags; /* extended flags: */ +#define FATNUM 0xf /* mask for numbering active FAT */ +#define FATMIRROR 0x80 /* FAT is mirrored (like it always was) */ + u_int16_t bpbFSVers; /* filesystem version */ +#define FSVERS 0 /* currently only 0 is understood */ + u_int32_t bpbRootClust; /* start cluster for root directory */ + u_int16_t bpbFSInfo; /* filesystem info structure sector */ + u_int16_t bpbBackup; /* backup boot sector */ + /* There is a 12 byte filler here, but we ignore it */ +}; + +#ifdef atari +/* + * BPB for gemdos filesystems. Atari leaves the obsolete stuff undefined. + * Currently there is no need for a separate BPB structure. + */ +#if 0 +struct bpb_a { + u_int16_t bpbBytesPerSec; /* bytes per sector */ + u_int8_t bpbSecPerClust; /* sectors per cluster */ + u_int16_t bpbResSectors; /* number of reserved sectors */ + u_int8_t bpbFATs; /* number of FATs */ + u_int16_t bpbRootDirEnts; /* number of root directory entries */ + u_int16_t bpbSectors; /* total number of sectors */ + u_int8_t bpbUseless1; /* meaningless on gemdos fs */ + u_int16_t bpbFATsecs; /* number of sectors per FAT */ + u_int16_t bpbUseless2; /* meaningless for harddisk fs */ + u_int16_t bpbUseless3; /* meaningless for harddisk fs */ + u_int16_t bpbHiddenSecs; /* the TOS-BIOS ignores this */ +}; +#endif +#endif /* atari */ + /* * The following structures represent how the bpb's look on disk. shorts * and longs are just character arrays of the appropriate length. This is @@ -64,40 +114,39 @@ struct bpb50 { * use the macros for the big-endian case. */ #include -#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)) - +#if (BYTE_ORDER == LITTLE_ENDIAN) /* && defined(UNALIGNED_ACCESS) */ +#define getushort(x) *((u_int16_t *)(x)) +#define getulong(x) *((u_int32_t *)(x)) +#define putushort(p, v) (*((u_int16_t *)(p)) = (v)) +#define putulong(p, v) (*((u_int32_t *)(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) +#define getushort(x) (((u_int8_t *)(x))[0] + (((u_int8_t *)(x))[1] << 8)) +#define getulong(x) (((u_int8_t *)(x))[0] + (((u_int8_t *)(x))[1] << 8) \ + + (((u_int8_t *)(x))[2] << 16) \ + + (((u_int8_t *)(x))[3] << 24)) +#define putushort(p, v) (((u_int8_t *)(p))[0] = (v), \ + ((u_int8_t *)(p))[1] = (v) >> 8) +#define putulong(p, v) (((u_int8_t *)(p))[0] = (v), \ + ((u_int8_t *)(p))[1] = (v) >> 8, \ + ((u_int8_t *)(p))[2] = (v) >> 16,\ + ((u_int8_t *)(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 */ + int8_t bpbBytesPerSec[2]; /* bytes per sector */ + int8_t bpbSecPerClust; /* sectors per cluster */ + int8_t bpbResSectors[2]; /* number of reserved sectors */ + int8_t bpbFATs; /* number of FATs */ + int8_t bpbRootDirEnts[2]; /* number of root directory entries */ + int8_t bpbSectors[2]; /* total number of sectors */ + int8_t bpbMedia; /* media descriptor */ + int8_t bpbFATsecs[2]; /* number of sectors per FAT */ + int8_t bpbSecPerTrack[2]; /* sectors per track */ + int8_t bpbHeads[2]; /* number of heads */ + int8_t bpbHiddenSecs[2]; /* number of hidden sectors */ }; /* @@ -105,16 +154,56 @@ struct byte_bpb33 { * 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 sectors if bpbSectors == 0 */ + int8_t bpbBytesPerSec[2]; /* bytes per sector */ + int8_t bpbSecPerClust; /* sectors per cluster */ + int8_t bpbResSectors[2]; /* number of reserved sectors */ + int8_t bpbFATs; /* number of FATs */ + int8_t bpbRootDirEnts[2]; /* number of root directory entries */ + int8_t bpbSectors[2]; /* total number of sectors */ + int8_t bpbMedia; /* media descriptor */ + int8_t bpbFATsecs[2]; /* number of sectors per FAT */ + int8_t bpbSecPerTrack[2]; /* sectors per track */ + int8_t bpbHeads[2]; /* number of heads */ + int8_t bpbHiddenSecs[4]; /* number of hidden sectors */ + int8_t bpbHugeSectors[4]; /* # of sectors if bpbSectors == 0 */ +}; + +/* + * BPB for DOS 7.10 (FAT32). This one has a few extensions to bpb50. + */ +struct byte_bpb710 { + u_int8_t bpbBytesPerSec[2]; /* bytes per sector */ + u_int8_t bpbSecPerClust; /* sectors per cluster */ + u_int8_t bpbResSectors[2]; /* number of reserved sectors */ + u_int8_t bpbFATs; /* number of FATs */ + u_int8_t bpbRootDirEnts[2]; /* number of root directory entries */ + u_int8_t bpbSectors[2]; /* total number of sectors */ + u_int8_t bpbMedia; /* media descriptor */ + u_int8_t bpbFATsecs[2]; /* number of sectors per FAT */ + u_int8_t bpbSecPerTrack[2]; /* sectors per track */ + u_int8_t bpbHeads[2]; /* number of heads */ + u_int8_t bpbHiddenSecs[4]; /* # of hidden sectors */ + u_int8_t bpbHugeSectors[4]; /* # of sectors if bpbSectors == 0 */ + u_int8_t bpbBigFATsecs[4]; /* like bpbFATsecs for FAT32 */ + u_int8_t bpbExtFlags[2]; /* extended flags: */ + u_int8_t bpbFSVers[2]; /* filesystem version */ + u_int8_t bpbRootClust[4]; /* start cluster for root directory */ + u_int8_t bpbFSInfo[2]; /* filesystem info structure sector */ + u_int8_t bpbBackup[2]; /* backup boot sector */ + /* There is a 12 byte filler here, but we ignore it */ +}; + +/* + * FAT32 FSInfo block. + */ +struct fsinfo { + u_int8_t fsisig1[4]; + u_int8_t fsifill1[480]; + u_int8_t fsisig2[4]; + u_int8_t fsinfree[4]; + u_int8_t fsinxtfree[4]; + u_int8_t fsifill2[12]; + u_int8_t fsisig3[4]; + u_int8_t fsifill3[508]; + u_int8_t fsisig4[4]; }; diff --git a/sys/fs/msdosfs/denode.h b/sys/fs/msdosfs/denode.h index 6ad3fccb5de8..2b9d63698f22 100644 --- a/sys/fs/msdosfs/denode.h +++ b/sys/fs/msdosfs/denode.h @@ -1,9 +1,9 @@ -/* $Id: denode.h,v 1.13 1997/08/26 07:32:36 phk Exp $ */ -/* $NetBSD: denode.h,v 1.8 1994/08/21 18:43:49 ws Exp $ */ +/* $Id: denode.h,v 1.14 1997/10/17 12:36:16 phk Exp $ */ +/* $NetBSD: denode.h,v 1.25 1997/11/17 15:36:28 ws Exp $ */ /*- - * Copyright (C) 1994 Wolfgang Solfrank. - * Copyright (C) 1994 TooLs GmbH. + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * @@ -103,8 +103,8 @@ * structure (fc_frcn). */ struct fatcache { - u_short fc_frcn; /* file relative cluster number */ - u_short fc_fsrcn; /* filesystem relative cluster number */ + u_long fc_frcn; /* file relative cluster number */ + u_long fc_fsrcn; /* filesystem relative cluster number */ }; /* @@ -121,7 +121,7 @@ struct fatcache { * to */ #define FC_LASTFC 1 /* entry for the last cluster in the file */ -#define FCE_EMPTY 0xffff /* doesn't represent an actual cluster # */ +#define FCE_EMPTY 0xffffffff /* doesn't represent an actual cluster # */ /* * Set a slot in the fat cache. @@ -143,19 +143,21 @@ struct denode { 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_diroffset; /* offset of this entry in the directory cluster */ u_long de_fndoffset; /* offset of found dir entry */ + int de_fndcnt; /* number of slots before de_fndoffset */ long de_refcnt; /* reference count */ struct msdosfsmount *de_pmp; /* addr of our mount struct */ struct lockf *de_lockf; /* byte level lock list */ - /* 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_Name[12]; /* name, from DOS 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_char de_CHun; /* Hundredth of second of CTime*/ + u_short de_CTime; /* creation time */ + u_short de_CDate; /* creation date */ + u_short de_ADate; /* access date */ + u_short de_MTime; /* modification time */ + u_short de_MDate; /* modification date */ + u_long de_StartCluster; /* starting cluster of file */ u_long de_FileSize; /* size of file in bytes */ struct fatcache de_fc[FC_SIZE]; /* fat cache */ u_quad_t de_modrev; /* Revision level for lease. */ @@ -164,31 +166,49 @@ struct denode { /* * Values for the de_flag field of the denode. */ -#define DE_UPDATE 0x0004 /* modification time update request */ -#define DE_MODIFIED 0x0080 /* denode has been modified, but DE_UPDATE - * isn't set */ +#define DE_UPDATE 0x0004 /* Modification time update request */ +#define DE_CREATE 0x0008 /* Creation time update */ +#define DE_ACCESS 0x0010 /* Access time update */ +#define DE_MODIFIED 0x0020 /* Denode has been modified */ +#define DE_RENAME 0x0040 /* Denode is in the process of being renamed */ + /* * 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) \ +#define DE_INTERNALIZE32(dep, dp) \ + ((dep)->de_StartCluster |= getushort((dp)->deHighClust) << 16) +#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_CHun = (dp)->deCHundredth, \ + (dep)->de_CTime = getushort((dp)->deCTime), \ + (dep)->de_CDate = getushort((dp)->deCDate), \ + (dep)->de_ADate = getushort((dp)->deADate), \ + (dep)->de_MTime = getushort((dp)->deMTime), \ + (dep)->de_MDate = getushort((dp)->deMDate), \ (dep)->de_StartCluster = getushort((dp)->deStartCluster), \ - (dep)->de_FileSize = getulong((dp)->deFileSize)) + (dep)->de_FileSize = getulong((dp)->deFileSize), \ + (FAT32((dep)->de_pmp) ? DE_INTERNALIZE32((dep), (dp)) : 0)) +#define DE_EXTERNALIZE32(dp, dep) \ + putushort((dp)->deHighClust, (dep)->de_StartCluster >> 16) #define DE_EXTERNALIZE(dp, dep) \ (bcopy((dep)->de_Name, (dp)->deName, 11), \ bzero((dp)->deReserved, 10), \ (dp)->deAttributes = (dep)->de_Attributes, \ - putushort((dp)->deTime, (dep)->de_Time), \ - putushort((dp)->deDate, (dep)->de_Date), \ + (dp)->deCHundredth = (dep)->de_CHun, \ + putushort((dp)->deCTime, (dep)->de_CTime), \ + putushort((dp)->deCDate, (dep)->de_CDate), \ + putushort((dp)->deADate, (dep)->de_ADate), \ + putushort((dp)->deMTime, (dep)->de_MTime), \ + putushort((dp)->deMDate, (dep)->de_MDate), \ putushort((dp)->deStartCluster, (dep)->de_StartCluster), \ - putulong((dp)->deFileSize, (dep)->de_FileSize)) + putulong((dp)->deFileSize, \ + ((dep)->de_Attributes & ATTR_DIRECTORY) ? 0 : (dep)->de_FileSize), \ + (FAT32((dep)->de_pmp) ? DE_EXTERNALIZE32((dp), (dep)) : 0)) #define de_forw de_chain[0] #define de_back de_chain[1] @@ -198,17 +218,20 @@ struct denode { #define VTODE(vp) ((struct denode *)(vp)->v_data) #define DETOV(de) ((de)->de_vnode) -#define DE_TIMES(dep, t) \ - if ((dep)->de_flag & DE_UPDATE) { \ - if (!((dep)->de_Attributes & ATTR_DIRECTORY)) { \ - struct timespec DE_TIMES_ts; \ - (dep)->de_flag |= DE_MODIFIED; \ - TIMEVAL_TO_TIMESPEC((t), &DE_TIMES_ts); \ - unix2dostime(&DE_TIMES_ts, &(dep)->de_Date, \ - &(dep)->de_Time); \ +#define DETIMES(dep, acc, mod, cre) \ + if ((dep)->de_flag & (DE_UPDATE | DE_CREATE | DE_ACCESS)) { \ + (dep)->de_flag |= DE_MODIFIED; \ + if ((dep)->de_flag & DE_UPDATE) { \ + unix2dostime((mod), &(dep)->de_MDate, &(dep)->de_MTime, NULL); \ (dep)->de_Attributes |= ATTR_ARCHIVE; \ } \ - (dep)->de_flag &= ~DE_UPDATE; \ + if (!((dep)->de_pmp->pm_flags & MSDOSFSMNT_NOWIN95)) { \ + if ((dep)->de_flag & DE_ACCESS) \ + unix2dostime((acc), &(dep)->de_ADate, NULL, NULL); \ + if ((dep)->de_flag & DE_CREATE) \ + unix2dostime((cre), &(dep)->de_CDate, &(dep)->de_CTime, &(dep)->de_CHun); \ + } \ + (dep)->de_flag &= ~(DE_UPDATE | DE_CREATE | DE_ACCESS); \ } /* @@ -219,9 +242,10 @@ struct defid { 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 */ + u_long defid_dirofs; /* offset of entry within the cluster */ +#if 0 + u_long defid_gen; /* generation number */ +#endif }; extern vop_t **msdosfs_vnodeop_p; @@ -233,5 +257,19 @@ int msdosfs_reclaim __P((struct vop_reclaim_args *)); /* * Internal service routine prototypes. */ -int deget __P((struct msdosfsmount * pmp, u_long dirclust, u_long diroffset, struct direntry * direntptr, struct denode ** depp)); +int deget __P((struct msdosfsmount *, u_long, u_long, struct denode **)); +int uniqdosname __P((struct denode *, struct componentname *, u_char *)); +int findwin95 __P((struct denode *)); + +int readep __P((struct msdosfsmount *pmp, u_long dirclu, u_long dirofs, struct buf **bpp, struct direntry **epp)); +int readde __P((struct denode *dep, struct buf **bpp, struct direntry **epp)); +int deextend __P((struct denode *dep, u_long length, struct ucred *cred)); +int fillinusemap __P((struct msdosfsmount *pmp)); +void reinsert __P((struct denode *dep)); +int dosdirempty __P((struct denode *dep)); +int createde __P((struct denode *dep, struct denode *ddep, struct denode **depp, struct componentname *cnp)); +int deupdat __P((struct denode *dep, int waitfor)); +int removede __P((struct denode *pdep, struct denode *dep)); +int detrunc __P((struct denode *dep, u_long length, int flags, struct ucred *cred, struct proc *p)); +int doscheckpath __P(( struct denode *source, struct denode *target)); #endif /* KERNEL */ diff --git a/sys/fs/msdosfs/direntry.h b/sys/fs/msdosfs/direntry.h index b97a1055f402..0bfe164af832 100644 --- a/sys/fs/msdosfs/direntry.h +++ b/sys/fs/msdosfs/direntry.h @@ -1,9 +1,9 @@ -/* $Id$ */ -/* $NetBSD: direntry.h,v 1.7 1994/08/21 18:43:54 ws Exp $ */ +/* $Id: direntry.h,v 1.4 1997/02/22 09:40:45 peter Exp $ */ +/* $NetBSD: direntry.h,v 1.14 1997/11/17 15:36:32 ws Exp $ */ /*- - * Copyright (C) 1994 Wolfgang Solfrank. - * Copyright (C) 1994 TooLs GmbH. + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * @@ -52,26 +52,55 @@ * 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 */ + u_int8_t 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_int8_t deExtension[3]; /* extension, blank filled */ + u_int8_t 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_int8_t deReserved[1]; /* reserved */ + u_int8_t deCHundredth; /* hundredth of seconds in CTime */ + u_int8_t deCTime[2]; /* create time */ + u_int8_t deCDate[2]; /* create date */ + u_int8_t deADate[2]; /* access date */ + u_int8_t deHighClust[2]; /* high bytes of cluster number */ + u_int8_t deMTime[2]; /* last update time */ + u_int8_t deMDate[2]; /* last update date */ + u_int8_t deStartCluster[2]; /* starting cluster of file */ + u_int8_t deFileSize[4]; /* size of file in bytes */ }; +/* + * Structure of a Win95 long name directory entry + */ +struct winentry { + u_int8_t weCnt; +#define WIN_LAST 0x40 +#define WIN_CNT 0x3f + u_int8_t wePart1[10]; + u_int8_t weAttributes; +#define ATTR_WIN95 0x0f + u_int8_t weReserved1; + u_int8_t weChksum; + u_int8_t wePart2[12]; + u_int16_t weReserved2; + u_int8_t wePart3[4]; +}; +#define WIN_CHARS 13 /* Number of chars per winentry */ + +/* + * Maximum filename length in Win95 + * Note: Must be < sizeof(dirent.d_name) + */ +#define WIN_MAXLEN 255 + /* * This is the format of the contents of the deTime field in the direntry * structure. @@ -97,8 +126,15 @@ struct direntry { #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)); +struct dirent; +void unix2dostime __P((struct timespec *tsp, u_int16_t *ddp, + u_int16_t *dtp, u_int8_t *dhp)); +void dos2unixtime __P((u_int dd, u_int dt, u_int dh, struct timespec *tsp)); +int dos2unixfn __P((u_char dn[11], u_char *un, int lower)); +int unix2dosfn __P((const u_char *un, u_char dn[12], int unlen, u_int gen)); +int unix2winfn __P((const u_char *un, int unlen, struct winentry *wep, int cnt, int chksum)); +int winChkName __P((const u_char *un, int unlen, struct winentry *wep, int chksum)); +int win2unixfn __P((struct winentry *wep, struct dirent *dp, int chksum)); +u_int8_t winChksum __P((u_int8_t *name)); +int winSlotCnt __P((const u_char *un, int unlen)); #endif /* KERNEL */ diff --git a/sys/fs/msdosfs/fat.h b/sys/fs/msdosfs/fat.h index f8fdb6fd1eeb..74b05e2cf78d 100644 --- a/sys/fs/msdosfs/fat.h +++ b/sys/fs/msdosfs/fat.h @@ -1,9 +1,9 @@ -/* $Id$ */ -/* $NetBSD: fat.h,v 1.4 1994/08/21 18:43:57 ws Exp $ */ +/* $Id: fat.h,v 1.6 1997/02/22 09:40:45 peter Exp $ */ +/* $NetBSD: fat.h,v 1.12 1997/11/17 15:36:36 ws Exp $ */ /*- - * Copyright (C) 1994 Wolfgang Solfrank. - * Copyright (C) 1994 TooLs GmbH. + * Copyright (C) 1994, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * @@ -51,28 +51,37 @@ /* * 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 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 CLUST_FIRST 2 /* first legal cluster number */ +#define CLUST_RSRVD 0xfffffff6 /* reserved cluster range */ +#define CLUST_BAD 0xfffffff7 /* a cluster with a defect */ +#define CLUST_EOFS 0xfffffff8 /* start of eof cluster range */ +#define CLUST_EOFE 0xffffffff /* 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 */ +#define FAT12_MASK 0x00000fff /* mask for 12 bit cluster numbers */ +#define FAT16_MASK 0x0000ffff /* mask for 16 bit cluster numbers */ +#define FAT32_MASK 0x0fffffff /* mask for FAT32 cluster numbers */ /* + * MSDOSFS: * 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. + * than 4078 ((CLUST_RSRVS - CLUST_FIRST) & FAT12_MASK) then we've got a + * 16 bit fat filesystem. While mounting, the result of this test is stored + * in pm_fatentrysize. + * GEMDOS-flavour (atari): + * If the filesystem is on floppy we've got a 12 bit fat filesystem, otherwise + * 16 bit. We check the d_type field in the disklabel struct while mounting + * and store the result in the pm_fatentrysize. Note that this kind of + * detection gets flakey when mounting a vnd-device. */ -#define FAT12(pmp) (pmp->pm_maxcluster <= 4086) -#define FAT16(pmp) (pmp->pm_maxcluster > 4086) +#define FAT12(pmp) (pmp->pm_fatmask == FAT12_MASK) +#define FAT16(pmp) (pmp->pm_fatmask == FAT16_MASK) +#define FAT32(pmp) (pmp->pm_fatmask == FAT32_MASK) -#define MSDOSFSEOF(cn) (((cn) & 0xfff8) == 0xfff8) +#define MSDOSFSEOF(pmp, cn) ((((cn) | ~(pmp)->pm_fatmask) & CLUST_EOFS) == CLUST_EOFS) #ifdef KERNEL /* @@ -88,7 +97,7 @@ */ #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 pcbmap __P((struct denode *dep, u_long findcn, daddr_t *bnp, u_long *cnp, int* sp)); 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)); @@ -96,15 +105,4 @@ 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)); void fc_purge __P((struct denode *dep, u_int frcn)); -int readep __P((struct msdosfsmount *pmp, u_long dirclu, u_long dirofs, struct buf **bpp, struct direntry **epp)); -int readde __P((struct denode *dep, struct buf **bpp, struct direntry **epp)); -int deextend __P((struct denode *dep, off_t length, struct ucred *cred)); -int fillinusemap __P((struct msdosfsmount *pmp)); -int reinsert __P((struct denode *dep)); -int dosdirempty __P((struct denode *dep)); -int createde __P((struct denode *dep, struct denode *ddep, struct denode **depp)); -int deupdat __P((struct denode *dep, struct timespec *tp, int waitfor)); -int removede __P((struct denode *pdep, struct denode *dep)); -int detrunc __P((struct denode *dep, u_long length, int flags, struct ucred *cred, struct proc *p)); -int doscheckpath __P(( struct denode *source, struct denode *target)); #endif /* KERNEL */ diff --git a/sys/fs/msdosfs/msdosfs_conv.c b/sys/fs/msdosfs/msdosfs_conv.c index 59f4d2c1457c..727cacd80304 100644 --- a/sys/fs/msdosfs/msdosfs_conv.c +++ b/sys/fs/msdosfs/msdosfs_conv.c @@ -1,6 +1,37 @@ -/* $Id: msdosfs_conv.c,v 1.13 1997/02/22 09:40:46 peter Exp $ */ -/* $NetBSD: msdosfs_conv.c,v 1.6.2.1 1994/08/30 02:27:57 cgd Exp $ */ +/* $Id: msdosfs_conv.c,v 1.14 1998/02/09 06:09:50 eivind Exp $ */ +/* $NetBSD: msdosfs_conv.c,v 1.25 1997/11/17 15:36:40 ws Exp $ */ +/*- + * Copyright (C) 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1995, 1997 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) * @@ -23,8 +54,9 @@ #include #include #include /* defines tz */ -#include /* defines tz */ +#include #include +#include /* * MSDOSFS include files. @@ -61,10 +93,11 @@ static u_short lastdtime; * file timestamps. The passed in unix time is assumed to be in GMT. */ void -unix2dostime(tsp, ddp, dtp) +unix2dostime(tsp, ddp, dtp, dhp) struct timespec *tsp; - u_short *ddp; - u_short *dtp; + u_int16_t *ddp; + u_int16_t *dtp; + u_int8_t *dhp; { u_long t; u_long days; @@ -80,9 +113,10 @@ unix2dostime(tsp, ddp, dtp) t = tsp->tv_sec - (tz.tz_minuteswest * 60) - (wall_cmos_clock ? adjkerntz : 0); /* - daylight savings time correction */ + t &= ~1; if (lasttime != t) { lasttime = t; - lastdtime = (((t % 60) >> 1) << DT_2SECONDS_SHIFT) + lastdtime = (((t / 2) % 30) << DT_2SECONDS_SHIFT) + (((t / 60) % 60) << DT_MINUTES_SHIFT) + (((t / 3600) % 24) << DT_HOURS_SHIFT); @@ -117,7 +151,11 @@ unix2dostime(tsp, ddp, dtp) lastddate += (year - 1980) << DD_YEAR_SHIFT; } } - *dtp = lastdtime; + if (dtp) + *dtp = lastdtime; + if (dhp) + *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000; + *ddp = lastddate; } @@ -136,9 +174,10 @@ static u_long lastseconds; * not be too efficient. */ void -dos2unixtime(dd, dt, tsp) - u_short dd; - u_short dt; +dos2unixtime(dd, dt, dh, tsp) + u_int dd; + u_int dt; + u_int dh; struct timespec *tsp; { u_long seconds; @@ -147,9 +186,18 @@ dos2unixtime(dd, dt, tsp) u_long days; u_short *months; + if (dd == 0) { + /* + * Uninitialized field, return the epoch. + */ + tsp->tv_sec = 0; + tsp->tv_nsec = 0; + return; + } seconds = (((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) << 1) + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60 - + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600; + + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600 + + dh / 100; /* * If the year, month, and day from the last conversion are the * same then use the saved value. @@ -165,8 +213,7 @@ dos2unixtime(dd, dt, tsp) months = year & 0x03 ? regyear : leapyear; month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT; if (month < 1 || month > 12) { - printf( - "dos2unixtime(): month value out of range (%ld)\n", + printf("dos2unixtime(): month value out of range (%ld)\n", month); month = 1; } @@ -178,17 +225,116 @@ dos2unixtime(dd, dt, tsp) tsp->tv_sec = seconds + lastseconds + (tz.tz_minuteswest * 60) + adjkerntz; /* + daylight savings time correction */ - tsp->tv_nsec = 0; + tsp->tv_nsec = (dh % 100) * 10000000; } -/* - * 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 | ' ') +static u_char +unix2dos[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 00-07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 08-0f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 10-17 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 18-1f */ + 0, 0x21, 0, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */ + 0x28, 0x29, 0, 0, 0, 0x2d, 0, 0, /* 28-2f */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */ + 0x38, 0x39, 0, 0, 0, 0, 0, 0, /* 38-3f */ + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 40-47 */ + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 48-4f */ + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 50-57 */ + 0x58, 0x59, 0x5a, 0, 0, 0, 0x5e, 0x5f, /* 58-5f */ + 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 60-67 */ + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 68-6f */ + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 70-77 */ + 0x58, 0x59, 0x5a, 0x7b, 0, 0x7d, 0x7e, 0, /* 78-7f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 80-87 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 88-8f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 90-97 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 98-9f */ + 0, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, /* a0-a7 */ + 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee, /* a8-af */ + 0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, /* b0-b7 */ + 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8, /* b8-bf */ + 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* c0-c7 */ + 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* c8-cf */ + 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, /* d0-d7 */ + 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1, /* d8-df */ + 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* e0-e7 */ + 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* e8-ef */ + 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0xf6, /* f0-f7 */ + 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0x98, /* f8-ff */ +}; + +static u_char +dos2unix[256] = { + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 00-07 */ + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 08-0f */ + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 10-17 */ + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 18-1f */ + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 28-2f */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */ + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 38-3f */ + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 40-47 */ + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 48-4f */ + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 50-57 */ + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 58-5f */ + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 60-67 */ + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 68-6f */ + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 70-77 */ + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 78-7f */ + 0xc7, 0xfc, 0xe9, 0xe2, 0xe4, 0xe0, 0xe5, 0xe7, /* 80-87 */ + 0xea, 0xeb, 0xe8, 0xef, 0xee, 0xec, 0xc4, 0xc5, /* 88-8f */ + 0xc9, 0xe6, 0xc6, 0xf4, 0xf6, 0xf2, 0xfb, 0xf9, /* 90-97 */ + 0xff, 0xd6, 0xdc, 0xf8, 0xa3, 0xd8, 0xd7, 0x3f, /* 98-9f */ + 0xe1, 0xed, 0xf3, 0xfa, 0xf1, 0xd1, 0xaa, 0xba, /* a0-a7 */ + 0xbf, 0xae, 0xac, 0xbd, 0xbc, 0xa1, 0xab, 0xbb, /* a8-af */ + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xc1, 0xc2, 0xc0, /* b0-b7 */ + 0xa9, 0x3f, 0x3f, 0x3f, 0x3f, 0xa2, 0xa5, 0x3f, /* b8-bf */ + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xe3, 0xc3, /* c0-c7 */ + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xa4, /* c8-cf */ + 0xf0, 0xd0, 0xca, 0xcb, 0xc8, 0x3f, 0xcd, 0xce, /* d0-d7 */ + 0xcf, 0x3f, 0x3f, 0x3f, 0x3f, 0xa6, 0xcc, 0x3f, /* d8-df */ + 0xd3, 0xdf, 0xd4, 0xd2, 0xf5, 0xd5, 0xb5, 0xfe, /* e0-e7 */ + 0xde, 0xda, 0xdb, 0xd9, 0xfd, 0xdd, 0xaf, 0x3f, /* e8-ef */ + 0xad, 0xb1, 0x3f, 0xbe, 0xb6, 0xa7, 0xf7, 0xb8, /* f0-f7 */ + 0xb0, 0xa8, 0xb7, 0xb9, 0xb3, 0xb2, 0x3f, 0x3f, /* f8-ff */ +}; + +static u_char +u2l[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 00-07 */ + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 08-0f */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 10-17 */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 18-1f */ + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 28-2f */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */ + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 38-3f */ + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 40-47 */ + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 48-4f */ + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 50-57 */ + 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 58-5f */ + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 60-67 */ + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 68-6f */ + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 70-77 */ + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 78-7f */ + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 80-87 */ + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 88-8f */ + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 90-97 */ + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 98-9f */ + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* a0-a7 */ + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* a8-af */ + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* b0-b7 */ + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* b8-bf */ + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* c0-c7 */ + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* c8-cf */ + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* d0-d7 */ + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* d8-df */ + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* e0-e7 */ + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* e8-ef */ + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* f0-f7 */ + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* f8-ff */ +}; /* * DOS filenames are made of 2 parts, the name part and the extension part. @@ -203,58 +349,14 @@ dos2unixtime(dd, dt, tsp) * null. */ int -dos2unixfn(dn, un) +dos2unixfn(dn, un, lower) u_char dn[11]; u_char *un; + int lower; { int i; - int ni; - int ei; - int thislong = 0; + int thislong = 1; 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 @@ -262,31 +364,71 @@ dos2unixfn(dn, un) * 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; + if (*dn == SLOT_E5) + c = dos2unix[0xe5]; + else + c = dos2unix[*dn]; + *un++ = lower ? u2l[c] : c; + dn++; - return thislong; + /* + * Copy the name portion into the unix filename string. + */ + for (i = 1; i < 8 && *dn != ' '; i++) { + c = dos2unix[*dn++]; + *un++ = lower ? u2l[c] : c; + thislong++; + } + dn += 8 - i; + + /* + * Now, if there is an extension then put in a period and copy in + * the extension. + */ + if (*dn != ' ') { + *un++ = '.'; + thislong++; + for (i = 0; i < 3 && *dn != ' '; i++) { + c = dos2unix[*dn++]; + *un++ = lower ? u2l[c] : c; + thislong++; + } + } + *un++ = 0; + + return (thislong); } /* - * Convert a unix filename to a DOS filename. This function does not ensure - * that valid characters for a dos filename are supplied. + * Convert a unix filename to a DOS filename according to Win95 rules. + * If applicable and gen is not 0, it is inserted into the converted + * filename as a generation number. + * Returns + * 0 if name couldn't be converted + * 1 if the converted name is the same as the original + * (no long filename entry necessary for Win95) + * 2 if conversion was successful + * 3 if conversion was successful and generation number was inserted */ -void -unix2dosfn(un, dn, unlen) - u_char *un; - u_char dn[11]; +int +unix2dosfn(un, dn, unlen, gen) + const u_char *un; + u_char dn[12]; int unlen; + u_int gen; { - int i; - u_char c; + int i, j, l; + int conv = 1; + const u_char *cp, *dp, *dp1; + u_char gentext[6], *wcp; /* * Fill the dos filename string with blanks. These are DOS's pad * characters. */ - for (i = 0; i <= 10; i++) + for (i = 0; i < 11; i++) dn[i] = ' '; + dn[11] = 0; /* * The filenames "." and ".." are handled specially, since they @@ -294,65 +436,393 @@ unix2dosfn(un, dn, unlen) */ if (un[0] == '.' && unlen == 1) { dn[0] = '.'; - return; + return gen <= 1; } if (un[0] == '.' && un[1] == '.' && unlen == 2) { dn[0] = '.'; dn[1] = '.'; - return; + return gen <= 1; } /* - * 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. + * Filenames with only blanks and dots are not allowed! */ - for (i = 0; i <= 7 && unlen && (c = *un) && c != '.'; i++) { - dn[i] = islower(c) ? toupper(c) : c; - un++; - unlen--; + for (cp = un, i = unlen; --i >= 0; cp++) + if (*cp != ' ' && *cp != '.') + break; + if (i < 0) + return 0; + + /* + * Now find the extension + * Note: dot as first char doesn't start extension + * and trailing dots and blanks are ignored + */ + dp = dp1 = 0; + for (cp = un + 1, i = unlen - 1; --i >= 0;) { + switch (*cp++) { + case '.': + if (!dp1) + dp1 = cp; + break; + case ' ': + break; + default: + if (dp1) + dp = dp1; + dp1 = 0; + break; + } } /* - * 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. + * Now convert it */ - if (dn[0] == SLOT_DELETED) + if (dp) { + if (dp1) + l = dp1 - dp; + else + l = unlen - (dp - un); + for (i = 0, j = 8; i < l && j < 11; i++, j++) { + if (dp[i] != (dn[j] = unix2dos[dp[i]]) + && conv != 3) + conv = 2; + if (!dn[j]) { + conv = 3; + dn[j--] = ' '; + } + } + if (i < l) + conv = 3; + dp--; + } else { + for (dp = cp; *--dp == ' ' || *dp == '.';); + dp++; + } + + /* + * Now convert the rest of the name + */ + for (i = j = 0; un < dp && j < 8; i++, j++, un++) { + if (*un != (dn[j] = unix2dos[*un]) + && conv != 3) + conv = 2; + if (!dn[j]) { + conv = 3; + dn[j--] = ' '; + } + } + if (un < dp) + conv = 3; + /* + * If we didn't have any chars in filename, + * generate a default + */ + if (!j) + dn[0] = '_'; + + /* + * The first character cannot be E5, + * because that means a deleted entry + */ + if (dn[0] == 0xe5) dn[0] = SLOT_E5; /* - * Strip any further characters up to a '.' or the end of the - * string. + * If there wasn't any char dropped, + * there is no place for generation numbers */ - while (unlen && (c = *un)) { - un++; - unlen--; - /* Make sure we've skipped over the dot before stopping. */ - if (c == '.') - break; + if (conv != 3) { + if (gen > 1) + return 0; + return conv; } /* - * 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. + * Now insert the generation number into the filename part */ - for (i = 8; i <= 10 && unlen && (c = *un); i++) { - dn[i] = islower(c) ? toupper(c) : c; - un++; - unlen--; - } + for (wcp = gentext + sizeof(gentext); wcp > gentext && gen; gen /= 10) + *--wcp = gen % 10 + '0'; + if (gen) + return 0; + for (i = 8; dn[--i] == ' ';); + i++; + if (gentext + sizeof(gentext) - wcp + 1 > 8 - i) + i = 8 - (gentext + sizeof(gentext) - wcp + 1); + dn[i++] = '~'; + while (wcp < gentext + sizeof(gentext)) + dn[i++] = *wcp++; + return 3; } /* - * Get rid of these macros before someone discovers we are using such - * hideous things. + * Create a Win95 long name directory entry + * Note: assumes that the filename is valid, + * i.e. doesn't consist solely of blanks and dots */ -#undef isupper -#undef islower -#undef toupper -#undef tolower +int +unix2winfn(un, unlen, wep, cnt, chksum) + const u_char *un; + int unlen; + struct winentry *wep; + int cnt; + int chksum; +{ + const u_int8_t *cp; + u_int8_t *wcp; + int i; + + /* + * Drop trailing blanks and dots + */ + for (cp = un + unlen; *--cp == ' ' || *cp == '.'; unlen--); + + un += (cnt - 1) * WIN_CHARS; + unlen -= (cnt - 1) * WIN_CHARS; + + /* + * Initialize winentry to some useful default + */ + for (wcp = (u_int8_t *)wep, i = sizeof(*wep); --i >= 0; *wcp++ = 0xff); + wep->weCnt = cnt; + wep->weAttributes = ATTR_WIN95; + wep->weReserved1 = 0; + wep->weChksum = chksum; + wep->weReserved2 = 0; + + /* + * Now convert the filename parts + */ + for (wcp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0;) { + if (--unlen < 0) + goto done; + *wcp++ = *un++; + *wcp++ = 0; + } + for (wcp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) { + if (--unlen < 0) + goto done; + *wcp++ = *un++; + *wcp++ = 0; + } + for (wcp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) { + if (--unlen < 0) + goto done; + *wcp++ = *un++; + *wcp++ = 0; + } + if (!unlen) + wep->weCnt |= WIN_LAST; + return unlen; + +done: + *wcp++ = 0; + *wcp++ = 0; + wep->weCnt |= WIN_LAST; + return 0; +} + +/* + * Compare our filename to the one in the Win95 entry + * Returns the checksum or -1 if no match + */ +int +winChkName(un, unlen, wep, chksum) + const u_char *un; + int unlen; + struct winentry *wep; + int chksum; +{ + u_int8_t *cp; + int i; + + /* + * First compare checksums + */ + if (wep->weCnt&WIN_LAST) + chksum = wep->weChksum; + else if (chksum != wep->weChksum) + chksum = -1; + if (chksum == -1) + return -1; + + /* + * Offset of this entry + */ + i = ((wep->weCnt&WIN_CNT) - 1) * WIN_CHARS; + un += i; + if ((unlen -= i) <= 0) + return -1; + if ((wep->weCnt&WIN_LAST) && unlen > WIN_CHARS) + return -1; + + /* + * Compare the name parts + */ + for (cp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0;) { + if (--unlen < 0) { + if (!*cp++ && !*cp) + return chksum; + return -1; + } + if (u2l[*cp++] != u2l[*un++] || *cp++) + return -1; + } + for (cp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) { + if (--unlen < 0) { + if (!*cp++ && !*cp) + return chksum; + return -1; + } + if (u2l[*cp++] != u2l[*un++] || *cp++) + return -1; + } + for (cp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) { + if (--unlen < 0) { + if (!*cp++ && !*cp) + return chksum; + return -1; + } + if (u2l[*cp++] != u2l[*un++] || *cp++) + return -1; + } + return chksum; +} + +/* + * Convert Win95 filename to dirbuf. + * Returns the checksum or -1 if impossible + */ +int +win2unixfn(wep, dp, chksum) + struct winentry *wep; + struct dirent *dp; + int chksum; +{ + u_int8_t *cp; + u_int8_t *np, *ep = dp->d_name + WIN_MAXLEN; + int i; + + if ((wep->weCnt&WIN_CNT) > howmany(WIN_MAXLEN, WIN_CHARS) + || !(wep->weCnt&WIN_CNT)) + return -1; + + /* + * First compare checksums + */ + if (wep->weCnt&WIN_LAST) { + chksum = wep->weChksum; + /* + * This works even though d_namlen is one byte! + */ + dp->d_namlen = (wep->weCnt&WIN_CNT) * WIN_CHARS; + } else if (chksum != wep->weChksum) + chksum = -1; + if (chksum == -1) + return -1; + + /* + * Offset of this entry + */ + i = ((wep->weCnt&WIN_CNT) - 1) * WIN_CHARS; + np = (u_int8_t *)dp->d_name + i; + + /* + * Convert the name parts + */ + for (cp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0;) { + switch (*np++ = *cp++) { + case 0: + dp->d_namlen -= sizeof(wep->wePart2)/2 + + sizeof(wep->wePart3)/2 + i + 1; + return chksum; + case '/': + np[-1] = 0; + return -1; + } + /* + * The size comparison should result in the compiler + * optimizing the whole if away + */ + if (WIN_MAXLEN % WIN_CHARS < sizeof(wep->wePart1) / 2 + && np > ep) { + np[-1] = 0; + return -1; + } + if (*cp++) + return -1; + } + for (cp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) { + switch (*np++ = *cp++) { + case 0: + dp->d_namlen -= sizeof(wep->wePart3)/2 + i + 1; + return chksum; + case '/': + np[-1] = 0; + return -1; + } + /* + * The size comparisons should be optimized away + */ + if (WIN_MAXLEN % WIN_CHARS >= sizeof(wep->wePart1) / 2 + && WIN_MAXLEN % WIN_CHARS < (sizeof(wep->wePart1) + sizeof(wep->wePart2)) / 2 + && np > ep) { + np[-1] = 0; + return -1; + } + if (*cp++) + return -1; + } + for (cp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) { + switch (*np++ = *cp++) { + case 0: + dp->d_namlen -= i + 1; + return chksum; + case '/': + np[-1] = 0; + return -1; + } + /* + * See above + */ + if (WIN_MAXLEN % WIN_CHARS >= (sizeof(wep->wePart1) + sizeof(wep->wePart2)) / 2 + && np > ep) { + np[-1] = 0; + return -1; + } + if (*cp++) + return -1; + } + return chksum; +} + +/* + * Compute the checksum of a DOS filename for Win95 use + */ +u_int8_t +winChksum(name) + u_int8_t *name; +{ + int i; + u_int8_t s; + + for (s = 0, i = 11; --i >= 0; s += *name++) + s = (s << 7)|(s >> 1); + return s; +} + +/* + * Determine the number of slots necessary for Win95 names + */ +int +winSlotCnt(un, unlen) + const u_char *un; + int unlen; +{ + for (un += unlen; unlen > 0; unlen--) + if (*--un != ' ' && *un != '.') + break; + if (unlen > WIN_MAXLEN) + return 0; + return howmany(unlen, WIN_CHARS); +} diff --git a/sys/fs/msdosfs/msdosfs_denode.c b/sys/fs/msdosfs/msdosfs_denode.c index 81e06992a6e9..6feabbb17697 100644 --- a/sys/fs/msdosfs/msdosfs_denode.c +++ b/sys/fs/msdosfs/msdosfs_denode.c @@ -1,9 +1,9 @@ -/* $Id: msdosfs_denode.c,v 1.30 1998/02/06 12:13:46 eivind Exp $ */ -/* $NetBSD: msdosfs_denode.c,v 1.9 1994/08/21 18:44:00 ws Exp $ */ +/* $Id: msdosfs_denode.c,v 1.31 1998/02/09 06:09:51 eivind Exp $ */ +/* $NetBSD: msdosfs_denode.c,v 1.28 1998/02/10 14:10:00 mrg Exp $ */ /*- - * Copyright (C) 1994 Wolfgang Solfrank. - * Copyright (C) 1994 TooLs GmbH. + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * @@ -69,8 +69,9 @@ static MALLOC_DEFINE(M_MSDOSFSNODE, "MSDOSFS node", "MSDOSFS vnode private part"); static struct denode **dehashtbl; -static u_long dehash; /* size of hash table - 1 */ -#define DEHASH(dev, deno) (dehashtbl[((dev) + (deno)) & dehash]) +static u_long dehash; /* size of hash table - 1 */ +#define DEHASH(dev, dcl, doff) (dehashtbl[((dev) + (dcl) + (doff) / \ + sizeof(struct direntry)) & dehash]) static struct simplelock dehash_slock; union _qcvt { @@ -96,12 +97,14 @@ static struct denode * static void msdosfs_hashins __P((struct denode *dep)); static void msdosfs_hashrem __P((struct denode *dep)); -int msdosfs_init(vfsp) +/*ARGSUSED*/ +int +msdosfs_init(vfsp) struct vfsconf *vfsp; { dehashtbl = hashinit(desiredvnodes/2, M_MSDOSFSMNT, &dehash); simple_lock_init(&dehash_slock); - return 0; + return (0); } static struct denode * @@ -116,7 +119,7 @@ msdosfs_hashget(dev, dirclust, diroff) loop: simple_lock(&dehash_slock); - for (dep = DEHASH(dev, dirclust + diroff); dep; dep = dep->de_next) { + for (dep = DEHASH(dev, dirclust, diroff); dep; dep = dep->de_next) { if (dirclust == dep->de_dirclust && diroff == dep->de_diroffset && dev == dep->de_dev @@ -140,7 +143,7 @@ msdosfs_hashins(dep) struct denode **depp, *deq; simple_lock(&dehash_slock); - depp = &DEHASH(dep->de_dev, dep->de_dirclust + dep->de_diroffset); + depp = &DEHASH(dep->de_dev, dep->de_dirclust, dep->de_diroffset); deq = *depp; if (deq) deq->de_prev = &dep->de_next; @@ -178,48 +181,41 @@ msdosfs_hashrem(dep) * 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) +deget(pmp, dirclust, diroffset, 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; + struct direntry *direntptr; struct denode *ldep; struct vnode *nvp; struct buf *bp; struct proc *p = curproc; /* XXX */ #ifdef MSDOSFS_DEBUG - printf("deget(pmp %p, dirclust %ld, diroffset %x, direntptr %p, depp %p)\n", - pmp, dirclust, diroffset, direntptr, depp); + printf("deget(pmp %p, dirclust %lu, diroffset %lx, depp %p)\n", + pmp, dirclust, diroffset, depp); #endif /* - * If dir entry is given and refers to a directory, convert to - * canonical form + * On FAT32 filesystems, root is a (more or less) normal + * directory */ - if (direntptr && (direntptr->deAttributes & ATTR_DIRECTORY)) { - dirclust = getushort(direntptr->deStartCluster); - if (dirclust == MSDOSFSROOT) - diroffset = MSDOSFSROOT_OFS; - else - diroffset = 0; - } + if (FAT32(pmp) && dirclust == MSDOSFSROOT) + dirclust = pmp->pm_rootdirblk; /* * 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 + * address of "." entry. For root dir (if not FAT32) 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. @@ -230,7 +226,7 @@ deget(pmp, dirclust, diroffset, direntptr, depp) ldep = msdosfs_hashget(dev, dirclust, diroffset); if (ldep) { *depp = ldep; - return 0; + return (0); } /* @@ -277,10 +273,15 @@ deget(pmp, dirclust, diroffset, direntptr, depp) */ msdosfs_hashins(ldep); + ldep->de_pmp = pmp; + ldep->de_devvp = pmp->pm_devvp; + ldep->de_refcnt = 1; /* * Copy the directory entry into the denode area of the vnode. */ - if (dirclust == MSDOSFSROOT && diroffset == MSDOSFSROOT_OFS) { + if ((dirclust == MSDOSFSROOT + || (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)) + && diroffset == MSDOSFSROOT_OFS) { /* * Directory entry for the root directory. There isn't one, * so we manufacture one. We should probably rummage @@ -288,39 +289,42 @@ deget(pmp, dirclust, diroffset, direntptr, depp) * exists), and then use the time and date from that entry * as the time and date for the root denode. */ + nvp->v_flag |= VROOT; /* should be further down XXX */ + ldep->de_Attributes = ATTR_DIRECTORY; - ldep->de_StartCluster = MSDOSFSROOT; - ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec; + if (FAT32(pmp)) + ldep->de_StartCluster = pmp->pm_rootdirblk; + /* de_FileSize will be filled in further down */ + else { + 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) + ldep->de_CHun = 0; + ldep->de_CTime = 0x0000; /* 00:00:00 */ + ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT) | (1 << DD_DAY_SHIFT); /* Jan 1, 1980 */ + ldep->de_ADate = ldep->de_CDate; + ldep->de_MTime = ldep->de_CTime; + ldep->de_MDate = ldep->de_CDate; /* leave the other fields as garbage */ } else { - bp = NULL; - if (!direntptr) { - error = readep(pmp, dirclust, diroffset, &bp, - &direntptr); - if (error) - return error; - } + error = readep(pmp, dirclust, diroffset, &bp, &direntptr); + if (error) + return (error); DE_INTERNALIZE(ldep, direntptr); - if (bp) - brelse(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 @@ -331,12 +335,10 @@ deget(pmp, dirclust, diroffset, direntptr, depp) u_long size; nvp->v_type = VDIR; - if (ldep->de_StartCluster == MSDOSFSROOT) - nvp->v_flag |= VROOT; - else { - error = pcbmap(ldep, 0xffff, 0, &size); + if (ldep->de_StartCluster != MSDOSFSROOT) { + error = pcbmap(ldep, 0xffff, 0, &size, 0); if (error == E2BIG) { - ldep->de_FileSize = size << pmp->pm_cnshift; + ldep->de_FileSize = de_cn2off(pmp, size); error = 0; } else printf("deget(): pcbmap returned %d\n", error); @@ -347,78 +349,40 @@ deget(pmp, dirclust, diroffset, direntptr, depp) SETLOW(ldep->de_modrev, mono_time.tv_usec * 4294); VREF(ldep->de_devvp); *depp = ldep; - return 0; + return (0); } int -deupdat(dep, tp, waitfor) +deupdat(dep, waitfor) struct denode *dep; - struct timespec *tp; int waitfor; { int error; struct buf *bp; struct direntry *dirp; - struct vnode *vp = DETOV(dep); + struct timespec ts; -#ifdef MSDOSFS_DEBUG - printf("deupdat(): dep %p\n", dep); -#endif - - /* - * If the denode-modified and update-mtime bits are 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_MODIFIED | 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 (DETOV(dep)->v_mount->mnt_flag & MNT_RDONLY) + return (0); + TIMEVAL_TO_TIMESPEC(&time, &ts); + DETIMES(dep, &ts, &ts, &ts); + if ((dep->de_flag & DE_MODIFIED) == 0) + return (0); + dep->de_flag &= ~DE_MODIFIED; + if (dep->de_Attributes & ATTR_DIRECTORY) + return (0); + if (dep->de_refcnt <= 0) + return (0); error = readde(dep, &bp, &dirp); if (error) - return error; - - /* - * If the mtime is to be updated, put the passed in time into the - * directory entry. - */ - if (dep->de_flag & DE_UPDATE) { - dep->de_Attributes |= ATTR_ARCHIVE; - unix2dostime(tp, &dep->de_Date, &dep->de_Time); - } - - /* - * The mtime is now up to date. The denode will be unmodifed soon. - */ - dep->de_flag &= ~(DE_MODIFIED | DE_UPDATE); - - /* - * Copy the directory entry out of the denode into the cluster it - * came from. - */ + return (error); 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 + return (bwrite(bp)); + else { bdwrite(bp); - return error; + return (0); + } } /* @@ -445,7 +409,7 @@ detrunc(dep, length, flags, cred, p) struct timespec ts; #ifdef MSDOSFS_DEBUG - printf("detrunc(): file %s, length %d, flags %d\n", dep->de_Name, length, flags); + printf("detrunc(): file %s, length %lu, flags %x\n", dep->de_Name, length, flags); #endif /* @@ -456,11 +420,10 @@ detrunc(dep, length, flags, cred, p) * 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 %ld, offset %ld\n", + if ((DETOV(dep)->v_flag & VROOT) && !FAT32(pmp)) { + printf("detrunc(): can't truncate root directory, clust %ld, offset %ld\n", dep->de_dirclust, dep->de_diroffset); - return EINVAL; + return (EINVAL); } @@ -483,16 +446,17 @@ detrunc(dep, length, flags, cred, p) dep->de_StartCluster = 0; eofentry = ~0; } else { - error = pcbmap(dep, de_clcount(pmp, length) - 1, 0, &eofentry); + error = pcbmap(dep, de_clcount(pmp, length) - 1, 0, + &eofentry, 0); if (error) { #ifdef MSDOSFS_DEBUG printf("detrunc(): pcbmap fails %d\n", error); #endif - return error; + return (error); } } - fc_purge(dep, (length + pmp->pm_crbomask) >> pmp->pm_cnshift); + fc_purge(dep, de_clcount(pmp, length)); /* * If the new length is not a multiple of the cluster size then we @@ -500,10 +464,6 @@ detrunc(dep, length, flags, cred, p) * 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, @@ -521,10 +481,11 @@ detrunc(dep, length, flags, cred, p) NOCRED, &bp); } if (error) { + brelse(bp); #ifdef MSDOSFS_DEBUG printf("detrunc(): bread fails %d\n", error); #endif - return error; + return (error); } /* * is this the right place for it? @@ -541,14 +502,14 @@ detrunc(dep, length, flags, cred, p) * we free the trailing clusters. */ dep->de_FileSize = length; - dep->de_flag |= DE_UPDATE; + if (!isadir) + dep->de_flag |= DE_UPDATE|DE_MODIFIED; vflags = (length > 0 ? V_SAVE : 0) | V_SAVEMETA; vinvalbuf(DETOV(dep), vflags, cred, p, 0, 0); vnode_pager_setsize(DETOV(dep), length); - TIMEVAL_TO_TIMESPEC(&time, &ts); - allerror = deupdat(dep, &ts, 1); + allerror = deupdat(dep, 1); #ifdef MSDOSFS_DEBUG - printf("detrunc(): allerror %d, eofentry %d\n", + printf("detrunc(): allerror %d, eofentry %lu\n", allerror, eofentry); #endif @@ -563,9 +524,9 @@ detrunc(dep, length, flags, cred, p) #ifdef MSDOSFS_DEBUG printf("detrunc(): fatentry errors %d\n", error); #endif - return error; + return (error); } - fc_setcache(dep, FC_LASTFC, (length - 1) >> pmp->pm_cnshift, + fc_setcache(dep, FC_LASTFC, de_cluster(pmp, length - 1), eofentry); } @@ -573,10 +534,10 @@ detrunc(dep, length, flags, cred, p) * Now free the clusters removed from the file because of the * truncation. */ - if (chaintofree != 0 && !MSDOSFSEOF(chaintofree)) + if (chaintofree != 0 && !MSDOSFSEOF(pmp, chaintofree)) freeclusterchain(pmp, chaintofree); - return allerror; + return (allerror); } /* @@ -585,7 +546,7 @@ detrunc(dep, length, flags, cred, p) int deextend(dep, length, cred) struct denode *dep; - off_t length; + u_long length; struct ucred *cred; { struct msdosfsmount *pmp = dep->de_pmp; @@ -596,18 +557,14 @@ deextend(dep, length, cred) /* * The root of a DOS filesystem cannot be extended. */ - if (DETOV(dep)->v_flag & VROOT) - return EINVAL; + if ((DETOV(dep)->v_flag & VROOT) && !FAT32(pmp)) + return (EINVAL); /* - * Directories can only be extended by the superuser. - * Is this really important? + * Directories cannot be extended. */ - if (dep->de_Attributes & ATTR_DIRECTORY) { - error = suser(cred, NULL); - if (error) - return error; - } + if (dep->de_Attributes & ATTR_DIRECTORY) + return (EISDIR); if (length <= dep->de_FileSize) panic("deextend: file too large"); @@ -618,26 +575,25 @@ deextend(dep, length, cred) count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize); if (count > 0) { if (count > pmp->pm_freeclustercount) - return ENOSPC; + return (ENOSPC); error = extendfile(dep, count, NULL, NULL, DE_CLEAR); if (error) { /* truncate the added clusters away again */ (void) detrunc(dep, dep->de_FileSize, 0, cred, NULL); - return error; + return (error); } } - - dep->de_flag |= DE_UPDATE; dep->de_FileSize = length; - TIMEVAL_TO_TIMESPEC(&time, &ts); - return deupdat(dep, &ts, 1); + dep->de_flag |= DE_UPDATE|DE_MODIFIED; + return (deupdat(dep, 1)); } /* * Move a denode to its correct hash queue after the file it represents has * been moved to a new directory. */ -int reinsert(dep) +void +reinsert(dep) struct denode *dep; { /* @@ -648,11 +604,10 @@ int reinsert(dep) * 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; + if (dep->de_Attributes & ATTR_DIRECTORY) + return; + msdosfs_hashrem(dep); + msdosfs_hashins(dep); } int @@ -671,27 +626,25 @@ msdosfs_reclaim(ap) if (prtactive && vp->v_usecount != 0) vprint("msdosfs_reclaim(): pushing active", vp); - /* - * Remove the denode from the denode hash chain we are in. + * Remove the denode from its hash chain. */ msdosfs_hashrem(dep); - - cache_purge(vp); /* - * Indicate that one less file on the filesystem is open. + * Purge old data structures associated with the denode. */ + cache_purge(vp); if (dep->de_devvp) { vrele(dep->de_devvp); dep->de_devvp = 0; } - +#if 0 /* XXX */ dep->de_flag = 0; - +#endif FREE(dep, M_MSDOSFSNODE); vp->v_data = NULL; - return 0; + return (0); } int @@ -715,7 +668,7 @@ msdosfs_inactive(ap) vprint("msdosfs_inactive(): pushing active", vp); /* - * Ignore inodes related to stale file handles. + * Ignore denodes related to stale file handles. */ if (dep->de_Name[0] == SLOT_DELETED) goto out; @@ -734,17 +687,13 @@ msdosfs_inactive(ap) dep->de_flag |= DE_UPDATE; dep->de_Name[0] = SLOT_DELETED; } - if (dep->de_flag & (DE_MODIFIED | DE_UPDATE)) { - TIMEVAL_TO_TIMESPEC(&time, &ts); - deupdat(dep, &ts, 0); - } + deupdat(dep, 0); + out: VOP_UNLOCK(vp, 0, p); - dep->de_flag = 0; - /* - * If we are done with the denode, then reclaim it so that it can - * be reused now. + * If we are done with the denode, reclaim it + * so that it can be reused immediately. */ #ifdef MSDOSFS_DEBUG printf("msdosfs_inactive(): v_usecount %d, de_Name[0] %x\n", vp->v_usecount, @@ -752,5 +701,5 @@ out: #endif if (dep->de_Name[0] == SLOT_DELETED) vrecycle(vp, (struct simplelock *)0, p); - return error; + return (error); } diff --git a/sys/fs/msdosfs/msdosfs_fat.c b/sys/fs/msdosfs/msdosfs_fat.c index bb25ec22779a..f5568605d74d 100644 --- a/sys/fs/msdosfs/msdosfs_fat.c +++ b/sys/fs/msdosfs/msdosfs_fat.c @@ -1,9 +1,9 @@ -/* $Id: msdosfs_fat.c,v 1.15 1998/02/06 12:13:46 eivind Exp $ */ -/* $NetBSD: msdosfs_fat.c,v 1.12 1994/08/21 18:44:04 ws Exp $ */ +/* $Id: msdosfs_fat.c,v 1.16 1998/02/09 06:09:52 eivind Exp $ */ +/* $NetBSD: msdosfs_fat.c,v 1.28 1997/11/17 15:36:49 ws Exp $ */ /*- - * Copyright (C) 1994 Wolfgang Solfrank. - * Copyright (C) 1994 TooLs GmbH. + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * @@ -56,6 +56,7 @@ #include #include /* to define statfs structure */ #include /* to define vattr structure */ +#include /* * msdosfs include files. @@ -79,9 +80,6 @@ static int fc_lmdistance[LMMAX];/* counters for how far off the last * cluster mapped entry was. */ static 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) - static int chainalloc __P((struct msdosfsmount *pmp, u_long start, u_long count, u_long fillwith, u_long *retcluster, u_long *got)); @@ -111,7 +109,8 @@ fatblock(pmp, ofs, bnp, sizep, bop) bn = ofs / pmp->pm_fatblocksize * pmp->pm_fatblocksec; size = min(pmp->pm_fatblocksec, pmp->pm_FATsecs - bn) * pmp->pm_BytesPerSec; - bn += pmp->pm_fatblk; + bn += pmp->pm_fatblk + pmp->pm_curfat * pmp->pm_FATsecs; + if (bnp) *bnp = bn; if (sizep) @@ -139,16 +138,17 @@ fatblock(pmp, ofs, bnp, sizep, bop) * If cnp is null, nothing is returned. */ int -pcbmap(dep, findcn, bnp, cnp) +pcbmap(dep, findcn, bnp, cnp, sp) 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 *sp; /* returned block size */ { int error; u_long i; u_long cn; - u_long prevcn; + u_long prevcn = 0; /* XXX: prevcn could be used unititialized */ u_long byteoffset; u_long bn; u_long bo; @@ -156,7 +156,6 @@ pcbmap(dep, findcn, bnp, cnp) u_long bp_bn = -1; struct msdosfsmount *pmp = dep->de_pmp; u_long bsize; - int fat12 = FAT12(pmp); /* 12 bit fat */ fc_bmapcalls++; @@ -164,8 +163,8 @@ pcbmap(dep, findcn, bnp, cnp) * If they don't give us someplace to return a value then don't * bother doing anything. */ - if (bnp == NULL && cnp == NULL) - return 0; + if (bnp == NULL && cnp == NULL && sp == NULL) + return (0); cn = dep->de_StartCluster; /* @@ -176,23 +175,32 @@ pcbmap(dep, findcn, bnp, cnp) */ if (cn == MSDOSFSROOT) { if (dep->de_Attributes & ATTR_DIRECTORY) { - if (findcn * pmp->pm_SectPerClust >= pmp->pm_rootdirsize) { + if (de_cn2off(pmp, findcn) >= dep->de_FileSize) { if (cnp) - *cnp = pmp->pm_rootdirsize / pmp->pm_SectPerClust; - return E2BIG; + *cnp = de_bn2cn(pmp, pmp->pm_rootdirsize); + return (E2BIG); } if (bnp) - *bnp = pmp->pm_rootdirblk + (findcn * pmp->pm_SectPerClust); + *bnp = pmp->pm_rootdirblk + de_cn2bn(pmp, findcn); if (cnp) *cnp = MSDOSFSROOT; - return 0; + if (sp) + *sp = min(pmp->pm_bpcluster, + dep->de_FileSize - de_cn2off(pmp, findcn)); + return (0); } else { /* just an empty file */ if (cnp) *cnp = 0; - return E2BIG; + return (E2BIG); } } + /* + * All other files do I/O in cluster sized blocks + */ + if (sp) + *sp = pmp->pm_bpcluster; + /* * Rummage around in the fat cache, maybe we can avoid tromping * thru every fat entry for the file. And, keep track of how far @@ -208,9 +216,11 @@ pcbmap(dep, findcn, bnp, cnp) /* * Handle all other files or directories the normal way. */ - prevcn = 0; for (; i < findcn; i++) { - if (MSDOSFSEOF(cn)) + /* + * Stop with all reserved clusters, not just with EOF. + */ + if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD) goto hiteof; byteoffset = FATOFS(pmp, cn); fatblock(pmp, byteoffset, &bn, &bsize, &bo); @@ -218,28 +228,32 @@ pcbmap(dep, findcn, bnp, cnp) if (bp) brelse(bp); error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); - if (error) - return error; + if (error) { + brelse(bp); + 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 (FAT32(pmp)) + cn = getulong(&bp->b_data[bo]); + else + cn = getushort(&bp->b_data[bo]); + if (FAT12(pmp) && (prevcn & 1)) + cn >>= 4; + cn &= pmp->pm_fatmask; + + /* + * Force the special cluster numbers + * to be the same for all cluster sizes + * to let the rest of msdosfs handle + * all cases the same. + */ + if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD) + cn |= ~pmp->pm_fatmask; } - if (!MSDOSFSEOF(cn)) { + if (!MSDOSFSEOF(pmp, cn)) { if (bp) brelse(bp); if (bnp) @@ -247,7 +261,7 @@ pcbmap(dep, findcn, bnp, cnp) if (cnp) *cnp = cn; fc_setcache(dep, FC_LASTMAP, i, cn); - return 0; + return (0); } hiteof:; @@ -257,7 +271,7 @@ hiteof:; brelse(bp); /* update last file cluster entry in the fat cache */ fc_setcache(dep, FC_LASTFC, i - 1, prevcn); - return E2BIG; + return (E2BIG); } /* @@ -292,7 +306,8 @@ fc_lookup(dep, findcn, frcnp, fsrcnp) * Purge the fat cache in denode dep of all entries relating to file * relative cluster frcn and beyond. */ -void fc_purge(dep, frcn) +void +fc_purge(dep, frcn) struct denode *dep; u_int frcn; { @@ -307,7 +322,9 @@ void fc_purge(dep, frcn) } /* - * Update all copies of the fat. The first copy is updated last. + * Update the fat. + * If mirroring the fat, update all copies, with the first copy as last. + * Else update only the current fat (ignoring the others). * * pmp - msdosfsmount structure for filesystem to update * bp - addr of modified fat block @@ -323,36 +340,80 @@ updatefats(pmp, bp, fatbn) struct buf *bpn; #ifdef MSDOSFS_DEBUG - printf("updatefats(pmp %p, bp %p, fatbn %ld)\n", pmp, bp, fatbn); + printf("updatefats(pmp %p, bp %p, fatbn %lu)\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. + * If we have an FSInfo block, update it. */ - 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); + if (pmp->pm_fsinfo) { + u_long cn = pmp->pm_nxtfree; + + if (pmp->pm_freeclustercount + && (pmp->pm_inusemap[cn / N_INUSEBITS] + & (1 << (cn % N_INUSEBITS)))) { + /* + * The cluster indicated in FSInfo isn't free + * any longer. Got get a new free one. + */ + for (cn = 0; cn < pmp->pm_maxcluster;) + if (pmp->pm_inusemap[cn / N_INUSEBITS] != (u_int)-1) + break; + pmp->pm_nxtfree = cn + + ffs(pmp->pm_inusemap[cn / N_INUSEBITS] + ^ (u_int)-1) - 1; + } + if (bread(pmp->pm_devvp, pmp->pm_fsinfo, 1024, NOCRED, &bpn) != 0) { + /* + * Ignore the error, but turn off FSInfo update for the future. + */ + pmp->pm_fsinfo = 0; + brelse(bpn); + } else { + struct fsinfo *fp = (struct fsinfo *)bpn->b_data; + + putulong(fp->fsinfree, pmp->pm_freeclustercount); + putulong(fp->fsinxtfree, pmp->pm_nxtfree); + if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT) + bwrite(bpn); + else + bdwrite(bpn); + } } + + if (pmp->pm_flags & MSDOSFS_FATMIRROR) { + /* + * 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_flags & MSDOSFSMNT_WAITONFAT) + bwrite(bpn); + else + bdwrite(bpn); + } + } + /* - * Write out the first fat last. + * Write out the first (or current) fat last. */ - if (pmp->pm_waitonfat) + if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT) bwrite(bp); else bdwrite(bp); + /* + * Maybe update fsinfo sector here? + */ } /* @@ -379,8 +440,8 @@ usemap_alloc(pmp, cn) struct msdosfsmount *pmp; u_long cn; { - pmp->pm_inusemap[cn / N_INUSEBITS] - |= 1 << (cn % N_INUSEBITS); + + pmp->pm_inusemap[cn / N_INUSEBITS] |= 1 << (cn % N_INUSEBITS); pmp->pm_freeclustercount--; } @@ -389,6 +450,7 @@ usemap_free(pmp, cn) struct msdosfsmount *pmp; u_long cn; { + pmp->pm_freeclustercount++; pmp->pm_inusemap[cn / N_INUSEBITS] &= ~(1 << (cn % N_INUSEBITS)); } @@ -402,18 +464,20 @@ clusterfree(pmp, cluster, oldcnp) int error; u_long oldcn; + usemap_free(pmp, cluster); 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; + if (error) { + usemap_alloc(pmp, cluster); + return (error); } - return error; + /* + * 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. + */ + if (oldcnp) + *oldcnp = oldcn; + return (0); } /* @@ -448,10 +512,10 @@ fatentry(function, pmp, cn, oldcontents, newcontents) 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 MSDOSFS_DEBUG + printf("fatentry(func %d, pmp %p, clust %lu, oldcon %p, newcon %lx)\n", + function, pmp, cn, oldcontents, newcontents); +#endif #ifdef DIAGNOSTIC /* @@ -459,7 +523,7 @@ fatentry(function, pmp, cn, oldcontents, newcontents) */ if ((function & (FAT_SET | FAT_GET)) == 0) { printf("fatentry(): function code doesn't specify get or set\n"); - return EINVAL; + return (EINVAL); } /* @@ -468,7 +532,7 @@ fatentry(function, pmp, cn, oldcontents, newcontents) */ if ((function & FAT_GET) && oldcontents == NULL) { printf("fatentry(): get function with no place to put result\n"); - return EINVAL; + return (EINVAL); } #endif @@ -476,28 +540,32 @@ fatentry(function, pmp, cn, oldcontents, newcontents) * Be sure the requested cluster is in the filesystem. */ if (cn < CLUST_FIRST || cn > pmp->pm_maxcluster) - return EINVAL; + return (EINVAL); byteoffset = FATOFS(pmp, cn); fatblock(pmp, byteoffset, &bn, &bsize, &bo); error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); - if (error) - return error; + if (error) { + brelse(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; - } + if (FAT32(pmp)) + readcn = getulong(&bp->b_data[bo]); + else + readcn = getushort(&bp->b_data[bo]); + if (FAT12(pmp) & (cn & 1)) + readcn >>= 4; + readcn &= pmp->pm_fatmask; + /* map reserved fat entries to same values for all fats */ + if ((readcn | ~pmp->pm_fatmask) >= CLUST_RSRVD) + readcn |= ~pmp->pm_fatmask; *oldcontents = readcn; } if (function & FAT_SET) { - if (FAT12(pmp)) { + switch (pmp->pm_fatmask) { + case FAT12_MASK: readcn = getushort(&bp->b_data[bo]); if (cn & 1) { readcn &= 0x000f; @@ -507,15 +575,28 @@ fatentry(function, pmp, cn, oldcontents, newcontents) readcn |= newcontents & 0xfff; } putushort(&bp->b_data[bo], readcn); - } else + break; + case FAT16_MASK: putushort(&bp->b_data[bo], newcontents); + break; + case FAT32_MASK: + /* + * According to spec we have to retain the + * high order bits of the fat entry. + */ + readcn = getulong(&bp->b_data[bo]); + readcn &= ~FAT32_MASK; + readcn |= newcontents & FAT32_MASK; + putulong(&bp->b_data[bo], readcn); + break; + } updatefats(pmp, bp, bn); bp = NULL; pmp->pm_fmod = 1; } if (bp) brelse(bp); - return 0; + return (0); } /* @@ -538,25 +619,28 @@ fatchain(pmp, start, count, fillwith) struct buf *bp; #ifdef MSDOSFS_DEBUG - printf("fatchain(pmp %p, start %ld, count %ld, fillwith %ld)\n", - pmp, start, count, fillwith); + printf("fatchain(pmp %p, start %lu, count %lu, fillwith %lx)\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; + return (EINVAL); while (count > 0) { byteoffset = FATOFS(pmp, start); fatblock(pmp, byteoffset, &bn, &bsize, &bo); error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); - if (error) - return error; + if (error) { + brelse(bp); + return (error); + } while (count > 0) { start++; newc = --count > 0 ? start : fillwith; - if (FAT12(pmp)) { + switch (pmp->pm_fatmask) { + case FAT12_MASK: readcn = getushort(&bp->b_data[bo]); if (start & 1) { readcn &= 0xf000; @@ -569,9 +653,18 @@ fatchain(pmp, start, count, fillwith) bo++; if (!(start & 1)) bo++; - } else { + break; + case FAT16_MASK: putushort(&bp->b_data[bo], newc); bo += 2; + break; + case FAT32_MASK: + readcn = getulong(&bp->b_data[bo]); + readcn &= ~pmp->pm_fatmask; + readcn |= newc & pmp->pm_fatmask; + putulong(&bp->b_data[bo], readcn); + bo += 4; + break; } if (bo >= bsize) break; @@ -579,7 +672,7 @@ fatchain(pmp, start, count, fillwith) updatefats(pmp, bp, bn); } pmp->pm_fmod = 1; - return 0; + return (0); } /* @@ -606,11 +699,11 @@ chainlength(pmp, start, count) map &= ~((1 << start) - 1); if (map) { len = ffs(map) - 1 - start; - return len > count ? count : len; + return (len > count ? count : len); } len = N_INUSEBITS - start; if (len >= count) - return count; + return (count); while (++idx <= max_idx) { if (len >= count) break; @@ -621,7 +714,7 @@ chainlength(pmp, start, count) } len += N_INUSEBITS; } - return len > count ? count : len; + return (len > count ? count : len); } /* @@ -645,21 +738,23 @@ chainalloc(pmp, start, count, fillwith, retcluster, got) u_long *got; { int error; + u_long cl, n; + + for (cl = start, n = count; n-- > 0;) + usemap_alloc(pmp, cl++); error = fatchain(pmp, start, count, fillwith); - if (error == 0) { + if (error != 0) + return (error); #ifdef MSDOSFS_DEBUG - printf("clusteralloc(): allocated cluster chain at %ld (%ld clusters)\n", - start, count); + printf("clusteralloc(): allocated cluster chain at %lu (%lu clusters)\n", + start, count); #endif - if (retcluster) - *retcluster = start; - if (got) - *got = count; - while (count-- > 0) - usemap_alloc(pmp, start++); - } - return error; + if (retcluster) + *retcluster = start; + if (got) + *got = count; + return (0); } /* @@ -683,15 +778,16 @@ clusteralloc(pmp, start, count, fillwith, retcluster, got) u_long *got; { u_long idx; - u_long len, newst, foundcn, foundl, cn, l; + u_long len, newst, foundl, cn, l; + u_long foundcn = 0; /* XXX: foundcn could be used unititialized */ u_int map; #ifdef MSDOSFS_DEBUG - printf("clusteralloc(): find %d clusters\n",count); + printf("clusteralloc(): find %lu clusters\n",count); #endif if (start) { if ((len = chainlength(pmp, start, count)) >= count) - return chainalloc(pmp, start, count, fillwith, retcluster, got); + return (chainalloc(pmp, start, count, fillwith, retcluster, got)); } else { /* * This is a new file, initialize start @@ -699,7 +795,7 @@ clusteralloc(pmp, start, count, fillwith, retcluster, got) struct timeval tv; microtime(&tv); - start = (tv.tv_usec >> 10)|tv.tv_usec; + start = (tv.tv_usec >> 10) | tv.tv_usec; len = 0; } @@ -707,7 +803,7 @@ clusteralloc(pmp, start, count, fillwith, retcluster, got) * Start at a (pseudo) random place to maximize cluster runs * under multiple writers. */ - foundcn = newst = (start * 1103515245 + 12345) % (pmp->pm_maxcluster + 1); + newst = (start * 1103515245 + 12345) % (pmp->pm_maxcluster + 1); foundl = 0; for (cn = newst; cn <= pmp->pm_maxcluster;) { @@ -717,7 +813,7 @@ clusteralloc(pmp, start, count, fillwith, retcluster, got) 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); + return (chainalloc(pmp, cn, count, fillwith, retcluster, got)); if (l > foundl) { foundcn = cn; foundl = l; @@ -734,7 +830,7 @@ clusteralloc(pmp, start, count, fillwith, retcluster, got) 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); + return (chainalloc(pmp, cn, count, fillwith, retcluster, got)); if (l > foundl) { foundcn = cn; foundl = l; @@ -746,12 +842,12 @@ clusteralloc(pmp, start, count, fillwith, retcluster, got) } if (!foundl) - return ENOSPC; + return (ENOSPC); if (len) - return chainalloc(pmp, start, len, fillwith, retcluster, got); + return (chainalloc(pmp, start, len, fillwith, retcluster, got)); else - return chainalloc(pmp, foundcn, foundl, fillwith, retcluster, got); + return (chainalloc(pmp, foundcn, foundl, fillwith, retcluster, got)); } @@ -768,7 +864,7 @@ freeclusterchain(pmp, cluster) struct msdosfsmount *pmp; u_long cluster; { - int error = 0; + int error; struct buf *bp = NULL; u_long bn, bo, bsize, byteoffset; u_long readcn, lbn = -1; @@ -780,13 +876,16 @@ freeclusterchain(pmp, cluster) if (bp) updatefats(pmp, bp, lbn); error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); - if (error) - return error; + if (error) { + brelse(bp); + return (error); + } lbn = bn; } usemap_free(pmp, cluster); - readcn = getushort(&bp->b_data[bo]); - if (FAT12(pmp)) { + switch (pmp->pm_fatmask) { + case FAT12_MASK: + readcn = getushort(&bp->b_data[bo]); if (cluster & 1) { cluster = readcn >> 4; readcn &= 0x000f; @@ -797,17 +896,24 @@ freeclusterchain(pmp, cluster) readcn |= MSDOSFSFREE & 0xfff; } putushort(&bp->b_data[bo], readcn); - cluster &= 0x0fff; - if ((cluster&0x0ff0) == 0x0ff0) - cluster |= 0xf000; - } else { - cluster = readcn; + break; + case FAT16_MASK: + cluster = getushort(&bp->b_data[bo]); putushort(&bp->b_data[bo], MSDOSFSFREE); + break; + case FAT32_MASK: + cluster = getulong(&bp->b_data[bo]); + putulong(&bp->b_data[bo], + (MSDOSFSFREE & FAT32_MASK) | (cluster & ~FAT32_MASK)); + break; } + cluster &= pmp->pm_fatmask; + if ((cluster | ~pmp->pm_fatmask) >= CLUST_RSRVD) + cluster |= pmp->pm_fatmask; } if (bp) updatefats(pmp, bp, bn); - return error; + return (0); } /* @@ -821,7 +927,6 @@ fillinusemap(pmp) struct buf *bp = NULL; u_long cn, readcn; int error; - int fat12 = FAT12(pmp); u_long bn, bo, bsize, byteoffset; /* @@ -846,21 +951,24 @@ fillinusemap(pmp) 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 (error) { + brelse(bp); + return (error); + } } + if (FAT32(pmp)) + readcn = getulong(&bp->b_data[bo]); + else + readcn = getushort(&bp->b_data[bo]); + if (FAT12(pmp) && (cn & 1)) + readcn >>= 4; + readcn &= pmp->pm_fatmask; if (readcn == 0) usemap_free(pmp, cn); } brelse(bp); - return 0; + return (0); } /* @@ -886,7 +994,7 @@ extendfile(dep, count, bpp, ncp, flags) u_long *ncp; int flags; { - int error = 0; + int error; u_long frcn; u_long cn, got; struct msdosfsmount *pmp = dep->de_pmp; @@ -895,9 +1003,10 @@ extendfile(dep, count, bpp, ncp, flags) /* * Don't try to extend the root directory */ - if (DETOV(dep)->v_flag & VROOT) { + if (dep->de_StartCluster == MSDOSFSROOT + && (dep->de_Attributes & ATTR_DIRECTORY)) { printf("extendfile(): attempt to extend root directory\n"); - return ENOSPC; + return (ENOSPC); } /* @@ -908,21 +1017,21 @@ extendfile(dep, count, bpp, ncp, flags) if (dep->de_fc[FC_LASTFC].fc_frcn == FCE_EMPTY && dep->de_StartCluster != 0) { fc_lfcempty++; - error = pcbmap(dep, 0xffff, 0, &cn); + error = pcbmap(dep, 0xffff, 0, &cn, 0); /* we expect it to return E2BIG */ if (error != E2BIG) - return error; - error = 0; + return (error); } 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). + * 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; @@ -930,7 +1039,7 @@ extendfile(dep, count, bpp, ncp, flags) cn = dep->de_fc[FC_LASTFC].fc_fsrcn + 1; error = clusteralloc(pmp, cn, count, CLUST_EOFE, &cn, &got); if (error) - return error; + return (error); count -= got; @@ -947,13 +1056,13 @@ extendfile(dep, count, bpp, ncp, flags) dep->de_StartCluster = cn; frcn = 0; } else { - error = fatentry(FAT_SET, pmp, dep->de_fc[FC_LASTFC].fc_fsrcn, + error = fatentry(FAT_SET, pmp, + dep->de_fc[FC_LASTFC].fc_fsrcn, 0, cn); if (error) { clusterfree(pmp, cn, NULL); - return error; + return (error); } - frcn = dep->de_fc[FC_LASTFC].fc_frcn + 1; } @@ -972,11 +1081,14 @@ extendfile(dep, count, bpp, ncp, flags) bp = getblk(pmp->pm_devvp, cntobn(pmp, cn++), pmp->pm_bpcluster, 0, 0); else { - bp = getblk(DETOV(dep), frcn++, pmp->pm_bpcluster, 0, 0); + bp = getblk(DETOV(dep), de_cn2bn(pmp, frcn++), + pmp->pm_bpcluster, 0, 0); /* * Do the bmap now, as in msdosfs_write */ - if (pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0)) + if (pcbmap(dep, + de_bn2cn(pmp, bp->b_lblkno), + &bp->b_blkno, 0, 0)) bp->b_blkno = -1; if (bp->b_blkno == -1) panic("extendfile: pcbmap"); @@ -985,13 +1097,11 @@ extendfile(dep, count, bpp, ncp, flags) if (bpp) { *bpp = bp; bpp = NULL; - } else { - bp->b_flags |= B_AGE; - bawrite(bp); - } + } else + bdwrite(bp); } } } - return 0; + return (0); } diff --git a/sys/fs/msdosfs/msdosfs_lookup.c b/sys/fs/msdosfs/msdosfs_lookup.c index b757cf059982..49914179a990 100644 --- a/sys/fs/msdosfs/msdosfs_lookup.c +++ b/sys/fs/msdosfs/msdosfs_lookup.c @@ -1,9 +1,9 @@ -/* $Id: msdosfs_lookup.c,v 1.13 1997/09/02 20:06:17 bde Exp $ */ -/* $NetBSD: msdosfs_lookup.c,v 1.14 1994/08/21 18:44:07 ws Exp $ */ +/* $Id: msdosfs_lookup.c,v 1.14 1997/09/10 19:44:36 phk Exp $ */ +/* $NetBSD: msdosfs_lookup.c,v 1.37 1997/11/17 15:36:54 ws Exp $ */ /*- - * Copyright (C) 1994 Wolfgang Solfrank. - * Copyright (C) 1994 TooLs GmbH. + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * @@ -94,16 +94,13 @@ msdosfs_lookup(ap) int error; int lockparent; int wantparent; - int slotstatus; - -#define NONE 0 -#define FOUND 1 - int slotoffset = -1; - int slotcluster = -1; + int slotcount; + int slotoffset = 0; int frcn; u_long cluster; - int rootreloff; + int blkoff; int diroff; + int blsize; int isadir; /* ~0 if found direntry is a directory */ u_long scn; /* starting cluster number */ struct vnode *pdp; @@ -118,6 +115,10 @@ msdosfs_lookup(ap) int nameiop = cnp->cn_nameiop; struct proc *p = cnp->cn_proc; + int wincnt = 1; + int chksum = -1; + int olddos = 1; + #ifdef MSDOSFS_DEBUG printf("msdosfs_lookup(): looking for %s\n", cnp->cn_nameptr); #endif @@ -127,8 +128,8 @@ msdosfs_lookup(ap) 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); + printf("msdosfs_lookup(): vdp %p, dp %p, Attr %02x\n", + vdp, dp, dp->de_Attributes); #endif /* @@ -145,25 +146,43 @@ msdosfs_lookup(ap) printf("msdosfs_lookup(): looking for . or .. in root directory\n"); #endif cluster = MSDOSFSROOT; - diroff = MSDOSFSROOT_OFS; + blkoff = 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; + switch (unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename, + cnp->cn_namelen, 0)) { + case 0: + return (EINVAL); + case 1: + break; + case 2: + wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, + cnp->cn_namelen) + 1; + break; + case 3: + olddos = 0; + wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, + cnp->cn_namelen) + 1; + break; } + if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) + wincnt = 1; + + /* + * Suppress search for slots unless creating + * file and at end of pathname, in which case + * we watch for a place to put the new file in + * case it doesn't already exist. + */ + slotcount = wincnt; + if ((nameiop == CREATE || nameiop == RENAME) && + (flags & ISLASTCN)) + slotcount = 0; - 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); + printf("msdosfs_lookup(): dos version of filename %s, length %ld\n", + dosfilename, cnp->cn_namelen); #endif /* * Search the directory pointed at by vdp for the name pointed at @@ -177,20 +196,23 @@ msdosfs_lookup(ap) * part of the pool of allocatable clusters. So, we treat it a * little differently. The root directory starts at "cluster" 0. */ - rootreloff = 0; + diroff = 0; for (frcn = 0;; frcn++) { - error = pcbmap(dp, frcn, &bn, &cluster); + error = pcbmap(dp, frcn, &bn, &cluster, &blsize); if (error) { if (error == E2BIG) break; - return error; + return (error); } - error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,NOCRED,&bp); - if (error) - return error; - for (diroff = 0; diroff < pmp->pm_depclust; diroff++) { - dep = (struct direntry *) bp->b_data + diroff; - + error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } + for (blkoff = 0; blkoff < blsize; + blkoff += sizeof(struct direntry), + diroff += sizeof(struct direntry)) { + dep = (struct direntry *)(bp->b_data + blkoff); /* * If the slot is empty and we are still looking * for an empty then remember this one. If the @@ -202,218 +224,313 @@ msdosfs_lookup(ap) */ 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; + /* + * Drop memory of previous long matches + */ + chksum = -1; + + if (slotcount < wincnt) { + slotcount++; + slotoffset = diroff; } if (dep->deName[0] == SLOT_EMPTY) { brelse(bp); goto notfound; } } else { + /* + * If there wasn't enough space for our winentries, + * forget about the empty space + */ + if (slotcount < wincnt) + slotcount = 0; + + /* + * Check for Win95 long filename entry + */ + if (dep->deAttributes == ATTR_WIN95) { + if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) + continue; + + chksum = winChkName((const u_char *)cnp->cn_nameptr, + cnp->cn_namelen, + (struct winentry *)dep, + chksum); + continue; + } + /* * 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; + if (dep->deAttributes & ATTR_VOLUME) { + chksum = -1; + continue; } + + /* + * Check for a checksum or name match + */ + if (chksum != winChksum(dep->deName) + && (!olddos || bcmp(dosfilename, dep->deName, 11))) { + chksum = -1; + continue; + } +#ifdef MSDOSFS_DEBUG + printf("msdosfs_lookup(): match blkoff %d, diroff %d\n", + blkoff, diroff); +#endif + /* + * Remember where this directory + * entry came from for whoever did + * this lookup. + */ + dp->de_fndoffset = diroff; + dp->de_fndcnt = 0; /* unused anyway */ + + goto found; } - rootreloff++; - } /* for (diroff = 0; .... */ + } /* for (blkoff = 0; .... */ /* * Release the buffer holding the directory cluster just * searched. */ brelse(bp); - } /* for (frcn = 0; ; frcn++) */ -notfound:; + } /* for (frcn = 0; ; frcn++) */ + +notfound: /* * We hold no disk buffers at this point. */ + /* + * Fixup the slot description to point to the place where + * we might put the new DOS direntry (putting the Win95 + * long name entries before that) + */ + if (!slotcount) { + slotcount = 1; + slotoffset = diroff; + } + if (wincnt > slotcount) + slotoffset += sizeof(struct direntry) * (wincnt - slotcount); + /* * 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); + printf("msdosfs_lookup(): op %d, refcnt %ld\n", + nameiop, dp->de_refcnt); + printf(" slotcount %d, slotoffset %d\n", + slotcount, slotoffset); #endif if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN) && dp->de_refcnt != 0) { - error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc); + /* + * Access for write is interpreted as allowing + * creation of files in the directory. + */ + error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_proc); if (error) - return error; - 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 */ + return (error); + /* + * Return an indication of where the new directory + * entry should be put. + */ + dp->de_fndoffset = slotoffset; + dp->de_fndcnt = wincnt - 1; + + /* + * We return with the directory locked, so that + * the parameters we set up above will still be + * valid if we actually decide to do a direnter(). + * We return ni_vp == NULL to indicate that the entry + * does not currently exist; we leave a pointer to + * the (locked) directory inode in ndp->ni_dvp. + * The pathname buffer is saved so that the name + * can be obtained later. + * + * NB - if the directory is unlocked, then this + * information cannot be used. + */ cnp->cn_flags |= SAVENAME; - if (!lockparent)/* leave searched dir locked? */ + if (!lockparent) VOP_UNLOCK(vdp, 0, p); - return EJUSTRETURN; + return (EJUSTRETURN); } /* - * Insert name in cache as non-existant if not trying to create it. + * Insert name into cache (as non-existent) if appropriate. */ if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) cache_enter(vdp, *vpp, cnp); - return ENOENT; + return (ENOENT); -found: ; +found: /* * NOTE: We still have the buffer with matched directory entry at * this point. */ isadir = dep->deAttributes & ATTR_DIRECTORY; scn = getushort(dep->deStartCluster); + if (FAT32(pmp)) { + scn |= getushort(dep->deHighClust) << 16; + if (scn == pmp->pm_rootdirblk) { + /* + * There should actually be 0 here. + * Just ignore the error. + */ + scn = MSDOSFSROOT; + } + } -foundroot:; + if (isadir) { + cluster = scn; + if (cluster == MSDOSFSROOT) + blkoff = MSDOSFSROOT_OFS; + else + blkoff = 0; + } else if (cluster == MSDOSFSROOT) + blkoff = diroff; + + /* + * Now release buf to allow deget to read the entry again. + * Reserving it here and giving it to deget could result + * in a deadlock. + */ + brelse(bp); + bp = 0; + +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. + * setup before jumping here. And, bp is already null. */ + if (FAT32(pmp) && scn == MSDOSFSROOT) + scn = pmp->pm_rootdirblk; /* - * 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 deleting, and at end of pathname, return + * parameters which can be used to remove file. + * If the wantparent flag isn't set, we return only + * the directory (in ndp->ni_dvp), otherwise we go + * on and lock the inode, being careful with ".". */ if (nameiop == DELETE && (flags & ISLASTCN)) { - error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc); - if (error) { - if (bp) - brelse(bp); - return error; - } + /* + * Don't allow deleting the root. + */ + if (blkoff == MSDOSFSROOT_OFS) + return EROFS; /* really? XXX */ + + /* + * Write access to directory required to delete files. + */ + error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_proc); + if (error) + return (error); + + /* + * Return pointer to current entry in dp->i_offset. + * Save directory inode pointer in ndp->ni_dvp for dirremove(). + */ 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; + return (0); } + error = deget(pmp, cluster, blkoff, &tdp); + if (error) + return (error); *vpp = DETOV(tdp); if (!lockparent) VOP_UNLOCK(vdp, 0, p); - if (bp) - brelse(bp); - return 0; + return (0); } /* - * If renaming. + * If rewriting (RENAME), return the inode and the + * information required to rewrite the present directory + * Must get inode of directory entry to verify it's a + * regular file, or empty directory. */ - if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) { - error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc); - if (error) { - if (bp) - brelse(bp); - return error; - } - 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; - } + if (nameiop == RENAME && wantparent && + (flags & ISLASTCN)) { + if (blkoff == MSDOSFSROOT_OFS) + return EROFS; /* really? XXX */ + + error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_proc); + if (error) + return (error); + + /* + * Careful about locking second inode. + * This can only occur if the target is ".". + */ + if (dp->de_StartCluster == scn && isadir) + return (EISDIR); + + if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0) + return (error); *vpp = DETOV(tdp); cnp->cn_flags |= SAVENAME; if (!lockparent) VOP_UNLOCK(vdp, 0, p); - if (bp) - brelse(bp); - return 0; + return (0); } /* - * ? + * Step through the translation in the name. We do not `vput' the + * directory because we may need it again if a symbolic link + * is relative to the current directory. Instead we save it + * unlocked as "pdp". We must get the target inode before unlocking + * the directory to insure that the inode will not be removed + * before we get it. We prevent deadlock by always fetching + * inodes from the root, moving down the directory tree. Thus + * when following backward pointers ".." we must unlock the + * parent directory before getting the requested directory. + * There is a potential race condition here if both the current + * and parent directories are removed before the VFS_VGET for the + * inode associated with ".." returns. We hope that this occurs + * infrequently since we cannot avoid this race condition without + * implementing a sophisticated deadlock detection algorithm. + * Note also that this simple deadlock detection scheme will not + * work if the file system has any hard links other than ".." + * that point backwards in the directory structure. */ pdp = vdp; if (flags & ISDOTDOT) { VOP_UNLOCK(pdp, 0, p); - error = deget(pmp, cluster, diroff, dep, &tdp); + error = deget(pmp, cluster, blkoff, &tdp); if (error) { - vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p); - if (bp) - brelse(bp); - return error; + vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p); + return (error); } - if (lockparent && (flags & ISLASTCN) - && (error = vn_lock(pdp, LK_EXCLUSIVE, p))) { + if (lockparent && (flags & ISLASTCN) && + (error = vn_lock(pdp, LK_EXCLUSIVE, p))) { vput(DETOV(tdp)); - return error; + return (error); } *vpp = DETOV(tdp); - } else if (dp->de_StartCluster == scn && isadir) { /* "." */ - VREF(vdp); + } else if (dp->de_StartCluster == scn && isadir) { + VREF(vdp); /* we want ourself, ie "." */ *vpp = vdp; } else { - error = deget(pmp, cluster, diroff, dep, &tdp); - if (error) { - if (bp) - brelse(bp); - return error; - } + if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0) + return (error); if (!lockparent || !(flags & ISLASTCN)) VOP_UNLOCK(pdp, 0, p); *vpp = DETOV(tdp); } - if (bp) - brelse(bp); /* - * Insert name in cache if wanted. + * Insert name into cache if appropriate. */ if (cnp->cn_flags & MAKEENTRY) cache_enter(vdp, *vpp, cnp); - return 0; + return (0); } /* @@ -421,21 +538,26 @@ foundroot:; * ddep - directory to add to * depp - return the address of the denode for the created directory entry * if depp != 0 + * cnp - componentname needed for Win95 long filenames */ int -createde(dep, ddep, depp) +createde(dep, ddep, depp, cnp) struct denode *dep; struct denode *ddep; struct denode **depp; + struct componentname *cnp; { int error; u_long dirclust, diroffset; struct direntry *ndep; struct msdosfsmount *pmp = ddep->de_pmp; struct buf *bp; + daddr_t bn; + int blsize; #ifdef MSDOSFS_DEBUG - printf("createde(dep %08x, ddep %08x, depp %08x)\n", dep, ddep, depp); + printf("createde(dep %p, ddep %p, depp %p, cnp %p)\n", + dep, ddep, depp, cnp); #endif /* @@ -446,106 +568,102 @@ createde(dep, ddep, depp) * to extend the root directory. We just return an error in that * case. */ - if (ddep->de_fndclust == (u_long)-1) { - error = extendfile(ddep, 1, &bp, &dirclust, DE_CLEAR); - if (error) + if (ddep->de_fndoffset >= ddep->de_FileSize) { + diroffset = ddep->de_fndoffset + sizeof(struct direntry) + - ddep->de_FileSize; + dirclust = de_clcount(pmp, diroffset); + error = extendfile(ddep, dirclust, 0, 0, DE_CLEAR); + if (error) { + (void)detrunc(ddep, ddep->de_FileSize, 0, NOCRED, NULL); 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; + ddep->de_FileSize += de_cn2off(pmp, dirclust); } + + /* + * 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. + */ + error = pcbmap(ddep, de_cluster(pmp, ddep->de_fndoffset), + &bn, &dirclust, &blsize); + if (error) + return error; + diroffset = ddep->de_fndoffset; + if (dirclust != MSDOSFSROOT) + diroffset &= pmp->pm_crbomask; + if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp)) != 0) { + brelse(bp); + return error; + } + ndep = bptoep(pmp, bp, ddep->de_fndoffset); + DE_EXTERNALIZE(ndep, dep); + /* + * Now write the Win95 long name + */ + if (ddep->de_fndcnt > 0) { + u_int8_t chksum = winChksum(ndep->deName); + const u_char *un = (const u_char *)cnp->cn_nameptr; + int unlen = cnp->cn_namelen; + int cnt = 1; + + while (--ddep->de_fndcnt >= 0) { + if (!(ddep->de_fndoffset & pmp->pm_crbomask)) { + if ((error = bwrite(bp)) != 0) + return error; + + ddep->de_fndoffset -= sizeof(struct direntry); + error = pcbmap(ddep, + de_cluster(pmp, + ddep->de_fndoffset), + &bn, 0, &blsize); + if (error) + return error; + + error = bread(pmp->pm_devvp, bn, blsize, + NOCRED, &bp); + if (error) { + brelse(bp); + return error; + } + ndep = bptoep(pmp, bp, ddep->de_fndoffset); + } else { + ndep--; + ddep->de_fndoffset -= sizeof(struct direntry); + } + if (!unix2winfn(un, unlen, (struct winentry *)ndep, cnt++, chksum)) + break; + } + } + + if ((error = bwrite(bp)) != 0) + return error; + /* * If they want us to return with the denode gotten. */ if (depp) { - error = deget(pmp, dirclust, diroffset, ndep, depp); - if (error) - return error; - } - error = bwrite(bp); - if (error) { - vput(DETOV(*depp)); /* free the vnode we got on error */ - return error; + if (dep->de_Attributes & ATTR_DIRECTORY) { + dirclust = dep->de_StartCluster; + if (FAT32(pmp) && dirclust == pmp->pm_rootdirblk) + dirclust = MSDOSFSROOT; + if (dirclust == MSDOSFSROOT) + diroffset = MSDOSFSROOT_OFS; + else + diroffset = 0; + } + return deget(pmp, dirclust, diroffset, depp); } + return 0; } -/* - * Read in a directory entry and mark it as being deleted. - */ -static 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. @@ -554,7 +672,7 @@ int dosdirempty(dep) struct denode *dep; { - int dei; + int blsize; int error; u_long cn; daddr_t bn; @@ -568,16 +686,21 @@ dosdirempty(dep) * 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) { + if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) { + if (error == E2BIG) + return (1); /* it's empty */ + return (0); + } + error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); + if (error) { + brelse(bp); + return (0); + } + for (dentp = (struct direntry *)bp->b_data; + (char *)dentp < bp->b_data + blsize; + dentp++) { + if (dentp->deName[0] != SLOT_DELETED && + (dentp->deAttributes & ATTR_VOLUME) == 0) { /* * In dos directories an entry whose name * starts with SLOT_EMPTY (0) starts the @@ -587,7 +710,7 @@ dosdirempty(dep) */ if (dentp->deName[0] == SLOT_EMPTY) { brelse(bp); - return 1; + return (1); } /* * Any names other than "." and ".." in a @@ -597,13 +720,12 @@ dosdirempty(dep) bcmp(dentp->deName, ".. ", 11)) { brelse(bp); #ifdef MSDOSFS_DEBUG - printf("dosdirempty(): entry %d found %02x, %02x\n", - dei, dentp->deName[0], dentp->deName[1]); + printf("dosdirempty(): entry found %02x, %02x\n", + dentp->deName[0], dentp->deName[1]); #endif - return 0; /* not empty */ + return (0); /* not empty */ } } - dentp++; } brelse(bp); } @@ -647,18 +769,25 @@ doscheckpath(source, target) } if (dep->de_StartCluster == MSDOSFSROOT) goto out; + pmp = dep->de_pmp; +#ifdef DIAGNOSTIC + if (pmp != source->de_pmp) + panic("doscheckpath: source and target on different filesystems"); +#endif + if (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk) + 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; } + 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) { @@ -666,28 +795,38 @@ doscheckpath(source, target) break; } scn = getushort(ep->deStartCluster); + if (FAT32(pmp)) + scn |= getushort(ep->deHighClust) << 16; + if (scn == source->de_StartCluster) { error = EINVAL; break; } if (scn == MSDOSFSROOT) break; + if (FAT32(pmp) && scn == pmp->pm_rootdirblk) { + /* + * scn should be 0 in this case, + * but we silently ignore the error. + */ + break; + } + vput(DETOV(dep)); - /* NOTE: deget() clears dep on error */ - error = deget(pmp, scn, 0, ep, &dep); brelse(bp); bp = NULL; - if (error) + /* NOTE: deget() clears dep on error */ + if ((error = deget(pmp, scn, 0, &dep)) != 0) break; } -out: ; +out:; if (bp) brelse(bp); if (error == ENOTDIR) printf("doscheckpath(): .. not a directory?\n"); if (dep != NULL) vput(DETOV(dep)); - return error; + return (error); } /* @@ -696,27 +835,33 @@ out: ; * directory entry within the block. */ int -readep(pmp, dirclu, dirofs, bpp, epp) +readep(pmp, dirclust, diroffset, bpp, epp) struct msdosfsmount *pmp; - u_long dirclu, dirofs; + u_long dirclust, diroffset; struct buf **bpp; struct direntry **epp; { int error; daddr_t bn; + int blsize; + u_long boff; - bn = detobn(pmp, dirclu, dirofs); - error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, bpp); - if (error) { + boff = diroffset & ~pmp->pm_crbomask; + blsize = pmp->pm_bpcluster; + if (dirclust == MSDOSFSROOT + && de_blk(pmp, diroffset + blsize) > pmp->pm_rootdirsize) + blsize = de_bn2off(pmp, pmp->pm_rootdirsize) & pmp->pm_crbomask; + bn = detobn(pmp, dirclust, diroffset); + if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, bpp)) != 0) { + brelse(*bpp); *bpp = NULL; - return error; + return (error); } if (epp) - *epp = bptoep(pmp, *bpp, dirofs); - return 0; + *epp = bptoep(pmp, *bpp, diroffset); + 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 @@ -728,6 +873,195 @@ readde(dep, bpp, epp) struct buf **bpp; struct direntry **epp; { - return readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset, - bpp, epp); + + return (readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset, + bpp, epp)); +} + +/* + * Remove a directory entry. At this point the file represented by the + * directory entry to be removed is still full length until noone 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 */ +{ + int error; + struct direntry *ep; + struct buf *bp; + daddr_t bn; + int blsize; + struct msdosfsmount *pmp = pdep->de_pmp; + u_long offset = pdep->de_fndoffset; + +#ifdef MSDOSFS_DEBUG + printf("removede(): filename %s, dep %p, offset %08lx\n", + dep->de_Name, dep, offset); +#endif + + dep->de_refcnt--; + offset += sizeof(struct direntry); + do { + offset -= sizeof(struct direntry); + error = pcbmap(pdep, de_cluster(pmp, offset), &bn, 0, &blsize); + if (error) + return error; + error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); + if (error) { + brelse(bp); + return error; + } + ep = bptoep(pmp, bp, offset); + /* + * Check whether, if we came here the second time, i.e. + * when underflowing into the previous block, the last + * entry in this block is a longfilename entry, too. + */ + if (ep->deAttributes != ATTR_WIN95 + && offset != pdep->de_fndoffset) { + brelse(bp); + break; + } + offset += sizeof(struct direntry); + while (1) { + /* + * We are a bit agressive here in that we delete any Win95 + * entries preceding this entry, not just the ones we "own". + * Since these presumably aren't valid anyway, + * there should be no harm. + */ + offset -= sizeof(struct direntry); + ep--->deName[0] = SLOT_DELETED; + if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) + || !(offset & pmp->pm_crbomask) + || ep->deAttributes != ATTR_WIN95) + break; + } + if ((error = bwrite(bp)) != 0) + return error; + } while (!(pmp->pm_flags & MSDOSFSMNT_NOWIN95) + && !(offset & pmp->pm_crbomask) + && offset); + return 0; +} + +/* + * Create a unique DOS name in dvp + */ +int +uniqdosname(dep, cnp, cp) + struct denode *dep; + struct componentname *cnp; + u_char *cp; +{ + struct msdosfsmount *pmp = dep->de_pmp; + struct direntry *dentp; + int gen; + int blsize; + u_long cn; + daddr_t bn; + struct buf *bp; + int error; + + for (gen = 1;; gen++) { + /* + * Generate DOS name with generation number + */ + if (!unix2dosfn((const u_char *)cnp->cn_nameptr, cp, + cnp->cn_namelen, gen)) + return gen == 1 ? EINVAL : EEXIST; + + /* + * Now look for a dir entry with this exact name + */ + for (cn = error = 0; !error; cn++) { + if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) { + if (error == E2BIG) /* EOF reached and not found */ + return 0; + return error; + } + error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); + if (error) { + brelse(bp); + return error; + } + for (dentp = (struct direntry *)bp->b_data; + (char *)dentp < bp->b_data + blsize; + dentp++) { + if (dentp->deName[0] == SLOT_EMPTY) { + /* + * Last used entry and not found + */ + brelse(bp); + return 0; + } + /* + * Ignore volume labels and Win95 entries + */ + if (dentp->deAttributes & ATTR_VOLUME) + continue; + if (!bcmp(dentp->deName, cp, 11)) { + error = EEXIST; + break; + } + } + brelse(bp); + } + } +} + +/* + * Find any Win'95 long filename entry in directory dep + */ +int +findwin95(dep) + struct denode *dep; +{ + struct msdosfsmount *pmp = dep->de_pmp; + struct direntry *dentp; + int blsize; + u_long cn; + daddr_t bn; + struct buf *bp; + + /* + * Read through the directory looking for Win'95 entries + * Note: Error currently handled just as EOF XXX + */ + for (cn = 0;; cn++) { + if (pcbmap(dep, cn, &bn, 0, &blsize)) + return 0; + if (bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp)) { + brelse(bp); + return 0; + } + for (dentp = (struct direntry *)bp->b_data; + (char *)dentp < bp->b_data + blsize; + dentp++) { + if (dentp->deName[0] == SLOT_EMPTY) { + /* + * Last used entry and not found + */ + brelse(bp); + return 0; + } + if (dentp->deName[0] == SLOT_DELETED) { + /* + * Ignore deleted files + * Note: might be an indication of Win'95 anyway XXX + */ + continue; + } + if (dentp->deAttributes == ATTR_WIN95) { + brelse(bp); + return 1; + } + } + brelse(bp); + } } diff --git a/sys/fs/msdosfs/msdosfs_vfsops.c b/sys/fs/msdosfs/msdosfs_vfsops.c index 1fc73f637e67..f552266d8afb 100644 --- a/sys/fs/msdosfs/msdosfs_vfsops.c +++ b/sys/fs/msdosfs/msdosfs_vfsops.c @@ -1,9 +1,9 @@ -/* $Id: msdosfs_vfsops.c,v 1.22 1997/10/12 20:25:01 phk Exp $ */ -/* $NetBSD: msdosfs_vfsops.c,v 1.19 1994/08/21 18:44:10 ws Exp $ */ +/* $Id: msdosfs_vfsops.c,v 1.23 1997/11/12 05:42:19 julian Exp $ */ +/* $NetBSD: msdosfs_vfsops.c,v 1.51 1997/11/17 15:36:58 ws Exp $ */ /*- - * Copyright (C) 1994 Wolfgang Solfrank. - * Copyright (C) 1994 TooLs GmbH. + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * @@ -59,6 +59,7 @@ #include #include #include +#include /* defines ALLPERMS */ #include #include @@ -70,8 +71,9 @@ MALLOC_DEFINE(M_MSDOSFSMNT, "MSDOSFS mount", "MSDOSFS mount structure"); static MALLOC_DEFINE(M_MSDOSFSFAT, "MSDOSFS FAT", "MSDOSFS file allocation table"); +static int update_mp __P((struct mount *mp, struct msdosfs_args *argp)); static int mountmsdosfs __P((struct vnode *devvp, struct mount *mp, - struct proc *p)); + struct proc *p, struct msdosfs_args *argp)); static int msdosfs_fhtovp __P((struct mount *, struct fid *, struct sockaddr *, struct vnode **, int *, struct ucred **)); @@ -90,6 +92,111 @@ static int msdosfs_vget __P((struct mount *mp, ino_t ino, struct vnode **vpp)); static int msdosfs_vptofh __P((struct vnode *, struct fid *)); +static int +update_mp(mp, argp) + struct mount *mp; + struct msdosfs_args *argp; +{ + struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); + int error; + + pmp->pm_gid = argp->gid; + pmp->pm_uid = argp->uid; + pmp->pm_mask = argp->mask & ALLPERMS; + pmp->pm_flags |= argp->flags & MSDOSFSMNT_MNTOPT; + +#ifndef __FreeBSD__ + /* + * GEMDOS knows nothing (yet) about win95 + */ + if (pmp->pm_flags & MSDOSFSMNT_GEMDOSFS) + pmp->pm_flags |= MSDOSFSMNT_NOWIN95; +#endif + + if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) + pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; + else if (!(pmp->pm_flags & + (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME))) { + struct vnode *rootvp; + + /* + * Try to divine whether to support Win'95 long filenames + */ + if (FAT32(pmp)) + pmp->pm_flags |= MSDOSFSMNT_LONGNAME; + else { + if ((error = msdosfs_root(mp, &rootvp)) != 0) + return error; + pmp->pm_flags |= findwin95(VTODE(rootvp)) + ? MSDOSFSMNT_LONGNAME + : MSDOSFSMNT_SHORTNAME; + vput(rootvp); + } + } + return 0; +} + +#ifndef __FreeBSD__ +int +msdosfs_mountroot() +{ + register struct mount *mp; + struct proc *p = curproc; /* XXX */ + size_t size; + int error; + struct msdosfs_args args; + + if (root_device->dv_class != DV_DISK) + return (ENODEV); + + /* + * Get vnodes for swapdev and rootdev. + */ + if (bdevvp(rootdev, &rootvp)) + panic("msdosfs_mountroot: can't setup rootvp"); + + mp = malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK); + bzero((char *)mp, (u_long)sizeof(struct mount)); + mp->mnt_op = &msdosfs_vfsops; + mp->mnt_flag = 0; + LIST_INIT(&mp->mnt_vnodelist); + + args.flags = 0; + args.uid = 0; + args.gid = 0; + args.mask = 0777; + + if ((error = mountmsdosfs(rootvp, mp, p, &args)) != 0) { + free(mp, M_MOUNT); + return (error); + } + + if ((error = update_mp(mp, &args)) != 0) { + (void)msdosfs_unmount(mp, 0, p); + free(mp, M_MOUNT); + return (error); + } + + if ((error = vfs_lock(mp)) != 0) { + (void)msdosfs_unmount(mp, 0, p); + free(mp, M_MOUNT); + return (error); + } + + CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list); + mp->mnt_vnodecovered = NULLVP; + (void) copystr("/", mp->mnt_stat.f_mntonname, MNAMELEN - 1, + &size); + bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); + (void) copystr(ROOTNAME, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, + &size); + bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); + (void)msdosfs_statfs(mp, &mp->mnt_stat, p); + vfs_unlock(mp); + return (0); +} +#endif + /* * 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 @@ -105,29 +212,27 @@ msdosfs_mount(mp, path, data, ndp, 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 */ + /* msdosfs specific mount control block */ + struct msdosfsmount *pmp = NULL; + size_t size; int error, flags; - u_int size; - struct ucred *cred, *scred; - struct vattr va; + mode_t accessmode; - /* - * Copy in the args for the mount request. - */ - error = copyin(data, (caddr_t) & args, sizeof(struct msdosfs_args)); + error = copyin(data, (caddr_t)&args, sizeof(struct msdosfs_args)); if (error) - return error; - + return (error); + if (args.magic != MSDOSFS_ARGSMAGIC) { + printf("Old mount_msdosfs, flags=%d\n", args.flags); + args.flags = 0; + } /* - * 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 updating, check whether changing from read-only to + * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { - pmp = (struct msdosfsmount *) mp->mnt_data; + pmp = VFSTOMSDOSFS(mp); error = 0; - if (pmp->pm_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) { + if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_flag & MNT_RDONLY)) { flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; @@ -135,214 +240,226 @@ msdosfs_mount(mp, path, data, ndp, p) } if (!error && (mp->mnt_flag & MNT_RELOAD)) /* not yet implemented */ - error = EINVAL; + error = EOPNOTSUPP; if (error) - return error; - if (pmp->pm_ronly && (mp->mnt_kern_flag & MNTK_WANTRDWR)) - pmp->pm_ronly = 0; + return (error); + if ((pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_kern_flag & MNTK_WANTRDWR)) { + /* + * If upgrade to read-write by non-root, then verify + * that user has necessary permissions on the device. + */ + if (p->p_ucred->cr_uid != 0) { + devvp = pmp->pm_devvp; + vn_lock(devvp, LK_EXCLUSIVE, p); + error = VOP_ACCESS(devvp, VREAD | VWRITE, + p->p_ucred, p); + if (error) { + VOP_UNLOCK(devvp, 0, p); + return (error); + } + VOP_UNLOCK(devvp, 0, p); + } + pmp->pm_flags &= ~MSDOSFSMNT_RONLY; + } if (args.fspec == 0) { +#ifdef __notyet__ /* doesn't work correctly with current mountd XXX */ + if (args.flags & MSDOSFSMNT_MNTOPT) { + pmp->pm_flags &= ~MSDOSFSMNT_MNTOPT; + pmp->pm_flags |= args.flags & MSDOSFSMNT_MNTOPT; + if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) + pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; + } +#endif /* * Process export requests. */ - return vfs_export(mp, &pmp->pm_export, &args.export); + 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. + * Not an update, or updating the name: look up the name + * and verify that it refers to a sensible block device. */ 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. - */ + if (error) + return (error); devvp = ndp->ni_vp; + if (devvp->v_type != VBLK) { vrele(devvp); - return ENOTBLK; + return (ENOTBLK); } if (major(devvp->v_rdev) >= nblkdev) { vrele(devvp); - return ENXIO; + 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 mount by non-root, then verify that user has necessary + * permissions on the device. */ - if (mp->mnt_flag & MNT_UPDATE) { + if (p->p_ucred->cr_uid != 0) { + accessmode = VREAD; + if ((mp->mnt_flag & MNT_RDONLY) == 0) + accessmode |= VWRITE; + vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); + error = VOP_ACCESS(devvp, accessmode, p->p_ucred, p); + if (error) { + vput(devvp); + return (error); + } + VOP_UNLOCK(devvp, 0, p); + } + if ((mp->mnt_flag & MNT_UPDATE) == 0) { + error = mountmsdosfs(devvp, mp, p, &args); +#ifdef MSDOSFS_DEBUG /* only needed for the printf below */ + pmp = VFSTOMSDOSFS(mp); +#endif + } else { if (devvp != pmp->pm_devvp) - error = EINVAL; + error = EINVAL; /* XXX needs translation */ 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); + } + + error = update_mp(mp, &args); + if (error) { + msdosfs_unmount(mp, MNT_FORCE, p); 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) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); + bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); + (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, + &size); + bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); (void) msdosfs_statfs(mp, &mp->mnt_stat, p); #ifdef MSDOSFS_DEBUG printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap); #endif - return 0; + return (0); } static int -mountmsdosfs(devvp, mp, p) +mountmsdosfs(devvp, mp, p, argp) struct vnode *devvp; struct mount *mp; struct proc *p; + struct msdosfs_args *argp; { - int i; - int bpc; - int bit; - int error; - int needclose; - int ronly = (mp->mnt_flag & MNT_RDONLY) != 0; + struct msdosfsmount *pmp; + struct buf *bp; dev_t dev = devvp->v_rdev; +#ifndef __FreeBSD__ + struct partinfo dpart; +#endif union bootsector *bsp; - struct msdosfsmount *pmp = NULL; - struct buf *bp0 = NULL; struct byte_bpb33 *b33; struct byte_bpb50 *b50; #ifdef PC98 u_int pc98_wrk; u_int Phy_Sector_Size; #endif + struct byte_bpb710 *b710; + u_int8_t SecPerClust; + int ronly, error; + int bsize = 0, dtype = 0, tmp; /* - * 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. + * Disallow multiple mounts of the same device. + * Disallow mounting of a device that is currently in use + * (except for root, which might share swap device for miniroot). + * Flush out any old buffers remaining from a previous use. */ error = vfs_mountedon(devvp); if (error) - return error; - if (vcount(devvp) > 1) - return EBUSY; + return (error); + if (vcount(devvp) > 1 && devvp != rootvp) + return (EBUSY); + vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0); + VOP_UNLOCK(devvp, 0, p); if (error) - return error; + return (error); - /* - * Now open the block special file. - */ - error = VOP_OPEN(devvp, ronly ? FREAD : FREAD | FWRITE, FSCRED, p); + ronly = (mp->mnt_flag & MNT_RDONLY) != 0; + error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p); if (error) - 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) { + return (error); + + bp = NULL; /* both used in error_exit */ + pmp = NULL; + +#ifndef __FreeBSD__ + if (argp->flags & MSDOSFSMNT_GEMDOSFS) { + /* + * We need the disklabel to calculate the size of a FAT entry + * later on. Also make sure the partition contains a filesystem + * of type FS_MSDOS. This doesn't work for floppies, so we have + * to check for them too. + * + * At least some parts of the msdos fs driver seem to assume + * that the size of a disk block will always be 512 bytes. + * Let's check it... + */ + error = VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, + FREAD, NOCRED, p); + if (error) + goto error_exit; + tmp = dpart.part->p_fstype; + dtype = dpart.disklab->d_type; + bsize = dpart.disklab->d_secsize; + if (bsize != 512 || (dtype!=DTYPE_FLOPPY && tmp!=FS_MSDOS)) { + error = EINVAL; + goto error_exit; + } } #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" + * Read the boot sector of the filesystem, and then check the + * boot signature. If not a dos boot sector then error out. */ #ifdef PC98 devvp->v_flag &= 0xffff; - error = bread(devvp, 0, 1024, NOCRED, &bp0); + error = bread(devvp, 0, 1024, NOCRED, &bp); #else - error = bread(devvp, 0, 512, NOCRED, &bp0); + error = bread(devvp, 0, 512, NOCRED, &bp); #endif if (error) 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 -#ifdef PC98 - if (bsp->bs50.bsBootSectSig != BOOTSIG && - bsp->bs50.bsBootSectSig != 0 && /* PC98 DOS 3.3x */ - bsp->bs50.bsBootSectSig != 15760 && /* PC98 DOS 5.0 */ - bsp->bs50.bsBootSectSig != 64070) { /* PC98 DOS 3.3B */ + bp->b_flags |= B_AGE; + bsp = (union bootsector *)bp->b_data; + b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB; + b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB; + b710 = (struct byte_bpb710 *)bsp->bs710.bsPBP; + +#ifndef __FreeBSD__ + if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) { +#endif +#ifdef PC98 + if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 + || bsp->bs50.bsBootSectSig1 != BOOTSIG1 + && bsp->bs50.bsBootSectSig0 != 0 /* PC98 DOS 3.3x */ + || bsp->bs50.bsBootSectSig1 != 0 + && bsp->bs50.bsBootSectSig0 != 0x90 /* PC98 DOS 5.0 */ + || bsp->bs50.bsBootSectSig1 != 0x3d + && bsp->bs50.bsBootSectSig0 != 0x46 /* PC98 DOS 3.3B */ + || bsp->bs50.bsBootSectSig1 != 0xfa) { #else - if (bsp->bs50.bsBootSectSig != BOOTSIG) { + if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 + || bsp->bs50.bsBootSectSig1 != BOOTSIG1) { #endif - error = EINVAL; - goto error_exit; + error = EINVAL; + goto error_exit; + } +#ifndef __FreeBSD__ } #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); @@ -353,28 +470,34 @@ mountmsdosfs(devvp, mp, p) * 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. */ + SecPerClust = b50->bpbSecPerClust; 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); + pmp->pm_Media = b50->bpbMedia; - /* XXX - We should probably check more values here */ - if (!pmp->pm_BytesPerSec || !pmp->pm_SectPerClust || - !pmp->pm_Heads || pmp->pm_Heads > 255 || -#ifdef PC98 - !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 255) { -#else - !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) { +#ifndef __FreeBSD__ + if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) { #endif - error = EINVAL; - goto error_exit; + /* XXX - We should probably check more values here */ + if (!pmp->pm_BytesPerSec || !SecPerClust + || !pmp->pm_Heads || pmp->pm_Heads > 255 +#ifdef PC98 + || !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 255) { +#else + || !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) { +#endif + error = EINVAL; + goto error_exit; + } +#ifndef __FreeBSD__ } +#endif if (pmp->pm_Sectors == 0) { pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); @@ -411,86 +534,193 @@ mountmsdosfs(devvp, mp, p) } pc98_wrk = pmp->pm_BytesPerSec / Phy_Sector_Size; pmp->pm_BytesPerSec = Phy_Sector_Size; - pmp->pm_SectPerClust = pmp->pm_SectPerClust * pc98_wrk; + SecPerClust = SecPerClust * pc98_wrk; pmp->pm_HugeSectors = pmp->pm_HugeSectors * pc98_wrk; pmp->pm_ResSectors = pmp->pm_ResSectors * pc98_wrk; pmp->pm_FATsecs = pmp->pm_FATsecs * pc98_wrk; pmp->pm_SecPerTrack = pmp->pm_SecPerTrack * pc98_wrk; pmp->pm_HiddenSects = pmp->pm_HiddenSects * pc98_wrk; #endif /* */ + if (pmp->pm_HugeSectors > 0xffffffff / pmp->pm_BytesPerSec + 1) { + /* + * We cannot deal currently with this size of disk + * due to fileid limitations (see msdosfs_getattr and + * msdosfs_readdir) + */ + error = EINVAL; + goto error_exit; + } + + if (pmp->pm_RootDirEnts == 0) { + if (bsp->bs710.bsBootSectSig2 != BOOTSIG2 + || bsp->bs710.bsBootSectSig3 != BOOTSIG3 + || pmp->pm_Sectors + || pmp->pm_FATsecs + || getushort(b710->bpbFSVers)) { + error = EINVAL; + goto error_exit; + } + pmp->pm_fatmask = FAT32_MASK; + pmp->pm_fatmult = 4; + pmp->pm_fatdiv = 1; + pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs); + if (getushort(b710->bpbExtFlags) & FATMIRROR) + pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM; + else + pmp->pm_flags |= MSDOSFS_FATMIRROR; + } else + pmp->pm_flags |= MSDOSFS_FATMIRROR; + +#ifndef __FreeBSD__ + if (argp->flags & MSDOSFSMNT_GEMDOSFS) { + if (FAT32(pmp)) { + /* + * GEMDOS doesn't know fat32. + */ + error = EINVAL; + goto error_exit; + } + + /* + * Check a few values (could do some more): + * - logical sector size: power of 2, >= block size + * - sectors per cluster: power of 2, >= 1 + * - number of sectors: >= 1, <= size of partition + */ + if ( (SecPerClust == 0) + || (SecPerClust & (SecPerClust - 1)) + || (pmp->pm_BytesPerSec < bsize) + || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1)) + || (pmp->pm_HugeSectors == 0) + || (pmp->pm_HugeSectors * (pmp->pm_BytesPerSec / bsize) + > dpart.part->p_size) + ) { + error = EINVAL; + goto error_exit; + } + /* + * XXX - Many parts of the msdos fs driver seem to assume that + * the number of bytes per logical sector (BytesPerSec) will + * always be the same as the number of bytes per disk block + * Let's pretend it is. + */ + tmp = pmp->pm_BytesPerSec / bsize; + pmp->pm_BytesPerSec = bsize; + pmp->pm_HugeSectors *= tmp; + pmp->pm_HiddenSects *= tmp; + pmp->pm_ResSectors *= tmp; + pmp->pm_Sectors *= tmp; + pmp->pm_FATsecs *= tmp; + SecPerClust *= tmp; + } +#endif 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; + if (FAT32(pmp)) { + pmp->pm_rootdirblk = getulong(b710->bpbRootClust); + pmp->pm_firstcluster = pmp->pm_fatblk + + (pmp->pm_FATs * pmp->pm_FATsecs); + pmp->pm_fsinfo = getushort(b710->bpbFSInfo); + } else { + pmp->pm_rootdirblk = pmp->pm_fatblk + + (pmp->pm_FATs * pmp->pm_FATsecs); + pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry) + + pmp->pm_BytesPerSec - 1) + / 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; + SecPerClust; pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1; pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec; + +#ifndef __FreeBSD__ + if (argp->flags & MSDOSFSMNT_GEMDOSFS) { + if ((pmp->pm_nmbrofclusters <= (0xff0 - 2)) + && ((dtype == DTYPE_FLOPPY) || ((dtype == DTYPE_VNODE) + && ((pmp->pm_Heads == 1) || (pmp->pm_Heads == 2)))) + ) { + pmp->pm_fatmask = FAT12_MASK; + pmp->pm_fatmult = 3; + pmp->pm_fatdiv = 2; + } else { + pmp->pm_fatmask = FAT16_MASK; + pmp->pm_fatmult = 2; + pmp->pm_fatdiv = 1; + } + } else +#endif + if (pmp->pm_fatmask == 0) { + if (pmp->pm_maxcluster + <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) { + /* + * This will usually be a floppy disk. This size makes + * sure that one fat entry will not be split across + * multiple blocks. + */ + pmp->pm_fatmask = FAT12_MASK; + pmp->pm_fatmult = 3; + pmp->pm_fatdiv = 2; + } else { + pmp->pm_fatmask = FAT16_MASK; + pmp->pm_fatmult = 2; + pmp->pm_fatdiv = 1; + } + } 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(): Warning: root directory is not a multiple of the clustersize in length\n"); + pmp->pm_bnshift = ffs(pmp->pm_BytesPerSec) - 1; /* * 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) { + pmp->pm_bpcluster = SecPerClust * pmp->pm_BytesPerSec; + pmp->pm_crbomask = pmp->pm_bpcluster - 1; + pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1; + + /* + * Check for valid cluster size + * must be a power of 2 + */ + if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) { 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; - } - -#ifdef PC98 - if (Phy_Sector_Size == 512) { - pmp->pm_brbomask = 0x01ff; /* 512 byte blocks only (so far) */ - pmp->pm_bnshift = 9; /* shift right 9 bits to get bn */ - } else { - pmp->pm_brbomask = 0x03ff; - pmp->pm_bnshift = 10; - } -#else - pmp->pm_brbomask = 0x01ff; /* 512 byte blocks only (so far) */ - pmp->pm_bnshift = 9; /* shift right 9 bits to get bn */ -#endif /* * Release the bootsector buffer. */ - brelse(bp0); - bp0 = NULL; + brelse(bp); + bp = NULL; + + /* + * Check FSInfo. + */ + if (pmp->pm_fsinfo) { + struct fsinfo *fp; + + if ((error = bread(devvp, pmp->pm_fsinfo, 1024, NOCRED, &bp)) != 0) + goto error_exit; + fp = (struct fsinfo *)bp->b_data; + if (!bcmp(fp->fsisig1, "RRaA", 4) + && !bcmp(fp->fsisig2, "rrAa", 4) + && !bcmp(fp->fsisig3, "\0\0\125\252", 4) + && !bcmp(fp->fsisig4, "\0\0\125\252", 4)) + pmp->pm_nxtfree = getulong(fp->fsinxtfree); + else + pmp->pm_fsinfo = 0; + brelse(bp); + bp = NULL; + } + + /* + * Check and validate (or perhaps invalidate?) the fsinfo structure? XXX + */ /* * Allocate memory for the bitmap of allocated clusters, and then @@ -510,8 +740,7 @@ mountmsdosfs(devvp, mp, p) /* * Have the inuse map filled in. */ - error = fillinusemap(pmp); - if (error) + if ((error = fillinusemap(pmp)) != 0) goto error_exit; /* @@ -520,13 +749,15 @@ mountmsdosfs(devvp, mp, p) * 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; + if (mp->mnt_flag & MNT_SYNCHRONOUS) + pmp->pm_flags |= MSDOSFSMNT_WAITONFAT; /* * Finish up. */ - pmp->pm_ronly = ronly; - if (ronly == 0) + if (ronly) + pmp->pm_flags |= MSDOSFSMNT_RONLY; + else pmp->pm_fmod = 1; mp->mnt_data = (qaddr_t) pmp; mp->mnt_stat.f_fsid.val[0] = (long)dev; @@ -536,19 +767,17 @@ mountmsdosfs(devvp, mp, p) return 0; -error_exit:; - if (bp0) - brelse(bp0); - if (needclose) - (void) VOP_CLOSE(devvp, ronly ? FREAD : FREAD | FWRITE, - NOCRED, p); +error_exit: + if (bp) + brelse(bp); + (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; + free(pmp->pm_inusemap, M_MSDOSFSFAT); + free(pmp, M_MSDOSFSMNT); + mp->mnt_data = (qaddr_t)0; } - return error; + return (error); } static int @@ -557,7 +786,8 @@ msdosfs_start(mp, flags, p) int flags; struct proc *p; { - return 0; + + return (0); } /* @@ -569,30 +799,47 @@ msdosfs_unmount(mp, mntflags, p) int mntflags; struct proc *p; { - int flags = 0; - int error; - struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; + struct msdosfsmount *pmp; + int error, flags; - /* 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) { + flags = 0; + if (mntflags & MNT_FORCE) flags |= FORCECLOSE; - } error = vflush(mp, NULLVP, flags); if (error) return error; + pmp = VFSTOMSDOSFS(mp); pmp->pm_devvp->v_specflags &= ~SI_MOUNTEDON; - error = VOP_CLOSE(pmp->pm_devvp, pmp->pm_ronly ? FREAD : FREAD | FWRITE, +#ifdef MSDOSFS_DEBUG + { + struct vnode *vp = pmp->pm_devvp; + + printf("msdosfs_umount(): just before calling VOP_CLOSE()\n"); + printf("flag %08lx, usecount %d, writecount %d, holdcnt %ld\n", + vp->v_flag, vp->v_usecount, vp->v_writecount, vp->v_holdcnt); + printf("lastr %d, id %lu, mount %p, op %p\n", + vp->v_lastr, vp->v_id, vp->v_mount, vp->v_op); + printf("freef %p, freeb %p, mount %p\n", + vp->v_freelist.tqe_next, vp->v_freelist.tqe_prev, + vp->v_mount); + printf("cleanblkhd %p, dirtyblkhd %p, numoutput %ld, type %d\n", + vp->v_cleanblkhd.lh_first, + vp->v_dirtyblkhd.lh_first, + vp->v_numoutput, vp->v_type); + printf("union %p, tag %d, data[0] %08x, data[1] %08x\n", + vp->v_socket, vp->v_tag, + ((u_int *)vp->v_data)[0], + ((u_int *)vp->v_data)[1]); + } +#endif + error = VOP_CLOSE(pmp->pm_devvp, (pmp->pm_flags&MSDOSFSMNT_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; + free(pmp->pm_inusemap, M_MSDOSFSFAT); + free(pmp, M_MSDOSFSMNT); + mp->mnt_data = (qaddr_t)0; mp->mnt_flag &= ~MNT_LOCAL; - return error; + return (error); } static int @@ -600,18 +847,18 @@ msdosfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { + struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 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 %p, pmp %p, ndep %p, vp %p\n", - mp, pmp, ndep, DETOV(ndep)); + printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp); #endif - if (error == 0) - *vpp = DETOV(ndep); - return error; + error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep); + if (error) + return (error); + *vpp = DETOV(ndep); + return (0); } static int @@ -631,11 +878,9 @@ msdosfs_statfs(mp, sbp, p) struct statfs *sbp; struct proc *p; { - struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; + struct msdosfsmount *pmp; - /* - * Fill in the stat block. - */ + pmp = VFSTOMSDOSFS(mp); sbp->f_bsize = pmp->pm_bpcluster; sbp->f_iosize = pmp->pm_bpcluster; sbp->f_blocks = pmp->pm_nmbrofclusters; @@ -643,23 +888,13 @@ msdosfs_statfs(mp, sbp, p) 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) { sbp->f_type = mp->mnt_vfc->vfc_typenum; - 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); + bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); + bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } -#if 0 - strncpy(&sbp->f_fstypename[0], mp->mnt_op->vfs_name, MFSNAMELEN); - sbp->f_fstypename[MFSNAMELEN] = '\0'; -#endif - return 0; + strncpy(sbp->f_fstypename, mp->mnt_vfc->vfc_name, MFSNAMELEN); + return (0); } static int @@ -669,39 +904,42 @@ msdosfs_sync(mp, waitfor, cred, p) struct ucred *cred; struct proc *p; { - struct vnode *vp; + struct vnode *vp, *nvp; struct denode *dep; - struct msdosfsmount *pmp; - int error; - int allerror = 0; - - pmp = (struct msdosfsmount *) mp->mnt_data; + struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); + int error, allerror = 0; /* * 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) + if (pmp->pm_fmod != 0) + if (pmp->pm_flags & MSDOSFSMNT_RONLY) panic("msdosfs_sync: rofs mod"); else { /* update fats here */ } - /* - * Go thru in memory denodes and write them out along with - * unwritten file blocks. + * Write back each (modified) denode. */ simple_lock(&mntvnode_slock); loop: - for (vp = mp->mnt_vnodelist.lh_first; vp; - vp = vp->v_mntvnodes.le_next) { - if (vp->v_mount != mp) /* not ours anymore */ + for (vp = mp->mnt_vnodelist.lh_first; + vp != NULL; + vp = nvp) { + /* + * If the vnode that we are about to sync is no longer + * assoicated with this mount point, start over. + */ + if (vp->v_mount != mp) goto loop; + simple_lock(&vp->v_interlock); + nvp = vp->v_mntvnodes.le_next; dep = VTODE(vp); - if ((dep->de_flag & (DE_MODIFIED | DE_UPDATE)) == 0 && - vp->v_dirtyblkhd.lh_first == NULL) { + if (vp->v_type == VNON || ((dep->de_flag & + (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0) + && vp->v_dirtyblkhd.lh_first == NULL) { simple_unlock(&vp->v_interlock); continue; } @@ -728,7 +966,7 @@ loop: error = VOP_FSYNC(pmp->pm_devvp, cred, waitfor, p); if (error) allerror = error; - return allerror; + return (allerror); } static int @@ -740,7 +978,7 @@ msdosfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) int *exflagsp; struct ucred **credanonp; { - struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; + struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); struct defid *defhp = (struct defid *) fhp; struct denode *dep; struct netcred *np; @@ -748,33 +986,33 @@ msdosfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) 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); + return (EACCES); + error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, &dep); if (error) { *vpp = NULLVP; - return error; + return (error); } *vpp = DETOV(dep); *exflagsp = np->netc_exflags; *credanonp = &np->netc_anon; - return 0; + return (0); } - static int msdosfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { - struct denode *dep = VTODE(vp); - struct defid *defhp = (struct defid *) fhp; + struct denode *dep; + struct defid *defhp; + dep = VTODE(vp); + 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; + /* defhp->defid_gen = dep->de_gen; */ + return (0); } static int diff --git a/sys/fs/msdosfs/msdosfs_vnops.c b/sys/fs/msdosfs/msdosfs_vnops.c index 3d7b3f7f5fe5..a78a4672c4d2 100644 --- a/sys/fs/msdosfs/msdosfs_vnops.c +++ b/sys/fs/msdosfs/msdosfs_vnops.c @@ -1,9 +1,9 @@ -/* $Id: msdosfs_vnops.c,v 1.54 1998/02/04 22:33:01 eivind Exp $ */ -/* $NetBSD: msdosfs_vnops.c,v 1.20 1994/08/21 18:44:13 ws Exp $ */ +/* $Id: msdosfs_vnops.c,v 1.55 1998/02/06 12:13:46 eivind Exp $ */ +/* $NetBSD: msdosfs_vnops.c,v 1.68 1998/02/10 14:10:04 mrg Exp $ */ /*- - * Copyright (C) 1994 Wolfgang Solfrank. - * Copyright (C) 1994 TooLs GmbH. + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * @@ -140,9 +140,20 @@ msdosfs_create(ap) int error; #ifdef MSDOSFS_DEBUG - printf("msdosfs_create(cnp %08x, vap %08x\n", cnp, ap->a_vap); + printf("msdosfs_create(cnp %p, vap %p\n", cnp, ap->a_vap); #endif + /* + * If this is the root directory and there is no space left we + * can't do anything. This is because the root directory can not + * change size. + */ + if (pdep->de_StartCluster == MSDOSFSROOT + && pdep->de_fndoffset >= pdep->de_FileSize) { + error = ENOSPC; + goto bad; + } + /* * Create a directory entry for the file, then call createde() to * have it installed. NOTE: DOS files are always executable. We @@ -150,28 +161,37 @@ msdosfs_create(ap) * readonly. */ #ifdef DIAGNOSTIC - if ((cnp->cn_flags & SAVENAME) == 0) + if ((cnp->cn_flags & HASBUF) == 0) panic("msdosfs_create: no name"); #endif bzero(&ndirent, sizeof(ndirent)); - TIMEVAL_TO_TIMESPEC(&time, &ts); - unix2dostime(&ts, &ndirent.de_Date, &ndirent.de_Time); - unix2dosfn((u_char *)cnp->cn_nameptr, ndirent.de_Name, cnp->cn_namelen); - ndirent.de_Attributes = (ap->a_vap->va_mode & VWRITE) - ? ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY; + error = uniqdosname(pdep, cnp, ndirent.de_Name); + if (error) + goto bad; + + ndirent.de_Attributes = (ap->a_vap->va_mode & VWRITE) ? + ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY; ndirent.de_StartCluster = 0; ndirent.de_FileSize = 0; ndirent.de_dev = pdep->de_dev; ndirent.de_devvp = pdep->de_devvp; - if ((error = createde(&ndirent, pdep, &dep)) == 0) { - *ap->a_vpp = DETOV(dep); - if ((cnp->cn_flags & SAVESTART) == 0) - zfree(namei_zone, cnp->cn_pnbuf); - } else { + ndirent.de_pmp = pdep->de_pmp; + ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; + TIMEVAL_TO_TIMESPEC(&time, &ts); + DETIMES(&ndirent, &ts, &ts, &ts); + error = createde(&ndirent, pdep, &dep, cnp); + if (error) + goto bad; + if ((cnp->cn_flags & SAVESTART) == 0) zfree(namei_zone, cnp->cn_pnbuf); - } - vput(ap->a_dvp); /* release parent dir */ - return error; + vput(ap->a_dvp); + *ap->a_vpp = DETOV(dep); + return (0); + +bad: + zfree(namei_zone, cnp->cn_pnbuf); + vput(ap->a_dvp); + return (error); } static int @@ -183,24 +203,22 @@ msdosfs_mknod(ap) struct vattr *a_vap; } */ *ap; { - int error; switch (ap->a_vap->va_type) { case VDIR: - error = msdosfs_mkdir((struct vop_mkdir_args *)ap); + return (msdosfs_mkdir((struct vop_mkdir_args *)ap)); break; case VREG: - error = msdosfs_create((struct vop_create_args *)ap); + return (msdosfs_create((struct vop_create_args *)ap)); break; default: - error = EINVAL; zfree(namei_zone, ap->a_cnp->cn_pnbuf); vput(ap->a_dvp); - break; + return (EINVAL); } - return error; + /* NOTREACHED */ } static int @@ -214,10 +232,13 @@ msdosfs_close(ap) { struct vnode *vp = ap->a_vp; struct denode *dep = VTODE(vp); + struct timespec ts; simple_lock(&vp->v_interlock); - if (vp->v_usecount > 1) - DE_TIMES(dep, &time); + if (vp->v_usecount > 1) { + TIMEVAL_TO_TIMESPEC(&time, &ts); + DETIMES(dep, &ts, &ts, &ts); + } simple_unlock(&vp->v_interlock); return 0; } @@ -309,9 +330,15 @@ msdosfs_getattr(ap) { u_int cn; struct denode *dep = VTODE(ap->a_vp); + struct msdosfsmount *pmp = dep->de_pmp; struct vattr *vap = ap->a_vap; + mode_t mode; + struct timespec ts; + u_long dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry); + u_long fileid; - DE_TIMES(dep, &time); + TIMEVAL_TO_TIMESPEC(&time, &ts); + DETIMES(dep, &ts, &ts, &ts); vap->va_fsid = dep->de_dev; /* * The following computation of the fileid must be the same as that @@ -319,41 +346,44 @@ msdosfs_getattr(ap) * doesn't work. */ if (dep->de_Attributes & ATTR_DIRECTORY) { - if ((cn = dep->de_StartCluster) == MSDOSFSROOT) - cn = 1; + fileid = cntobn(pmp, dep->de_StartCluster) * dirsperblk; + if (dep->de_StartCluster == MSDOSFSROOT) + fileid = 1; } else { - if ((cn = dep->de_dirclust) == MSDOSFSROOT) - cn = 1; - cn = (cn << 16) | (dep->de_diroffset & 0xffff); + fileid = cntobn(pmp, dep->de_dirclust) * dirsperblk; + if (dep->de_dirclust == MSDOSFSROOT) + fileid = roottobn(pmp, 0) * dirsperblk; + fileid += dep->de_diroffset / sizeof(struct direntry); } - vap->va_fileid = cn; - vap->va_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) | - ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH)); - vap->va_mode &= dep->de_pmp->pm_mask; - if (dep->de_Attributes & ATTR_DIRECTORY) - vap->va_mode |= S_IFDIR; + vap->va_fileid = fileid; + if ((dep->de_Attributes & ATTR_READONLY) == 0) + mode = S_IRWXU|S_IRWXG|S_IRWXO; + else + mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; + vap->va_mode = mode & pmp->pm_mask; + vap->va_uid = pmp->pm_uid; + vap->va_gid = pmp->pm_gid; vap->va_nlink = 1; - vap->va_gid = dep->de_pmp->pm_gid; - vap->va_uid = dep->de_pmp->pm_uid; vap->va_rdev = 0; vap->va_size = dep->de_FileSize; - dos2unixtime(dep->de_Date, dep->de_Time, &vap->va_atime); - vap->va_mtime = vap->va_atime; -#if 0 -#ifndef MSDOSFS_NODIRMOD - if (vap->va_mode & S_IFDIR) - TIMEVAL_TO_TIMESPEC(&time, &vap->va_mtime); -#endif -#endif - vap->va_ctime = vap->va_atime; - vap->va_flags = (dep->de_Attributes & ATTR_ARCHIVE) ? 0 : SF_ARCHIVED; + dos2unixtime(dep->de_MDate, dep->de_MTime, 0, &vap->va_mtime); + if (pmp->pm_flags & MSDOSFSMNT_LONGNAME) { + dos2unixtime(dep->de_ADate, 0, 0, &vap->va_atime); + dos2unixtime(dep->de_CDate, dep->de_CTime, dep->de_CHun, &vap->va_ctime); + } else { + vap->va_atime = vap->va_mtime; + vap->va_ctime = vap->va_mtime; + } + vap->va_flags = 0; + if ((dep->de_Attributes & ATTR_ARCHIVE) == 0) + vap->va_flags |= SF_ARCHIVED; vap->va_gen = 0; - vap->va_blocksize = dep->de_pmp->pm_bpcluster; - vap->va_bytes = (dep->de_FileSize + dep->de_pmp->pm_crbomask) & - ~(dep->de_pmp->pm_crbomask); + vap->va_blocksize = pmp->pm_bpcluster; + vap->va_bytes = + (dep->de_FileSize + pmp->pm_crbomask) & ~pmp->pm_crbomask; vap->va_type = ap->a_vp->v_type; vap->va_filerev = dep->de_modrev; - return 0; + return (0); } static int @@ -367,10 +397,16 @@ msdosfs_setattr(ap) { struct vnode *vp = ap->a_vp; struct denode *dep = VTODE(ap->a_vp); + struct msdosfsmount *pmp = dep->de_pmp; struct vattr *vap = ap->a_vap; struct ucred *cred = ap->a_cred; int error = 0; +#ifdef MSDOSFS_DEBUG + printf("msdosfs_setattr(): vp %p, vap %p, cred %p, p %p\n", + ap->a_vp, vap, cred, ap->a_p); +#endif + /* * Check for unsettable attributes. */ @@ -378,12 +414,21 @@ msdosfs_setattr(ap) (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { +#ifdef MSDOSFS_DEBUG + printf("msdosfs_setattr(): returning EINVAL\n"); + printf(" va_type %d, va_nlink %x, va_fsid %lx, va_fileid %lx\n", + vap->va_type, vap->va_nlink, vap->va_fsid, vap->va_fileid); + printf(" va_blocksize %lx, va_rdev %x, va_bytes %qx, va_gen %lx\n", + vap->va_blocksize, vap->va_rdev, vap->va_bytes, vap->va_gen); + printf(" va_uid %x, va_gid %x\n", + vap->va_uid, vap->va_gid); +#endif return (EINVAL); } if (vap->va_flags != VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); - if (cred->cr_uid != dep->de_pmp->pm_uid && + if (cred->cr_uid != pmp->pm_uid && (error = suser(cred, &ap->a_p->p_acflag))) return (error); /* @@ -411,19 +456,26 @@ msdosfs_setattr(ap) dep->de_flag |= DE_MODIFIED; } - if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (uid_t)VNOVAL) { + if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) { + uid_t uid; + gid_t gid; + if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); - if ((cred->cr_uid != dep->de_pmp->pm_uid || - vap->va_uid != dep->de_pmp->pm_uid || - (vap->va_gid != dep->de_pmp->pm_gid && - !groupmember(vap->va_gid, cred))) && + uid = vap->va_uid; + if (uid == (uid_t)VNOVAL) + uid = pmp->pm_uid; + gid = vap->va_gid; + if (gid == (gid_t)VNOVAL) + gid = pmp->pm_gid; + if ((cred->cr_uid != pmp->pm_uid || uid != pmp->pm_uid || + (gid != pmp->pm_gid && !groupmember(gid, cred))) && (error = suser(cred, &ap->a_p->p_acflag))) return error; - if (vap->va_uid != dep->de_pmp->pm_uid || - vap->va_gid != dep->de_pmp->pm_gid) + if (uid != pmp->pm_uid || gid != pmp->pm_gid) return EINVAL; } + if (vap->va_size != VNOVAL) { /* * Disallow write attempts on read-only file systems; @@ -443,41 +495,45 @@ msdosfs_setattr(ap) if (error) return error; } - if (vap->va_mtime.tv_sec != VNOVAL) { + if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); - if (cred->cr_uid != dep->de_pmp->pm_uid && + if (cred->cr_uid != pmp->pm_uid && (error = suser(cred, &ap->a_p->p_acflag)) && ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || - (error = VOP_ACCESS(vp, VWRITE, cred, ap->a_p)))) - return error; - dep->de_flag |= DE_UPDATE; - error = deupdat(dep, &vap->va_mtime, 1); - if (error) - return error; + (error = VOP_ACCESS(ap->a_vp, VWRITE, cred, ap->a_p)))) + return (error); + if (vp->v_type != VDIR) { + if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 && + vap->va_atime.tv_sec != VNOVAL) + unix2dostime(&vap->va_atime, &dep->de_ADate, NULL, NULL); + if (vap->va_mtime.tv_sec != VNOVAL) + unix2dostime(&vap->va_mtime, &dep->de_MDate, &dep->de_MTime, NULL); + dep->de_Attributes |= ATTR_ARCHIVE; + dep->de_flag |= DE_MODIFIED; + } } - /* * DOS files only have the ability to have their writability * attribute set, so we use the owner write bit to set the readonly * attribute. */ - error = 0; - if (vap->va_mode != (u_short) VNOVAL) { + if (vap->va_mode != (mode_t)VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); - if (cred->cr_uid != dep->de_pmp->pm_uid && + if (cred->cr_uid != pmp->pm_uid && (error = suser(cred, &ap->a_p->p_acflag))) - return error; - - /* We ignore the read and execute bits */ - if (vap->va_mode & VWRITE) - dep->de_Attributes &= ~ATTR_READONLY; - else - dep->de_Attributes |= ATTR_READONLY; - dep->de_flag |= DE_MODIFIED; + return (error); + if (vp->v_type != VDIR) { + /* We ignore the read and execute bits. */ + if (vap->va_mode & VWRITE) + dep->de_Attributes &= ~ATTR_READONLY; + else + dep->de_Attributes |= ATTR_READONLY; + dep->de_flag |= DE_MODIFIED; + } } - return error; + return (deupdat(dep, 1)); } static int @@ -491,11 +547,12 @@ msdosfs_read(ap) { int error = 0; int diff; + int blsize; int isadir; long n; long on; daddr_t lbn; - daddr_t rablock; + daddr_t rablock, rablock1; int rasize; struct buf *bp; struct vnode *vp = ap->a_vp; @@ -507,34 +564,33 @@ msdosfs_read(ap) * If they didn't ask for any data, then we are done. */ if (uio->uio_resid == 0) - return 0; + return (0); if (uio->uio_offset < 0) - return EINVAL; + return (EINVAL); isadir = dep->de_Attributes & ATTR_DIRECTORY; do { - lbn = uio->uio_offset >> pmp->pm_cnshift; + lbn = de_cluster(pmp, uio->uio_offset); on = uio->uio_offset & pmp->pm_crbomask; n = min((u_long) (pmp->pm_bpcluster - on), uio->uio_resid); diff = dep->de_FileSize - uio->uio_offset; if (diff <= 0) - return 0; - /* convert cluster # to block # if a directory */ - if (isadir) { - error = pcbmap(dep, lbn, &lbn, 0); - if (error) - return error; - } + return (0); if (diff < n) n = diff; + /* convert cluster # to block # if a directory */ + if (isadir) { + error = pcbmap(dep, lbn, &lbn, 0, &blsize); + if (error) + return (error); + } /* * If we are operating on a directory file then be sure to * do i/o with the vnode for the filesystem instead of the * vnode for the directory. */ if (isadir) { - error = bread(pmp->pm_devvp, lbn, pmp->pm_bpcluster, - NOCRED, &bp); + error = bread(pmp->pm_devvp, lbn, blsize, NOCRED, &bp); } else { rablock = lbn + 1; #ifdef PC98 @@ -545,36 +601,28 @@ msdosfs_read(ap) vp->v_flag |= 0x10000; #endif if (vp->v_lastr + 1 == lbn && - rablock * pmp->pm_bpcluster < dep->de_FileSize) { + de_cn2off(pmp, rablock) < dep->de_FileSize) { + rablock1 = de_cn2bn(pmp, rablock); rasize = pmp->pm_bpcluster; - error = breadn(vp, lbn, pmp->pm_bpcluster, - &rablock, &rasize, 1, - NOCRED, &bp); - } else { - error = bread(vp, lbn, pmp->pm_bpcluster, NOCRED, - &bp); - } + error = breadn(vp, de_cn2bn(pmp, lbn), + pmp->pm_bpcluster, &rablock1, &rasize, 1, + NOCRED, &bp); + } else + error = bread(vp, de_cn2bn(pmp, lbn), + pmp->pm_bpcluster, NOCRED, &bp); vp->v_lastr = lbn; } n = min(n, pmp->pm_bpcluster - bp->b_resid); if (error) { brelse(bp); - return error; + return (error); } error = uiomove(bp->b_data + on, (int) n, uio); - /* - * If we have read everything from this block or have read - * to end of file then we are done with this block. Mark - * it to say the buffer can be reused if need be. - */ -#if 0 - if (n + on == pmp->pm_bpcluster || - uio->uio_offset == dep->de_FileSize) - bp->b_flags |= B_AGE; -#endif + if (!isadir) + dep->de_flag |= DE_ACCESS; brelse(bp); } while (error == 0 && uio->uio_resid > 0 && n != 0); - return error; + return (error); } /* @@ -590,10 +638,9 @@ msdosfs_write(ap) } */ *ap; { int n; - int isadir; int croffset; int resid; - int osize; + u_long osize; int error = 0; u_long count; daddr_t bn, lastcn; @@ -606,60 +653,42 @@ msdosfs_write(ap) struct denode *dep = VTODE(vp); struct msdosfsmount *pmp = dep->de_pmp; struct ucred *cred = ap->a_cred; - struct timespec ts; #ifdef MSDOSFS_DEBUG - printf("msdosfs_write(vp %08x, uio %08x, ioflag %08x, cred %08x\n", - vp, uio, ioflag, cred); - printf("msdosfs_write(): diroff %d, dirclust %d, startcluster %d\n", - dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster); + printf("msdosfs_write(vp %p, uio %p, ioflag %x, cred %p\n", + vp, uio, ioflag, cred); + printf("msdosfs_write(): diroff %lu, dirclust %lu, startcluster %lu\n", + dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster); #endif switch (vp->v_type) { case VREG: if (ioflag & IO_APPEND) uio->uio_offset = dep->de_FileSize; - isadir = 0; thisvp = vp; break; - case VDIR: - if ((ioflag & IO_SYNC) == 0) - panic("msdosfs_write(): non-sync directory update"); - isadir = 1; - thisvp = pmp->pm_devvp; - break; - + return EISDIR; default: panic("msdosfs_write(): bad file type"); - break; } if (uio->uio_offset < 0) - return EINVAL; + return (EINVAL); if (uio->uio_resid == 0) - return 0; + return (0); /* * If they've exceeded their filesize limit, tell them about it. */ - if (vp->v_type == VREG && p && + if (p && ((uio->uio_offset + uio->uio_resid) > - p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) { + p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) { psignal(p, SIGXFSZ); - return EFBIG; + return (EFBIG); } - /* - * If attempting to write beyond the end of the root directory we - * stop that here because the root directory can not grow. - */ - if ((dep->de_Attributes & ATTR_DIRECTORY) && - dep->de_StartCluster == MSDOSFSROOT && - (uio->uio_offset + uio->uio_resid) > dep->de_FileSize) - return ENOSPC; - /* * If the offset we are starting the write at is beyond the end of * the file, then they've done a seek. Unix filesystems allow @@ -669,7 +698,7 @@ msdosfs_write(ap) if (uio->uio_offset > dep->de_FileSize) { error = deextend(dep, uio->uio_offset, cred); if (error) - return error; + return (error); } /* @@ -678,7 +707,6 @@ msdosfs_write(ap) resid = uio->uio_resid; osize = dep->de_FileSize; - #ifdef PC98 /* * 1024byte/sector support @@ -691,21 +719,17 @@ msdosfs_write(ap) * size ahead of the time to hopefully get a contiguous area. */ if (uio->uio_offset + resid > osize) { - count = de_clcount(pmp, uio->uio_offset + resid) - de_clcount(pmp, osize); - if ((error = extendfile(dep, count, NULL, NULL, 0)) - && (error != ENOSPC || (ioflag & IO_UNIT))) + count = de_clcount(pmp, uio->uio_offset + resid) - + de_clcount(pmp, osize); + error = extendfile(dep, count, NULL, NULL, 0); + if (error && (error != ENOSPC || (ioflag & IO_UNIT))) goto errexit; lastcn = dep->de_fc[FC_LASTFC].fc_frcn; } else lastcn = de_clcount(pmp, osize) - 1; do { - bn = de_blk(pmp, uio->uio_offset); - if (isadir) { - error = pcbmap(dep, bn, &bn, 0); - if (error) - break; - } else if (bn > lastcn) { + if (de_cluster(pmp, uio->uio_offset) > lastcn) { error = ENOSPC; break; } @@ -718,6 +742,7 @@ msdosfs_write(ap) vnode_pager_setsize(vp, dep->de_FileSize); } + bn = de_blk(pmp, uio->uio_offset); if ((uio->uio_offset & pmp->pm_crbomask) == 0 && (de_blk(pmp, uio->uio_offset + uio->uio_resid) > de_blk(pmp, uio->uio_offset) || uio->uio_offset + uio->uio_resid >= dep->de_FileSize)) { @@ -732,27 +757,28 @@ msdosfs_write(ap) * Do the bmap now, since pcbmap needs buffers * for the fat table. (see msdosfs_strategy) */ - if (!isadir) { - if (bp->b_blkno == bp->b_lblkno) { - error = pcbmap(dep, bp->b_lblkno, - &bp->b_blkno, 0); - if (error) - bp->b_blkno = -1; - } - if (bp->b_blkno == -1) { - brelse(bp); - if (!error) - error = EIO; /* XXX */ - break; - } + if (bp->b_blkno == bp->b_lblkno) { + error = pcbmap(dep, + de_bn2cn(pmp, bp->b_lblkno), + &bp->b_blkno, 0, 0); + if (error) + bp->b_blkno = -1; + } + if (bp->b_blkno == -1) { + brelse(bp); + if (!error) + error = EIO; /* XXX */ + break; } } else { /* * The block we need to write into exists, so read it in. */ error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp); - if (error) + if (error) { + brelse(bp); break; + } } /* @@ -774,9 +800,9 @@ msdosfs_write(ap) */ if (ioflag & IO_SYNC) (void) bwrite(bp); - else if (n + croffset == pmp->pm_bpcluster) { + else if (n + croffset == pmp->pm_bpcluster) bawrite(bp); - } else + else bdwrite(bp); dep->de_flag |= DE_UPDATE; } while (error == 0 && uio->uio_resid > 0); @@ -796,11 +822,9 @@ errexit: if (uio->uio_resid != resid) error = 0; } - } else if (ioflag & IO_SYNC) { - TIMEVAL_TO_TIMESPEC(&time, &ts); - error = deupdat(dep, &ts, 1); - } - return error; + } else if (ioflag & IO_SYNC) + error = deupdat(dep, 1); + return (error); } /* @@ -818,12 +842,9 @@ msdosfs_fsync(ap) struct proc *a_p; } */ *ap; { - register struct vnode *vp = ap->a_vp; - register struct buf *bp; - int wait = ap->a_waitfor == MNT_WAIT; - struct timespec ts; - struct buf *nbp; + struct vnode *vp = ap->a_vp; int s; + struct buf *bp, *nbp; /* * Flush all dirty buffers associated with a vnode. @@ -853,8 +874,7 @@ loop: } #endif splx(s); - TIMEVAL_TO_TIMESPEC(&time, &ts); - return deupdat(VTODE(vp), &ts, wait); + return (deupdat(VTODE(vp), ap->a_waitfor == MNT_WAIT)); } static int @@ -869,9 +889,12 @@ msdosfs_remove(ap) struct denode *dep = VTODE(ap->a_vp); struct denode *ddep = VTODE(ap->a_dvp); - error = removede(ddep,dep); + if (ap->a_vp->v_type == VDIR) + error = EPERM; + else + error = removede(ddep, dep); #ifdef MSDOSFS_DEBUG - printf("msdosfs_remove(), dep %08x, v_usecount %d\n", dep, ap->a_vp->v_usecount); + printf("msdosfs_remove(), dep %p, v_usecount %d\n", dep, ap->a_vp->v_usecount); #endif if (ddep == dep) vrele(ap->a_vp); @@ -879,7 +902,7 @@ msdosfs_remove(ap) vput(ap->a_vp); /* causes msdosfs_inactive() to be called * via vrele() */ vput(ap->a_dvp); - return error; + return (error); } /* @@ -962,22 +985,27 @@ msdosfs_rename(ap) struct componentname *a_tcnp; } */ *ap; { - u_char toname[11]; - int error; - int newparent = 0; - int sourceisadirectory = 0; - u_long cn; - daddr_t bn; + struct vnode *tdvp = ap->a_tdvp; + struct vnode *fvp = ap->a_fvp; + struct vnode *fdvp = ap->a_fdvp; struct vnode *tvp = ap->a_tvp; + struct componentname *tcnp = ap->a_tcnp; struct componentname *fcnp = ap->a_fcnp; struct proc *p = fcnp->cn_proc; + struct denode *ip, *xp, *dp, *zp; + u_char toname[11], oldname[11]; + u_long from_diroffset, to_diroffset; + u_char to_count; + int doingdirectory = 0, newparent = 0; + int error; + u_long cn; + daddr_t bn; struct denode *fddep; /* from file's parent directory */ struct denode *fdep; /* from file or directory */ struct denode *tddep; /* to file's parent directory */ struct denode *tdep; /* to file or directory */ struct msdosfsmount *pmp; struct direntry *dotdotp; - struct direntry *ep; struct buf *bp; fddep = VTODE(ap->a_fdvp); @@ -986,28 +1014,46 @@ msdosfs_rename(ap) tdep = tvp ? VTODE(tvp) : NULL; pmp = fddep->de_pmp; - /* Check for cross-device rename */ - if ((ap->a_fvp->v_mount != ap->a_tdvp->v_mount) || - (tvp && (ap->a_fvp->v_mount != tvp->v_mount))) { + pmp = VFSTOMSDOSFS(fdvp->v_mount); + +#ifdef DIAGNOSTIC + if ((tcnp->cn_flags & HASBUF) == 0 || + (fcnp->cn_flags & HASBUF) == 0) + panic("msdosfs_rename: no name"); +#endif + /* + * Check for cross-device rename. + */ + if ((fvp->v_mount != tdvp->v_mount) || + (tvp && (fvp->v_mount != tvp->v_mount))) { error = EXDEV; - goto bad; +abortit: + VOP_ABORTOP(tdvp, tcnp); + if (tdvp == tvp) + vrele(tdvp); + else + vput(tdvp); + if (tvp) + vput(tvp); + VOP_ABORTOP(fdvp, fcnp); + vrele(fdvp); + vrele(fvp); + return (error); } /* - * Convert the filename in tcnp into a dos filename. We copy this - * into the denode and directory entry for the destination - * file/directory. + * If source and dest are the same, do nothing. */ - unix2dosfn((u_char *) ap->a_tcnp->cn_nameptr, - toname, ap->a_tcnp->cn_namelen); + if (tvp == fvp) { + error = 0; + goto abortit; + } - /* - * At this point this is the lock state of the denodes: - * fddep referenced - * fdep referenced - * tddep locked - * tdep locked if it exists - */ + error = vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p); + if (error) + goto abortit; + dp = VTODE(fdvp); + ip = VTODE(fvp); /* * Be sure we are not renaming ".", "..", or an alias of ".". This @@ -1015,218 +1061,270 @@ msdosfs_rename(ap) * "ls" or "pwd" with the "." directory entry missing, and "cd .." * doesn't work if the ".." entry is missing. */ - if (fdep->de_Attributes & ATTR_DIRECTORY) { - if ((ap->a_fcnp->cn_namelen == 1 - && ap->a_fcnp->cn_nameptr[0] == '.') - || fddep == fdep - || (ap->a_fcnp->cn_flags | ap->a_tcnp->cn_flags) - & ISDOTDOT) { - VOP_ABORTOP(ap->a_tdvp, ap->a_tcnp); - vput(ap->a_tdvp); - if (tvp) - vput(tvp); - VOP_ABORTOP(ap->a_fdvp, ap->a_fcnp); - vrele(ap->a_fdvp); - vrele(ap->a_fvp); - return EINVAL; + if (ip->de_Attributes & ATTR_DIRECTORY) { + /* + * Avoid ".", "..", and aliases of "." for obvious reasons. + */ + if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') || + dp == ip || + (fcnp->cn_flags & ISDOTDOT) || + (tcnp->cn_flags & ISDOTDOT) || + (ip->de_flag & DE_RENAME)) { + VOP_UNLOCK(fvp, 0, p); + error = EINVAL; + goto abortit; } - sourceisadirectory = 1; + ip->de_flag |= DE_RENAME; + doingdirectory++; } /* - * If we are renaming a directory, and the directory is being moved - * to another directory, then we must be sure the destination - * directory is not in the subtree of the source directory. This - * could orphan everything under the source directory. - * doscheckpath() unlocks the destination's parent directory so we - * must look it up again to relock it. + * When the target exists, both the directory + * and target vnodes are returned locked. */ - if (fddep->de_StartCluster != tddep->de_StartCluster) + dp = VTODE(tdvp); + xp = tvp ? VTODE(tvp) : NULL; + /* + * Remember direntry place to use for destination + */ + to_diroffset = dp->de_fndoffset; + to_count = dp->de_fndcnt; + + /* + * If ".." must be changed (ie the directory gets a new + * parent) then the source directory must not be in the + * directory heirarchy above the target, as this would + * orphan everything below the source directory. Also + * the user must have write permission in the source so + * as to be able to change "..". We must repeat the call + * to namei, as the parent directory is unlocked by the + * call to doscheckpath(). + */ + error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc); + VOP_UNLOCK(fvp, 0, p); + if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster) newparent = 1; - if (sourceisadirectory && newparent) { - if (tdep) { - vput(ap->a_tvp); - tdep = NULL; - } - /* doscheckpath() vput()'s tddep */ - error = doscheckpath(fdep, tddep); - tddep = NULL; - if (error) + vrele(fdvp); + if (doingdirectory && newparent) { + if (error) /* write access check above */ goto bad; - if ((ap->a_tcnp->cn_flags & SAVESTART) == 0) - panic("msdosfs_rename(): lost to startdir"); - error = relookup(ap->a_tdvp, &tvp, ap->a_tcnp); + if (xp != NULL) + vput(tvp); + /* + * doscheckpath() vput()'s dp, + * so we have to do a relookup afterwards + */ + error = doscheckpath(ip, dp); if (error) - goto bad; - tddep = VTODE(ap->a_tdvp); - tdep = tvp ? VTODE(tvp) : NULL; + goto out; + if ((tcnp->cn_flags & SAVESTART) == 0) + panic("msdosfs_rename: lost to startdir"); + error = relookup(tdvp, &tvp, tcnp); + if (error) + goto out; + dp = VTODE(tdvp); + xp = tvp ? VTODE(tvp) : NULL; } - /* - * If the destination exists, then be sure its type (file or dir) - * matches that of the source. And, if it is a directory make sure - * it is empty. Then delete the destination. - */ - if (tdep) { - if (tdep->de_Attributes & ATTR_DIRECTORY) { - if (!sourceisadirectory) { - error = ENOTDIR; - goto bad; - } - if (!dosdirempty(tdep)) { + if (xp != NULL) { + /* + * Target must be empty if a directory and have no links + * to it. Also, ensure source and target are compatible + * (both directories, or both not directories). + */ + if (xp->de_Attributes & ATTR_DIRECTORY) { + if (!dosdirempty(xp)) { error = ENOTEMPTY; goto bad; } - cache_purge(DETOV(tddep)); - } else { /* destination is file */ - if (sourceisadirectory) { - error = EISDIR; + if (!doingdirectory) { + error = ENOTDIR; goto bad; } + cache_purge(tdvp); + } else if (doingdirectory) { + error = EISDIR; + goto bad; } - error = removede(tddep,tdep); + error = removede(dp, xp); if (error) goto bad; - vput(ap->a_tvp); - tdep = NULL; + vput(tvp); + xp = NULL; } /* - * If the source and destination are in the same directory then - * just read in the directory entry, change the name in the - * directory entry and write it back to disk. + * Convert the filename in tcnp into a dos filename. We copy this + * into the denode and directory entry for the destination + * file/directory. */ - if (newparent == 0) { - /* tddep and fddep point to the same denode here */ - vn_lock(ap->a_fvp, LK_EXCLUSIVE, p); /* ap->a_fdvp is already locked */ - error = readep(fddep->de_pmp, fdep->de_dirclust, - fdep->de_diroffset, &bp, &ep); - if (error) { - VOP_UNLOCK(ap->a_fvp, 0, p); - goto bad; - } - bcopy(toname, ep->deName, 11); - error = bwrite(bp); - if (error) { - VOP_UNLOCK(ap->a_fvp, 0, p); - goto bad; - } - bcopy(toname, fdep->de_Name, 11); /* update denode */ + error = uniqdosname(VTODE(tdvp), tcnp, toname); + if (error) + goto abortit; + + /* + * Since from wasn't locked at various places above, + * have to do a relookup here. + */ + fcnp->cn_flags &= ~MODMASK; + fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; + if ((fcnp->cn_flags & SAVESTART) == 0) + panic("msdosfs_rename: lost from startdir"); + if (!newparent) + VOP_UNLOCK(tdvp, 0, p); + (void) relookup(fdvp, &fvp, fcnp); + if (fvp == NULL) { /* - * fdep locked fddep and tddep point to the same denode - * which is locked tdep is NULL + * From name has disappeared. */ + if (doingdirectory) + panic("rename: lost dir entry"); + vrele(ap->a_fvp); + if (newparent) + VOP_UNLOCK(tdvp, 0, p); + vrele(tdvp); + return 0; + } + xp = VTODE(fvp); + zp = VTODE(fdvp); + from_diroffset = zp->de_fndoffset; + + /* + * Ensure that the directory entry still exists and has not + * changed till now. If the source is a file the entry may + * have been unlinked or renamed. In either case there is + * no further work to be done. If the source is a directory + * then it cannot have been rmdir'ed or renamed; this is + * prohibited by the DE_RENAME flag. + */ + if (xp != ip) { + if (doingdirectory) + panic("rename: lost dir entry"); + vrele(ap->a_fvp); + VOP_UNLOCK(fvp, 0, p); + if (newparent) + VOP_UNLOCK(fdvp, 0, p); + xp = NULL; } else { - u_long dirsize = 0L; + vrele(fvp); + xp = NULL; /* - * If the source and destination are in different - * directories, then mark the entry in the source directory - * as deleted and write a new entry in the destination - * directory. Then move the denode to the correct hash + * First write a new entry in the destination + * directory and mark the entry in the source directory + * as deleted. Then move the denode to the correct hash * chain for its new location in the filesystem. And, if * we moved a directory, then update its .. entry to point - * to the new parent directory. If we moved a directory - * will also insure that the directory entry on disk has a - * filesize of zero. + * to the new parent directory. */ - vn_lock(ap->a_fvp, LK_EXCLUSIVE, p); - bcopy(toname, fdep->de_Name, 11); /* update denode */ - if (fdep->de_Attributes & ATTR_DIRECTORY) { - dirsize = fdep->de_FileSize; - fdep->de_FileSize = 0; - } - error = createde(fdep, tddep, (struct denode **) 0); - if (fdep->de_Attributes & ATTR_DIRECTORY) { - fdep->de_FileSize = dirsize; - } + bcopy(ip->de_Name, oldname, 11); + bcopy(toname, ip->de_Name, 11); /* update denode */ + dp->de_fndoffset = to_diroffset; + dp->de_fndcnt = to_count; + error = createde(ip, dp, (struct denode **)0, tcnp); if (error) { - /* should put back filename */ - VOP_UNLOCK(ap->a_fvp, 0, p); + bcopy(oldname, ip->de_Name, 11); + if (newparent) + VOP_UNLOCK(fdvp, 0, p); + VOP_UNLOCK(fvp, 0, p); goto bad; } - vn_lock(ap->a_fdvp, LK_EXCLUSIVE, p); - error = readep(fddep->de_pmp, fddep->de_fndclust, - fddep->de_fndoffset, &bp, &ep); + ip->de_refcnt++; + zp->de_fndoffset = from_diroffset; + error = removede(zp, ip); if (error) { - VOP_UNLOCK(ap->a_fvp, 0, p); - VOP_UNLOCK(ap->a_fdvp, 0, p); + /* XXX should really panic here, fs is corrupt */ + if (newparent) + VOP_UNLOCK(fdvp, 0, p); + VOP_UNLOCK(fvp, 0, p); goto bad; } - ep->deName[0] = SLOT_DELETED; - error = bwrite(bp); - if (error) { - VOP_UNLOCK(ap->a_fvp, 0, p); - VOP_UNLOCK(ap->a_fdvp, 0, p); - goto bad; + if (!doingdirectory) { + error = pcbmap(dp, de_cluster(pmp, to_diroffset), 0, + &ip->de_dirclust, 0); + if (error) { + /* XXX should really panic here, fs is corrupt */ + if (newparent) + VOP_UNLOCK(fdvp, 0, p); + VOP_UNLOCK(fvp, 0, p); + goto bad; + } + if (ip->de_dirclust != MSDOSFSROOT) + ip->de_diroffset = to_diroffset & pmp->pm_crbomask; } - if (!sourceisadirectory) { - fdep->de_dirclust = tddep->de_fndclust; - fdep->de_diroffset = tddep->de_fndoffset; - reinsert(fdep); - } - VOP_UNLOCK(ap->a_fdvp, 0, p); + reinsert(ip); + if (newparent) + VOP_UNLOCK(fdvp, 0, p); } - /* fdep is still locked here */ /* * If we moved a directory to a new parent directory, then we must * fixup the ".." entry in the moved directory. */ - if (sourceisadirectory && newparent) { - cn = fdep->de_StartCluster; + if (doingdirectory && newparent) { + cn = ip->de_StartCluster; if (cn == MSDOSFSROOT) { /* this should never happen */ panic("msdosfs_rename(): updating .. in root directory?"); - } else { + } else bn = cntobn(pmp, cn); - } error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp); if (error) { - /* should really panic here, fs is corrupt */ - VOP_UNLOCK(ap->a_fvp, 0, p); + /* XXX should really panic here, fs is corrupt */ + brelse(bp); + VOP_UNLOCK(fvp, 0, p); goto bad; } - dotdotp = (struct direntry *) bp->b_data + 1; - putushort(dotdotp->deStartCluster, tddep->de_StartCluster); + dotdotp = (struct direntry *)bp->b_data + 1; + putushort(dotdotp->deStartCluster, dp->de_StartCluster); + if (FAT32(pmp)) + putushort(dotdotp->deHighClust, dp->de_StartCluster >> 16); error = bwrite(bp); - VOP_UNLOCK(ap->a_fvp, 0, p); if (error) { - /* should really panic here, fs is corrupt */ + /* XXX should really panic here, fs is corrupt */ + VOP_UNLOCK(fvp, 0, p); goto bad; } - } else - VOP_UNLOCK(ap->a_fvp, 0, p); -bad: ; - vrele(DETOV(fdep)); - vrele(DETOV(fddep)); - if (tdep) - vput(DETOV(tdep)); - if (tddep) - vput(DETOV(tddep)); - return error; + } + + VOP_UNLOCK(fvp, 0, p); +bad: + if (xp) + vput(tvp); + vput(tdvp); +out: + ip->de_flag &= ~DE_RENAME; + vrele(fdvp); + vrele(fvp); + return (error); + } static struct { struct direntry dot; struct direntry dotdot; -} dosdirtemplate = { - { - ". ", " ", /* the . entry */ - ATTR_DIRECTORY, /* file attribute */ - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* resevered */ - {210, 4}, {210, 4}, /* time and date */ - {0, 0}, /* startcluster */ - {0, 0, 0, 0}, /* filesize */ - },{ - ".. ", " ", /* the .. entry */ - ATTR_DIRECTORY, /* file attribute */ - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* resevered */ - {210, 4}, {210, 4}, /* time and date */ - {0, 0}, /* startcluster */ - {0, 0, 0, 0}, /* filesize */ - } +} dosdirtemplate = { + { ". ", " ", /* the . entry */ + ATTR_DIRECTORY, /* file attribute */ + 0, /* reserved */ + 0, { 0, 0 }, { 0, 0 }, /* create time & date */ + { 0, 0 }, /* access date */ + { 0, 0 }, /* high bits of start cluster */ + { 210, 4 }, { 210, 4 }, /* modify time & date */ + { 0, 0 }, /* startcluster */ + { 0, 0, 0, 0 } /* filesize */ + }, + { ".. ", " ", /* the .. entry */ + ATTR_DIRECTORY, /* file attribute */ + 0, /* reserved */ + 0, { 0, 0 }, { 0, 0 }, /* create time & date */ + { 0, 0 }, /* access date */ + { 0, 0 }, /* high bits of start cluster */ + { 210, 4 }, { 210, 4 }, /* modify time & date */ + { 0, 0 }, /* startcluster */ + { 0, 0, 0, 0 } /* filesize */ + } }; static int @@ -1238,42 +1336,41 @@ msdosfs_mkdir(ap) struct vattr *a_vap; } */ *ap; { - int bn; - int error; - u_long newcluster; - struct denode *pdep; - struct denode *ndep; - struct direntry *denp; + struct componentname *cnp = ap->a_cnp; struct denode ndirent; - struct msdosfsmount *pmp; + struct denode *dep; + struct denode *pdep = VTODE(ap->a_dvp); + int error; + int bn; + u_long newcluster, pcl; + struct direntry *denp; + struct msdosfsmount *pmp = pdep->de_pmp; struct buf *bp; struct timespec ts; - u_short dDate, dTime; - - pdep = VTODE(ap->a_dvp); /* * If this is the root directory and there is no space left we * can't do anything. This is because the root directory can not * change size. */ - if (pdep->de_StartCluster == MSDOSFSROOT && pdep->de_fndclust == (u_long)-1) { - zfree(namei_zone, ap->a_cnp->cn_pnbuf); - vput(ap->a_dvp); - return ENOSPC; + if (pdep->de_StartCluster == MSDOSFSROOT + && pdep->de_fndoffset >= pdep->de_FileSize) { + error = ENOSPC; + goto bad2; } - pmp = pdep->de_pmp; - /* * Allocate a cluster to hold the about to be created directory. */ error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL); - if (error) { - zfree(namei_zone, ap->a_cnp->cn_pnbuf); - vput(ap->a_dvp); - return error; - } + if (error) + goto bad2; + + bzero(&ndirent, sizeof(ndirent)); + ndirent.de_pmp = pmp; + ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; + TIMEVAL_TO_TIMESPEC(&time, &ts); + DETIMES(&ndirent, &ts, &ts, &ts); /* * Now fill the cluster with the "." and ".." entries. And write @@ -1285,50 +1382,66 @@ msdosfs_mkdir(ap) bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0); bzero(bp->b_data, pmp->pm_bpcluster); bcopy(&dosdirtemplate, bp->b_data, sizeof dosdirtemplate); - denp = (struct direntry *) bp->b_data; - putushort(denp->deStartCluster, newcluster); - TIMEVAL_TO_TIMESPEC(&time, &ts); - unix2dostime(&ts, &dDate, &dTime); - putushort(denp->deDate, dDate); - putushort(denp->deTime, dTime); - denp++; - putushort(denp->deStartCluster, pdep->de_StartCluster); - putushort(denp->deDate, dDate); - putushort(denp->deTime, dTime); - error = bwrite(bp); - if (error) { - clusterfree(pmp, newcluster, NULL); - zfree(namei_zone, ap->a_cnp->cn_pnbuf); - vput(ap->a_dvp); - return error; + denp = (struct direntry *)bp->b_data; + putushort(denp[0].deStartCluster, newcluster); + putushort(denp[0].deCDate, ndirent.de_CDate); + putushort(denp[0].deCTime, ndirent.de_CTime); + denp[0].deCHundredth = ndirent.de_CHun; + putushort(denp[0].deADate, ndirent.de_ADate); + putushort(denp[0].deMDate, ndirent.de_MDate); + putushort(denp[0].deMTime, ndirent.de_MTime); + pcl = pdep->de_StartCluster; + if (FAT32(pmp) && pcl == pmp->pm_rootdirblk) + pcl = 0; + putushort(denp[1].deStartCluster, pcl); + putushort(denp[1].deCDate, ndirent.de_CDate); + putushort(denp[1].deCTime, ndirent.de_CTime); + denp[1].deCHundredth = ndirent.de_CHun; + putushort(denp[1].deADate, ndirent.de_ADate); + putushort(denp[1].deMDate, ndirent.de_MDate); + putushort(denp[1].deMTime, ndirent.de_MTime); + if (FAT32(pmp)) { + putushort(denp[0].deHighClust, newcluster >> 16); + putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16); } + error = bwrite(bp); + if (error) + goto bad; + /* * Now build up a directory entry pointing to the newly allocated * cluster. This will be written to an empty slot in the parent * directory. */ - ndep = &ndirent; - bzero(ndep, sizeof(*ndep)); - unix2dosfn((u_char *)ap->a_cnp->cn_nameptr, - ndep->de_Name, ap->a_cnp->cn_namelen); - TIMEVAL_TO_TIMESPEC(&time, &ts); - unix2dostime(&ts, &ndep->de_Date, &ndep->de_Time); - ndep->de_StartCluster = newcluster; - ndep->de_Attributes = ATTR_DIRECTORY; - - error = createde(ndep, pdep, &ndep); - if (error) { - clusterfree(pmp, newcluster, NULL); - } else { - *ap->a_vpp = DETOV(ndep); - } - zfree(namei_zone, ap->a_cnp->cn_pnbuf); -#ifdef MSDOSFS_DEBUG - printf("msdosfs_mkdir(): vput(%08x)\n", ap->a_dvp); +#ifdef DIAGNOSTIC + if ((cnp->cn_flags & HASBUF) == 0) + panic("msdosfs_mkdir: no name"); #endif + error = uniqdosname(pdep, cnp, ndirent.de_Name); + if (error) + goto bad; + + ndirent.de_Attributes = ATTR_DIRECTORY; + ndirent.de_StartCluster = newcluster; + ndirent.de_FileSize = 0; + ndirent.de_dev = pdep->de_dev; + ndirent.de_devvp = pdep->de_devvp; + error = createde(&ndirent, pdep, &dep, cnp); + if (error) + goto bad; + if ((cnp->cn_flags & SAVESTART) == 0) + zfree(namei_zone, cnp->cn_pnbuf); vput(ap->a_dvp); - return error; + *ap->a_vpp = DETOV(dep); + return (0); + +bad: + clusterfree(pmp, newcluster, NULL); +bad2: + zfree(namei_zone, cnp->cn_pnbuf); + vput(ap->a_dvp); + return (error); } static int @@ -1339,21 +1452,27 @@ msdosfs_rmdir(ap) struct componentname *a_cnp; } */ *ap; { - struct denode *ddep; - struct denode *dep; - int error = 0; - - ddep = VTODE(ap->a_dvp); /* parent dir of dir to delete */ - dep = VTODE(ap->a_vp);/* directory to delete */ + register struct vnode *vp = ap->a_vp; + register struct vnode *dvp = ap->a_dvp; + register struct componentname *cnp = ap->a_cnp; + register struct denode *ip, *dp; + int error; + + ip = VTODE(vp); + dp = VTODE(dvp); /* - * Be sure the directory being deleted is empty. + * Verify the directory is empty (and valid). + * (Rmdir ".." won't be valid since + * ".." will contain a reference to + * the current directory and thus be + * non-empty.) */ - if (dosdirempty(dep) == 0) { + error = 0; + if (!dosdirempty(ip) || ip->de_flag & DE_RENAME) { error = ENOTEMPTY; goto out; } - /* * Delete the entry from the directory. For dos filesystems this * gets rid of the directory entry on disk, the in memory copy @@ -1362,30 +1481,27 @@ msdosfs_rmdir(ap) * up access and eventually msdosfs_reclaim() will be called which * will remove it from the denode cache. */ - error = removede(ddep,dep); + error = removede(dp, ip); if (error) goto out; - /* * This is where we decrement the link count in the parent * directory. Since dos filesystems don't do this we just purge * the name cache and let go of the parent directory denode. */ - cache_purge(DETOV(ddep)); - vput(ap->a_dvp); - ap->a_dvp = NULL; - + cache_purge(dvp); + vput(dvp); + dvp = NULL; /* * Truncate the directory that is being deleted. */ - error = detrunc(dep, (u_long) 0, IO_SYNC, NOCRED, NULL); - cache_purge(DETOV(dep)); - -out: ; - if (ap->a_dvp) - vput(ap->a_dvp); - vput(ap->a_vp); - return error; + error = detrunc(ip, (u_long)0, IO_SYNC, cnp->cn_cred, cnp->cn_proc); + cache_purge(vp); +out: + if (dvp) + vput(dvp); + vput(vp); + return (error); } /* @@ -1402,39 +1518,11 @@ msdosfs_symlink(ap) } */ *ap; { zfree(namei_zone, ap->a_cnp->cn_pnbuf); + /* VOP_ABORTOP(ap->a_dvp, ap->a_cnp); ??? */ vput(ap->a_dvp); - return EINVAL; + return (EOPNOTSUPP); } -/* - * Dummy dirents to simulate the "." and ".." entries of the root directory - * in a dos filesystem. Dos doesn't provide these. Note that each entry - * must be the same size as a dos directory entry (32 bytes). - */ -static struct dos_dirent { - u_long d_fileno; - u_short d_reclen; - u_char d_type; - u_char d_namlen; - u_char d_name[24]; -} rootdots[2] = { - - { - 1, /* d_fileno */ - sizeof(struct direntry), /* d_reclen */ - DT_DIR, /* d_type */ - 1, /* d_namlen */ - "." /* d_name */ - }, - { - 1, /* d_fileno */ - sizeof(struct direntry), /* d_reclen */ - DT_DIR, /* d_type */ - 2, /* d_namlen */ - ".." /* d_name */ - } -}; - static int msdosfs_readdir(ap) struct vop_readdir_args /* { @@ -1448,30 +1536,30 @@ msdosfs_readdir(ap) { int error = 0; int diff; - char pushout; long n; + int blsize; long on; long lost; long count; u_long cn; u_long fileno; + u_long dirsperblk; long bias = 0; - daddr_t bn; - daddr_t lbn; + daddr_t bn, lbn; struct buf *bp; struct denode *dep = VTODE(ap->a_vp); struct msdosfsmount *pmp = dep->de_pmp; struct direntry *dentp; - struct dirent *prev; - struct dirent *crnt; - u_char dirbuf[512]; /* holds converted dos directories */ + struct dirent dirbuf; struct uio *uio = ap->a_uio; - off_t off; + u_long *cookies = NULL; int ncookies = 0; + off_t offset, off; + int chksum = -1; #ifdef MSDOSFS_DEBUG - printf("msdosfs_readdir(): vp %08x, uio %08x, cred %08x, eofflagp %08x\n", - ap->a_vp, uio, ap->a_cred, ap->a_eofflag); + printf("msdosfs_readdir(): vp %p, uio %p, cred %p, eofflagp %p\n", + ap->a_vp, uio, ap->a_cred, ap->a_eofflag); #endif /* @@ -1481,7 +1569,12 @@ msdosfs_readdir(ap) * So, fail attempts to readdir() on a plain file. */ if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) - return ENOTDIR; + return (ENOTDIR); + + /* + * To be safe, initialize dirbuf + */ + bzero(dirbuf.d_name, sizeof(dirbuf.d_name)); /* * If the user buffer is smaller than the size of one dos directory @@ -1489,13 +1582,22 @@ msdosfs_readdir(ap) * directory entry, then we fail the read. */ count = uio->uio_resid & ~(sizeof(struct direntry) - 1); - lost = uio->uio_resid - count; + offset = uio->uio_offset; if (count < sizeof(struct direntry) || - (uio->uio_offset & (sizeof(struct direntry) - 1))) - return EINVAL; + (offset & (sizeof(struct direntry) - 1))) + return (EINVAL); + lost = uio->uio_resid - count; uio->uio_resid = count; - uio->uio_iov->iov_len = count; - off = uio->uio_offset; + + if (ap->a_ncookies) { + ncookies = uio->uio_resid / 16; + MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, + M_WAITOK); + *ap->a_cookies = cookies; + *ap->a_ncookies = ncookies; + } + + dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry); /* * If they are reading from the root directory then, we simulate @@ -1504,194 +1606,184 @@ msdosfs_readdir(ap) * simulate these entries. By this I mean that at file offset 64 we * read the first entry in the root directory that lives on disk. */ - if (dep->de_StartCluster == MSDOSFSROOT) { - /* - * printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n", - * uio->uio_offset); - */ + if (dep->de_StartCluster == MSDOSFSROOT + || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) { +#if 0 + printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n", + offset); +#endif bias = 2 * sizeof(struct direntry); - if (uio->uio_offset < 2 * sizeof(struct direntry)) { - if (uio->uio_offset - && uio->uio_offset != sizeof(struct direntry)) { - error = EINVAL; - goto out; + if (offset < bias) { + for (n = (int)offset / sizeof(struct direntry); + n < 2; n++) { + if (FAT32(pmp)) + dirbuf.d_fileno = cntobn(pmp, + pmp->pm_rootdirblk) + * dirsperblk; + else + dirbuf.d_fileno = 1; + dirbuf.d_type = DT_DIR; + switch (n) { + case 0: + dirbuf.d_namlen = 1; + strcpy(dirbuf.d_name, "."); + break; + case 1: + dirbuf.d_namlen = 2; + strcpy(dirbuf.d_name, ".."); + break; + } + dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf); + if (uio->uio_resid < dirbuf.d_reclen) + goto out; + error = uiomove((caddr_t) &dirbuf, + dirbuf.d_reclen, uio); + if (error) + goto out; + if (cookies) { + *cookies++ = offset; + if (--ncookies <= 0) + goto out; + } + offset += sizeof(struct direntry); } - n = 1; - if (!uio->uio_offset) { - n = 2; - ncookies++; - } - ncookies++; - error = uiomove((char *) rootdots + uio->uio_offset, - n * sizeof(struct direntry), uio); } } - while (!error && uio->uio_resid > 0) { - lbn = (uio->uio_offset - bias) >> pmp->pm_cnshift; - on = (uio->uio_offset - bias) & pmp->pm_crbomask; - n = min((u_long) (pmp->pm_bpcluster - on), uio->uio_resid); - diff = dep->de_FileSize - (uio->uio_offset - bias); + + off = offset; + while (uio->uio_resid > 0) { + lbn = de_cluster(pmp, offset - bias); + on = (offset - bias) & pmp->pm_crbomask; + n = min(pmp->pm_bpcluster - on, uio->uio_resid); + diff = dep->de_FileSize - (offset - bias); if (diff <= 0) break; - if (diff < n) - n = diff; - error = pcbmap(dep, lbn, &bn, &cn); + n = min(n, diff); + error = pcbmap(dep, lbn, &bn, &cn, &blsize); if (error) break; - error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp); - n = min(n, pmp->pm_bpcluster - bp->b_resid); + error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); if (error) { brelse(bp); - return error; + return (error); } + n = min(n, blsize - bp->b_resid); /* - * code to convert from dos directory entries to ufs - * directory entries + * Convert from dos directory entries to fs-independent + * directory entries. */ - pushout = 0; - dentp = (struct direntry *)(bp->b_data + on); - prev = 0; - crnt = (struct dirent *) dirbuf; - while ((char *) dentp < bp->b_data + on + n) { - /* - * printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n", - * dentp, prev, crnt, dentp->deName[0], dentp->deAttributes); - */ - /* - * If we have an empty entry or a slot from a - * deleted file, or a volume label entry just - * concatenate its space onto the end of the - * previous entry or, manufacture an empty entry if - * there is no previous entry. - */ - if (dentp->deName[0] == SLOT_EMPTY || - dentp->deName[0] == SLOT_DELETED || - (dentp->deAttributes & ATTR_VOLUME)) { - if (prev) { - prev->d_reclen += sizeof(struct direntry); - } else { - prev = crnt; - prev->d_fileno = 0; - prev->d_reclen = sizeof(struct direntry); - prev->d_type = DT_UNKNOWN; - prev->d_namlen = 0; - prev->d_name[0] = 0; - ncookies++; - } - } else { - /* - * this computation of d_fileno must match - * the computation of va_fileid in - * msdosfs_getattr - */ - if (dentp->deAttributes & ATTR_DIRECTORY) { - /* if this is the root directory */ - fileno = getushort(dentp->deStartCluster); - if (fileno == MSDOSFSROOT) - fileno = 1; - } else { - /* - * if the file's dirent lives in - * root dir - */ - if ((fileno = cn) == MSDOSFSROOT) - fileno = 1; - fileno = (fileno << 16) | - ((dentp - (struct direntry *) bp->b_data) & 0xffff); - } - crnt->d_fileno = fileno; - crnt->d_reclen = sizeof(struct direntry); - crnt->d_type = (dentp->deAttributes & ATTR_DIRECTORY) - ? DT_DIR : DT_REG; - crnt->d_namlen = dos2unixfn(dentp->deName, - (u_char *)crnt->d_name); - /* - * printf("readdir: file %s, fileno %08x, attr %02x, start %08x\n", - * crnt->d_name, crnt->d_fileno, dentp->deAttributes, - * dentp->deStartCluster); - */ - prev = crnt; - ncookies++; - } - dentp++; - - crnt = (struct dirent *) ((char *) crnt + sizeof(struct direntry)); - pushout = 1; - - /* - * If our intermediate buffer is full then copy its - * contents to user space. I would just use the - * buffer the buf header points to but, I'm afraid - * that when we brelse() it someone else might find - * it in the cache and think its contents are - * valid. Maybe there is a way to invalidate the - * buffer before brelse()'ing it. - */ - if ((u_char *) crnt >= &dirbuf[sizeof dirbuf]) { - pushout = 0; - error = uiomove(dirbuf, sizeof(dirbuf), uio); - if (error) - break; - prev = 0; - crnt = (struct dirent *) dirbuf; - } - } - if (pushout) { - pushout = 0; - error = uiomove(dirbuf, (char *) crnt - (char *) dirbuf, - uio); - } - + for (dentp = (struct direntry *)(bp->b_data + on); + (char *)dentp < bp->b_data + on + n; + dentp++, offset += sizeof(struct direntry)) { #if 0 - /* - * If we have read everything from this block or have read - * to end of file then we are done with this block. Mark - * it to say the buffer can be reused if need be. - */ - if (n + on == pmp->pm_bpcluster || - (uio->uio_offset - bias) == dep->de_FileSize) - bp->b_flags |= B_AGE; -#endif /* if 0 */ - brelse(bp); - if (n == 0) - break; - } -out: ; - uio->uio_resid += lost; - if (!error && ap->a_ncookies != NULL) { - struct dirent* dpStart; - struct dirent* dpEnd; - struct dirent* dp; - u_long *cookies; - u_long *cookiep; + printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n", + dentp, prev, crnt, dentp->deName[0], dentp->deAttributes); +#endif + /* + * If this is an unused entry, we can stop. + */ + if (dentp->deName[0] == SLOT_EMPTY) { + brelse(bp); + goto out; + } + /* + * Skip deleted entries. + */ + if (dentp->deName[0] == SLOT_DELETED) { + chksum = -1; + continue; + } - if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) - panic("msdosfs_readdir: unexpected uio from NFS server"); - dpStart = (struct dirent *) - (uio->uio_iov->iov_base - (uio->uio_offset - off)); - dpEnd = (struct dirent *) uio->uio_iov->iov_base; - cookies = malloc(ncookies * sizeof(*cookies), M_TEMP, M_WAITOK); - for (dp = dpStart, cookiep = cookies; - dp < dpEnd; - dp = (struct dirent *)((caddr_t) dp + dp->d_reclen)) { - off += dp->d_reclen; - *cookiep++ = (u_long) off; + /* + * Handle Win95 long directory entries + */ + if (dentp->deAttributes == ATTR_WIN95) { + if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) + continue; + chksum = win2unixfn((struct winentry *)dentp, &dirbuf, chksum); + continue; + } + + /* + * Skip volume labels + */ + if (dentp->deAttributes & ATTR_VOLUME) { + chksum = -1; + continue; + } + /* + * This computation of d_fileno must match + * the computation of va_fileid in + * msdosfs_getattr. + */ + if (dentp->deAttributes & ATTR_DIRECTORY) { + fileno = getushort(dentp->deStartCluster); + if (FAT32(pmp)) + fileno |= getushort(dentp->deHighClust) << 16; + /* if this is the root directory */ + if (fileno == MSDOSFSROOT) + if (FAT32(pmp)) + fileno = cntobn(pmp, + pmp->pm_rootdirblk) + * dirsperblk; + else + fileno = 1; + else + fileno = cntobn(pmp, fileno) * dirsperblk; + dirbuf.d_fileno = fileno; + dirbuf.d_type = DT_DIR; + } else { + dirbuf.d_fileno = offset / sizeof(struct direntry); + dirbuf.d_type = DT_REG; + } + if (chksum != winChksum(dentp->deName)) + dirbuf.d_namlen = dos2unixfn(dentp->deName, + (u_char *)dirbuf.d_name, + pmp->pm_flags & MSDOSFSMNT_SHORTNAME); + else + dirbuf.d_name[dirbuf.d_namlen] = 0; + chksum = -1; + dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf); + if (uio->uio_resid < dirbuf.d_reclen) { + brelse(bp); + goto out; + } + error = uiomove((caddr_t) &dirbuf, + dirbuf.d_reclen, uio); + if (error) { + brelse(bp); + goto out; + } + if (cookies) { + *cookies++ = off; + off = offset + sizeof(struct direntry); + if (--ncookies <= 0) { + brelse(bp); + goto out; + } + } } - *ap->a_ncookies = ncookies; - *ap->a_cookies = cookies; + brelse(bp); } +out: + /* Subtract unused cookies */ + if (ap->a_ncookies) + *ap->a_ncookies -= ncookies; + + uio->uio_offset = offset; + uio->uio_resid += lost; /* * Set the eofflag (NFS uses it) */ if (ap->a_eofflag) - if (dep->de_FileSize - (uio->uio_offset - bias) <= 0) + if (dep->de_FileSize - (offset - bias) <= 0) *ap->a_eofflag = 1; else *ap->a_eofflag = 0; - return error; + return (error); } static int @@ -1703,7 +1795,7 @@ msdosfs_abortop(ap) { if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) zfree(namei_zone, ap->a_cnp->cn_pnbuf); - return 0; + return (0); } /* @@ -1725,11 +1817,12 @@ msdosfs_bmap(ap) } */ *ap; { struct denode *dep = VTODE(ap->a_vp); + struct msdosfsmount *pmp = dep->de_pmp; if (ap->a_vpp != NULL) *ap->a_vpp = dep->de_devvp; if (ap->a_bnp == NULL) - return 0; + return (0); if (ap->a_runp) { /* * Sequential clusters should be counted here. @@ -1739,7 +1832,7 @@ msdosfs_bmap(ap) if (ap->a_runb) { *ap->a_runb = 0; } - return pcbmap(dep, ap->a_bn, ap->a_bnp, 0); + return (pcbmap(dep, de_bn2cn(pmp, ap->a_bn), ap->a_bnp, 0, 0)); } static int @@ -1762,18 +1855,21 @@ msdosfs_strategy(ap) * don't allow files with holes, so we shouldn't ever see this. */ if (bp->b_blkno == bp->b_lblkno) { - error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0); - if (error) - bp->b_blkno = -1; - if (bp->b_blkno == -1) - clrbuf(bp); + error = pcbmap(dep, de_bn2cn(dep->de_pmp, bp->b_lblkno), + &bp->b_blkno, 0, 0); + if (error) { + bp->b_error = error; + bp->b_flags |= B_ERROR; + biodone(bp); + return (error); + } + if ((long)bp->b_blkno == -1) + vfs_bio_clrbuf(bp); } if (bp->b_blkno == -1) { biodone(bp); - return error; + return (0); } -#ifdef DIAGNOSTIC -#endif /* * Read/write the block from/to the disk that contains the desired * file block. @@ -1781,7 +1877,7 @@ msdosfs_strategy(ap) vp = dep->de_devvp; bp->b_dev = vp->v_rdev; VOCALL(vp->v_op, VOFFSET(vop_strategy), ap); - return 0; + return (0); } static int @@ -1798,7 +1894,7 @@ msdosfs_print(ap) printf(" dev %d, %d", major(dep->de_dev), minor(dep->de_dev)); lockmgr_printinfo(&dep->de_lock); printf("\n"); - return 0; + return (0); } static int @@ -1809,25 +1905,28 @@ msdosfs_pathconf(ap) int *a_retval; } */ *ap; { + struct msdosfsmount *pmp = VTODE(ap->a_vp)->de_pmp; + switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = 1; - return 0; + return (0); case _PC_NAME_MAX: - *ap->a_retval = 12; - return 0; + *ap->a_retval = pmp->pm_flags & MSDOSFSMNT_LONGNAME ? WIN_MAXLEN : 12; + return (0); case _PC_PATH_MAX: - *ap->a_retval = PATH_MAX; /* 255? */ - return 0; + *ap->a_retval = PATH_MAX; + return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; - return 0; + return (0); case _PC_NO_TRUNC: *ap->a_retval = 0; - return 0; + return (0); default: - return EINVAL; + return (EINVAL); } + /* NOTREACHED */ } /* Global vfs data structures for msdosfs */ diff --git a/sys/fs/msdosfs/msdosfsmount.h b/sys/fs/msdosfs/msdosfsmount.h index 49a93279a11f..3a0ee6e047a3 100644 --- a/sys/fs/msdosfs/msdosfsmount.h +++ b/sys/fs/msdosfs/msdosfsmount.h @@ -1,9 +1,9 @@ -/* $Id: msdosfsmount.h,v 1.11 1997/03/03 17:36:11 bde Exp $ */ -/* $NetBSD: msdosfsmount.h,v 1.7 1994/08/21 18:44:17 ws Exp $ */ +/* $Id: msdosfsmount.h,v 1.12 1997/10/12 20:25:02 phk Exp $ */ +/* $NetBSD: msdosfsmount.h,v 1.17 1997/11/17 15:37:07 ws Exp $ */ /*- - * Copyright (C) 1994 Wolfgang Solfrank. - * Copyright (C) 1994 TooLs GmbH. + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * @@ -63,90 +63,119 @@ MALLOC_DECLARE(M_MSDOSFSMNT); 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_FATsecs; /* actual number of fat sectors */ u_long pm_fatblk; /* block # of first FAT */ - u_long pm_rootdirblk; /* block # of root directory */ + u_long pm_rootdirblk; /* block # (cluster # for FAT32) of root directory number */ 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_bnshift; /* shift file offset right this amount to get a block number */ 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_long pm_fatmask; /* mask to use for fat numbers */ + u_long pm_fsinfo; /* fsinfo block number */ + u_long pm_nxtfree; /* next free cluster in fsinfo block */ + u_int pm_fatmult; /* these 2 values are used in fat */ + u_int pm_fatdiv; /* offset computation */ + u_int pm_curfat; /* current fat for FAT32 (0 otherwise) */ 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 complete, when 0 use bdwrite, else use bwrite */ + u_int pm_flags; /* see below */ struct netexport pm_export; /* export information */ }; +/* Byte offset in FAT on filesystem pmp, cluster cn */ +#define FATOFS(pmp, cn) ((cn) * (pmp)->pm_fatmult / (pmp)->pm_fatdiv) + + +#define VFSTOMSDOSFS(mp) ((struct msdosfsmount *)mp->mnt_data) /* 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 +/* + * Convert pointer to buffer -> pointer to direntry + */ +#define bptoep(pmp, bp, dirofs) \ + ((struct direntry *)(((bp)->b_data) \ + + ((dirofs) & (pmp)->pm_crbomask))) + +/* + * Convert block number to cluster number + */ +#define de_bn2cn(pmp, bn) \ + ((bn) >> ((pmp)->pm_cnshift - (pmp)->pm_bnshift)) + +/* + * Convert cluster number to block number + */ +#define de_cn2bn(pmp, cn) \ + ((cn) << ((pmp)->pm_cnshift - (pmp)->pm_bnshift)) + +/* + * Convert file offset to cluster number + */ +#define de_cluster(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) + +/* + * Convert file offset to block number + */ +#define de_blk(pmp, off) \ + (de_cn2bn(pmp, de_cluster((pmp), (off)))) + +/* + * Convert cluster number to file offset + */ +#define de_cn2off(pmp, cn) \ + ((cn) << (pmp)->pm_cnshift) + +/* + * Convert block number to file offset + */ +#define de_bn2off(pmp, bn) \ + ((bn) << (pmp)->pm_bnshift) /* * 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) + (de_cn2bn((pmp), (cn)-CLUST_FIRST) + (pmp)->pm_firstcluster) /* * 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) + (de_blk((pmp), (dirofs)) + (pmp)->pm_rootdirblk) /* * Calculate block number for directory entry at cluster dirclu, offset @@ -157,27 +186,8 @@ struct msdosfsmount { ? 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) - int msdosfs_init __P((struct vfsconf *vfsp)); +int msdosfs_mountroot __P((void)); #endif /* KERNEL */ @@ -190,6 +200,27 @@ struct msdosfs_args { 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 */ + int flags; /* see below */ + int magic; /* version number */ }; +/* + * Msdosfs mount options: + */ +#define MSDOSFSMNT_SHORTNAME 1 /* Force old DOS short names only */ +#define MSDOSFSMNT_LONGNAME 2 /* Force Win'95 long names */ +#define MSDOSFSMNT_NOWIN95 4 /* Completely ignore Win95 entries */ +#ifndef __FreeBSD__ +#define MSDOSFSMNT_GEMDOSFS 8 /* This is a gemdos-flavour */ +#endif +/* All flags above: */ +#define MSDOSFSMNT_MNTOPT \ + (MSDOSFSMNT_SHORTNAME|MSDOSFSMNT_LONGNAME|MSDOSFSMNT_NOWIN95 \ + /*|MSDOSFSMNT_GEMDOSFS*/) +#define MSDOSFSMNT_RONLY 0x80000000 /* mounted read-only */ +#define MSDOSFSMNT_WAITONFAT 0x40000000 /* mounted synchronous */ +#define MSDOSFS_FATMIRROR 0x20000000 /* FAT is mirrored */ + +#define MSDOSFS_ARGSMAGIC 0xe4eff300 + #endif /* !_MSDOSFS_MSDOSFSMOUNT_H_ */ diff --git a/sys/msdosfs/bootsect.h b/sys/msdosfs/bootsect.h index 86fc415770c0..11b93371a534 100644 --- a/sys/msdosfs/bootsect.h +++ b/sys/msdosfs/bootsect.h @@ -1,5 +1,5 @@ -/* $Id$ */ -/* $NetBSD: bootsect.h,v 1.4 1994/06/29 06:35:28 cgd Exp $ */ +/* $Id: bootsect.h,v 1.5 1997/02/22 09:40:43 peter Exp $ */ +/* $NetBSD: bootsect.h,v 1.9 1997/11/17 15:36:17 ws Exp $ */ /* * Written by Paul Popelka (paulp@uts.amdahl.com) @@ -23,36 +23,78 @@ * 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[479]; /* pad so structure is 512 bytes long */ - u_short bsBootSectSig; -#define BOOTSIG 0xaa55 + u_int8_t bsJump[3]; /* jump inst E9xxxx or EBxx90 */ + int8_t bsOemName[8]; /* OEM name and version */ + int8_t bsBPB[19]; /* BIOS parameter block */ + int8_t bsDriveNumber; /* drive number (0x80) */ + int8_t bsBootCode[479]; /* pad so struct is 512b */ + u_int8_t bsBootSectSig0; + u_int8_t bsBootSectSig1; +#define BOOTSIG0 0x55 +#define BOOTSIG1 0xaa +}; + +struct extboot { + int8_t exDriveNumber; /* drive number (0x80) */ + int8_t exReserved1; /* reserved */ + int8_t exBootSignature; /* ext. boot signature (0x29) */ +#define EXBOOTSIG 0x29 + int8_t exVolumeID[4]; /* volume ID number */ + int8_t exVolumeLabel[11]; /* volume label */ + int8_t exFileSysType[8]; /* fs type (FAT12 or FAT16) */ }; 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 + u_int8_t bsJump[3]; /* jump inst E9xxxx or EBxx90 */ + int8_t bsOemName[8]; /* OEM name and version */ + int8_t bsBPB[25]; /* BIOS parameter block */ + int8_t bsExt[26]; /* Bootsector Extension */ + int8_t bsBootCode[448]; /* pad so structure is 512b */ + u_int8_t bsBootSectSig0; + u_int8_t bsBootSectSig1; +#define BOOTSIG0 0x55 +#define BOOTSIG1 0xaa }; +struct bootsector710 { + u_int8_t bsJump[3]; /* jump inst E9xxxx or EBxx90 */ + int8_t bsOEMName[8]; /* OEM name and version */ + int8_t bsPBP[53]; /* BIOS parameter block */ + int8_t bsExt[26]; /* Bootsector Extension */ + int8_t bsBootCode[418]; /* pad so structure is 512b */ + u_int8_t bsBootSectSig2; /* 2 & 3 are only defined for FAT32? */ + u_int8_t bsBootSectSig3; + u_int8_t bsBootSectSig0; + u_int8_t bsBootSectSig1; +#define BOOTSIG0 0x55 +#define BOOTSIG1 0xaa +#define BOOTSIG2 0 +#define BOOTSIG3 0 +}; +#ifdef atari +/* + * The boot sector on a gemdos fs is a little bit different from the msdos fs + * format. Currently there is no need to declare a seperate structure, the + * bootsector33 struct will do. + */ +#if 0 +struct bootsec_atari { + u_int8_t bsBranch[2]; /* branch inst if auto-boot */ + int8_t bsFiller[6]; /* anything or nothing */ + int8_t bsSerial[3]; /* serial no. for mediachange */ + int8_t bsBPB[19]; /* BIOS parameter block */ + int8_t bsBootCode[482]; /* pad so struct is 512b */ +}; +#endif +#endif /* atari */ + union bootsector { struct bootsector33 bs33; struct bootsector50 bs50; + struct bootsector710 bs710; }; +#if 0 /* * Shorthand for fields in the bpb. */ @@ -68,3 +110,4 @@ union bootsector { #define bsHeads bsBPB.bpbHeads #define bsHiddenSecs bsBPB.bpbHiddenSecs #define bsHugeSectors bsBPB.bpbHugeSectors +#endif diff --git a/sys/msdosfs/bpb.h b/sys/msdosfs/bpb.h index 33e1eb69e00c..bc00a758de51 100644 --- a/sys/msdosfs/bpb.h +++ b/sys/msdosfs/bpb.h @@ -1,5 +1,5 @@ -/* $Id$ */ -/* $NetBSD: bpb.h,v 1.3 1994/06/29 06:35:29 cgd Exp $ */ +/* $Id: bpb.h,v 1.5 1997/02/22 09:40:44 peter Exp $ */ +/* $NetBSD: bpb.h,v 1.7 1997/11/17 15:36:24 ws Exp $ */ /* * Written by Paul Popelka (paulp@uts.amdahl.com) @@ -21,17 +21,17 @@ * 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 */ + u_int16_t bpbBytesPerSec; /* bytes per sector */ + u_int8_t bpbSecPerClust; /* sectors per cluster */ + u_int16_t bpbResSectors; /* number of reserved sectors */ + u_int8_t bpbFATs; /* number of FATs */ + u_int16_t bpbRootDirEnts; /* number of root directory entries */ + u_int16_t bpbSectors; /* total number of sectors */ + u_int8_t bpbMedia; /* media descriptor */ + u_int16_t bpbFATsecs; /* number of sectors per FAT */ + u_int16_t bpbSecPerTrack; /* sectors per track */ + u_int16_t bpbHeads; /* number of heads */ + u_int16_t bpbHiddenSecs; /* number of hidden sectors */ }; /* @@ -39,20 +39,70 @@ struct bpb33 { * 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 sectors if bpbSectors == 0 */ + u_int16_t bpbBytesPerSec; /* bytes per sector */ + u_int8_t bpbSecPerClust; /* sectors per cluster */ + u_int16_t bpbResSectors; /* number of reserved sectors */ + u_int8_t bpbFATs; /* number of FATs */ + u_int16_t bpbRootDirEnts; /* number of root directory entries */ + u_int16_t bpbSectors; /* total number of sectors */ + u_int8_t bpbMedia; /* media descriptor */ + u_int16_t bpbFATsecs; /* number of sectors per FAT */ + u_int16_t bpbSecPerTrack; /* sectors per track */ + u_int16_t bpbHeads; /* number of heads */ + u_int32_t bpbHiddenSecs; /* # of hidden sectors */ + u_int32_t bpbHugeSectors; /* # of sectors if bpbSectors == 0 */ }; +/* + * BPB for DOS 7.10 (FAT32). This one has a few extensions to bpb50. + */ +struct bpb710 { + u_int16_t bpbBytesPerSec; /* bytes per sector */ + u_int8_t bpbSecPerClust; /* sectors per cluster */ + u_int16_t bpbResSectors; /* number of reserved sectors */ + u_int8_t bpbFATs; /* number of FATs */ + u_int16_t bpbRootDirEnts; /* number of root directory entries */ + u_int16_t bpbSectors; /* total number of sectors */ + u_int8_t bpbMedia; /* media descriptor */ + u_int16_t bpbFATsecs; /* number of sectors per FAT */ + u_int16_t bpbSecPerTrack; /* sectors per track */ + u_int16_t bpbHeads; /* number of heads */ + u_int32_t bpbHiddenSecs; /* # of hidden sectors */ + u_int32_t bpbHugeSectors; /* # of sectors if bpbSectors == 0 */ + u_int32_t bpbBigFATsecs; /* like bpbFATsecs for FAT32 */ + u_int16_t bpbExtFlags; /* extended flags: */ +#define FATNUM 0xf /* mask for numbering active FAT */ +#define FATMIRROR 0x80 /* FAT is mirrored (like it always was) */ + u_int16_t bpbFSVers; /* filesystem version */ +#define FSVERS 0 /* currently only 0 is understood */ + u_int32_t bpbRootClust; /* start cluster for root directory */ + u_int16_t bpbFSInfo; /* filesystem info structure sector */ + u_int16_t bpbBackup; /* backup boot sector */ + /* There is a 12 byte filler here, but we ignore it */ +}; + +#ifdef atari +/* + * BPB for gemdos filesystems. Atari leaves the obsolete stuff undefined. + * Currently there is no need for a separate BPB structure. + */ +#if 0 +struct bpb_a { + u_int16_t bpbBytesPerSec; /* bytes per sector */ + u_int8_t bpbSecPerClust; /* sectors per cluster */ + u_int16_t bpbResSectors; /* number of reserved sectors */ + u_int8_t bpbFATs; /* number of FATs */ + u_int16_t bpbRootDirEnts; /* number of root directory entries */ + u_int16_t bpbSectors; /* total number of sectors */ + u_int8_t bpbUseless1; /* meaningless on gemdos fs */ + u_int16_t bpbFATsecs; /* number of sectors per FAT */ + u_int16_t bpbUseless2; /* meaningless for harddisk fs */ + u_int16_t bpbUseless3; /* meaningless for harddisk fs */ + u_int16_t bpbHiddenSecs; /* the TOS-BIOS ignores this */ +}; +#endif +#endif /* atari */ + /* * The following structures represent how the bpb's look on disk. shorts * and longs are just character arrays of the appropriate length. This is @@ -64,40 +114,39 @@ struct bpb50 { * use the macros for the big-endian case. */ #include -#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)) - +#if (BYTE_ORDER == LITTLE_ENDIAN) /* && defined(UNALIGNED_ACCESS) */ +#define getushort(x) *((u_int16_t *)(x)) +#define getulong(x) *((u_int32_t *)(x)) +#define putushort(p, v) (*((u_int16_t *)(p)) = (v)) +#define putulong(p, v) (*((u_int32_t *)(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) +#define getushort(x) (((u_int8_t *)(x))[0] + (((u_int8_t *)(x))[1] << 8)) +#define getulong(x) (((u_int8_t *)(x))[0] + (((u_int8_t *)(x))[1] << 8) \ + + (((u_int8_t *)(x))[2] << 16) \ + + (((u_int8_t *)(x))[3] << 24)) +#define putushort(p, v) (((u_int8_t *)(p))[0] = (v), \ + ((u_int8_t *)(p))[1] = (v) >> 8) +#define putulong(p, v) (((u_int8_t *)(p))[0] = (v), \ + ((u_int8_t *)(p))[1] = (v) >> 8, \ + ((u_int8_t *)(p))[2] = (v) >> 16,\ + ((u_int8_t *)(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 */ + int8_t bpbBytesPerSec[2]; /* bytes per sector */ + int8_t bpbSecPerClust; /* sectors per cluster */ + int8_t bpbResSectors[2]; /* number of reserved sectors */ + int8_t bpbFATs; /* number of FATs */ + int8_t bpbRootDirEnts[2]; /* number of root directory entries */ + int8_t bpbSectors[2]; /* total number of sectors */ + int8_t bpbMedia; /* media descriptor */ + int8_t bpbFATsecs[2]; /* number of sectors per FAT */ + int8_t bpbSecPerTrack[2]; /* sectors per track */ + int8_t bpbHeads[2]; /* number of heads */ + int8_t bpbHiddenSecs[2]; /* number of hidden sectors */ }; /* @@ -105,16 +154,56 @@ struct byte_bpb33 { * 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 sectors if bpbSectors == 0 */ + int8_t bpbBytesPerSec[2]; /* bytes per sector */ + int8_t bpbSecPerClust; /* sectors per cluster */ + int8_t bpbResSectors[2]; /* number of reserved sectors */ + int8_t bpbFATs; /* number of FATs */ + int8_t bpbRootDirEnts[2]; /* number of root directory entries */ + int8_t bpbSectors[2]; /* total number of sectors */ + int8_t bpbMedia; /* media descriptor */ + int8_t bpbFATsecs[2]; /* number of sectors per FAT */ + int8_t bpbSecPerTrack[2]; /* sectors per track */ + int8_t bpbHeads[2]; /* number of heads */ + int8_t bpbHiddenSecs[4]; /* number of hidden sectors */ + int8_t bpbHugeSectors[4]; /* # of sectors if bpbSectors == 0 */ +}; + +/* + * BPB for DOS 7.10 (FAT32). This one has a few extensions to bpb50. + */ +struct byte_bpb710 { + u_int8_t bpbBytesPerSec[2]; /* bytes per sector */ + u_int8_t bpbSecPerClust; /* sectors per cluster */ + u_int8_t bpbResSectors[2]; /* number of reserved sectors */ + u_int8_t bpbFATs; /* number of FATs */ + u_int8_t bpbRootDirEnts[2]; /* number of root directory entries */ + u_int8_t bpbSectors[2]; /* total number of sectors */ + u_int8_t bpbMedia; /* media descriptor */ + u_int8_t bpbFATsecs[2]; /* number of sectors per FAT */ + u_int8_t bpbSecPerTrack[2]; /* sectors per track */ + u_int8_t bpbHeads[2]; /* number of heads */ + u_int8_t bpbHiddenSecs[4]; /* # of hidden sectors */ + u_int8_t bpbHugeSectors[4]; /* # of sectors if bpbSectors == 0 */ + u_int8_t bpbBigFATsecs[4]; /* like bpbFATsecs for FAT32 */ + u_int8_t bpbExtFlags[2]; /* extended flags: */ + u_int8_t bpbFSVers[2]; /* filesystem version */ + u_int8_t bpbRootClust[4]; /* start cluster for root directory */ + u_int8_t bpbFSInfo[2]; /* filesystem info structure sector */ + u_int8_t bpbBackup[2]; /* backup boot sector */ + /* There is a 12 byte filler here, but we ignore it */ +}; + +/* + * FAT32 FSInfo block. + */ +struct fsinfo { + u_int8_t fsisig1[4]; + u_int8_t fsifill1[480]; + u_int8_t fsisig2[4]; + u_int8_t fsinfree[4]; + u_int8_t fsinxtfree[4]; + u_int8_t fsifill2[12]; + u_int8_t fsisig3[4]; + u_int8_t fsifill3[508]; + u_int8_t fsisig4[4]; }; diff --git a/sys/msdosfs/denode.h b/sys/msdosfs/denode.h index 6ad3fccb5de8..2b9d63698f22 100644 --- a/sys/msdosfs/denode.h +++ b/sys/msdosfs/denode.h @@ -1,9 +1,9 @@ -/* $Id: denode.h,v 1.13 1997/08/26 07:32:36 phk Exp $ */ -/* $NetBSD: denode.h,v 1.8 1994/08/21 18:43:49 ws Exp $ */ +/* $Id: denode.h,v 1.14 1997/10/17 12:36:16 phk Exp $ */ +/* $NetBSD: denode.h,v 1.25 1997/11/17 15:36:28 ws Exp $ */ /*- - * Copyright (C) 1994 Wolfgang Solfrank. - * Copyright (C) 1994 TooLs GmbH. + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * @@ -103,8 +103,8 @@ * structure (fc_frcn). */ struct fatcache { - u_short fc_frcn; /* file relative cluster number */ - u_short fc_fsrcn; /* filesystem relative cluster number */ + u_long fc_frcn; /* file relative cluster number */ + u_long fc_fsrcn; /* filesystem relative cluster number */ }; /* @@ -121,7 +121,7 @@ struct fatcache { * to */ #define FC_LASTFC 1 /* entry for the last cluster in the file */ -#define FCE_EMPTY 0xffff /* doesn't represent an actual cluster # */ +#define FCE_EMPTY 0xffffffff /* doesn't represent an actual cluster # */ /* * Set a slot in the fat cache. @@ -143,19 +143,21 @@ struct denode { 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_diroffset; /* offset of this entry in the directory cluster */ u_long de_fndoffset; /* offset of found dir entry */ + int de_fndcnt; /* number of slots before de_fndoffset */ long de_refcnt; /* reference count */ struct msdosfsmount *de_pmp; /* addr of our mount struct */ struct lockf *de_lockf; /* byte level lock list */ - /* 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_Name[12]; /* name, from DOS 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_char de_CHun; /* Hundredth of second of CTime*/ + u_short de_CTime; /* creation time */ + u_short de_CDate; /* creation date */ + u_short de_ADate; /* access date */ + u_short de_MTime; /* modification time */ + u_short de_MDate; /* modification date */ + u_long de_StartCluster; /* starting cluster of file */ u_long de_FileSize; /* size of file in bytes */ struct fatcache de_fc[FC_SIZE]; /* fat cache */ u_quad_t de_modrev; /* Revision level for lease. */ @@ -164,31 +166,49 @@ struct denode { /* * Values for the de_flag field of the denode. */ -#define DE_UPDATE 0x0004 /* modification time update request */ -#define DE_MODIFIED 0x0080 /* denode has been modified, but DE_UPDATE - * isn't set */ +#define DE_UPDATE 0x0004 /* Modification time update request */ +#define DE_CREATE 0x0008 /* Creation time update */ +#define DE_ACCESS 0x0010 /* Access time update */ +#define DE_MODIFIED 0x0020 /* Denode has been modified */ +#define DE_RENAME 0x0040 /* Denode is in the process of being renamed */ + /* * 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) \ +#define DE_INTERNALIZE32(dep, dp) \ + ((dep)->de_StartCluster |= getushort((dp)->deHighClust) << 16) +#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_CHun = (dp)->deCHundredth, \ + (dep)->de_CTime = getushort((dp)->deCTime), \ + (dep)->de_CDate = getushort((dp)->deCDate), \ + (dep)->de_ADate = getushort((dp)->deADate), \ + (dep)->de_MTime = getushort((dp)->deMTime), \ + (dep)->de_MDate = getushort((dp)->deMDate), \ (dep)->de_StartCluster = getushort((dp)->deStartCluster), \ - (dep)->de_FileSize = getulong((dp)->deFileSize)) + (dep)->de_FileSize = getulong((dp)->deFileSize), \ + (FAT32((dep)->de_pmp) ? DE_INTERNALIZE32((dep), (dp)) : 0)) +#define DE_EXTERNALIZE32(dp, dep) \ + putushort((dp)->deHighClust, (dep)->de_StartCluster >> 16) #define DE_EXTERNALIZE(dp, dep) \ (bcopy((dep)->de_Name, (dp)->deName, 11), \ bzero((dp)->deReserved, 10), \ (dp)->deAttributes = (dep)->de_Attributes, \ - putushort((dp)->deTime, (dep)->de_Time), \ - putushort((dp)->deDate, (dep)->de_Date), \ + (dp)->deCHundredth = (dep)->de_CHun, \ + putushort((dp)->deCTime, (dep)->de_CTime), \ + putushort((dp)->deCDate, (dep)->de_CDate), \ + putushort((dp)->deADate, (dep)->de_ADate), \ + putushort((dp)->deMTime, (dep)->de_MTime), \ + putushort((dp)->deMDate, (dep)->de_MDate), \ putushort((dp)->deStartCluster, (dep)->de_StartCluster), \ - putulong((dp)->deFileSize, (dep)->de_FileSize)) + putulong((dp)->deFileSize, \ + ((dep)->de_Attributes & ATTR_DIRECTORY) ? 0 : (dep)->de_FileSize), \ + (FAT32((dep)->de_pmp) ? DE_EXTERNALIZE32((dp), (dep)) : 0)) #define de_forw de_chain[0] #define de_back de_chain[1] @@ -198,17 +218,20 @@ struct denode { #define VTODE(vp) ((struct denode *)(vp)->v_data) #define DETOV(de) ((de)->de_vnode) -#define DE_TIMES(dep, t) \ - if ((dep)->de_flag & DE_UPDATE) { \ - if (!((dep)->de_Attributes & ATTR_DIRECTORY)) { \ - struct timespec DE_TIMES_ts; \ - (dep)->de_flag |= DE_MODIFIED; \ - TIMEVAL_TO_TIMESPEC((t), &DE_TIMES_ts); \ - unix2dostime(&DE_TIMES_ts, &(dep)->de_Date, \ - &(dep)->de_Time); \ +#define DETIMES(dep, acc, mod, cre) \ + if ((dep)->de_flag & (DE_UPDATE | DE_CREATE | DE_ACCESS)) { \ + (dep)->de_flag |= DE_MODIFIED; \ + if ((dep)->de_flag & DE_UPDATE) { \ + unix2dostime((mod), &(dep)->de_MDate, &(dep)->de_MTime, NULL); \ (dep)->de_Attributes |= ATTR_ARCHIVE; \ } \ - (dep)->de_flag &= ~DE_UPDATE; \ + if (!((dep)->de_pmp->pm_flags & MSDOSFSMNT_NOWIN95)) { \ + if ((dep)->de_flag & DE_ACCESS) \ + unix2dostime((acc), &(dep)->de_ADate, NULL, NULL); \ + if ((dep)->de_flag & DE_CREATE) \ + unix2dostime((cre), &(dep)->de_CDate, &(dep)->de_CTime, &(dep)->de_CHun); \ + } \ + (dep)->de_flag &= ~(DE_UPDATE | DE_CREATE | DE_ACCESS); \ } /* @@ -219,9 +242,10 @@ struct defid { 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 */ + u_long defid_dirofs; /* offset of entry within the cluster */ +#if 0 + u_long defid_gen; /* generation number */ +#endif }; extern vop_t **msdosfs_vnodeop_p; @@ -233,5 +257,19 @@ int msdosfs_reclaim __P((struct vop_reclaim_args *)); /* * Internal service routine prototypes. */ -int deget __P((struct msdosfsmount * pmp, u_long dirclust, u_long diroffset, struct direntry * direntptr, struct denode ** depp)); +int deget __P((struct msdosfsmount *, u_long, u_long, struct denode **)); +int uniqdosname __P((struct denode *, struct componentname *, u_char *)); +int findwin95 __P((struct denode *)); + +int readep __P((struct msdosfsmount *pmp, u_long dirclu, u_long dirofs, struct buf **bpp, struct direntry **epp)); +int readde __P((struct denode *dep, struct buf **bpp, struct direntry **epp)); +int deextend __P((struct denode *dep, u_long length, struct ucred *cred)); +int fillinusemap __P((struct msdosfsmount *pmp)); +void reinsert __P((struct denode *dep)); +int dosdirempty __P((struct denode *dep)); +int createde __P((struct denode *dep, struct denode *ddep, struct denode **depp, struct componentname *cnp)); +int deupdat __P((struct denode *dep, int waitfor)); +int removede __P((struct denode *pdep, struct denode *dep)); +int detrunc __P((struct denode *dep, u_long length, int flags, struct ucred *cred, struct proc *p)); +int doscheckpath __P(( struct denode *source, struct denode *target)); #endif /* KERNEL */ diff --git a/sys/msdosfs/direntry.h b/sys/msdosfs/direntry.h index b97a1055f402..0bfe164af832 100644 --- a/sys/msdosfs/direntry.h +++ b/sys/msdosfs/direntry.h @@ -1,9 +1,9 @@ -/* $Id$ */ -/* $NetBSD: direntry.h,v 1.7 1994/08/21 18:43:54 ws Exp $ */ +/* $Id: direntry.h,v 1.4 1997/02/22 09:40:45 peter Exp $ */ +/* $NetBSD: direntry.h,v 1.14 1997/11/17 15:36:32 ws Exp $ */ /*- - * Copyright (C) 1994 Wolfgang Solfrank. - * Copyright (C) 1994 TooLs GmbH. + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * @@ -52,26 +52,55 @@ * 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 */ + u_int8_t 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_int8_t deExtension[3]; /* extension, blank filled */ + u_int8_t 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_int8_t deReserved[1]; /* reserved */ + u_int8_t deCHundredth; /* hundredth of seconds in CTime */ + u_int8_t deCTime[2]; /* create time */ + u_int8_t deCDate[2]; /* create date */ + u_int8_t deADate[2]; /* access date */ + u_int8_t deHighClust[2]; /* high bytes of cluster number */ + u_int8_t deMTime[2]; /* last update time */ + u_int8_t deMDate[2]; /* last update date */ + u_int8_t deStartCluster[2]; /* starting cluster of file */ + u_int8_t deFileSize[4]; /* size of file in bytes */ }; +/* + * Structure of a Win95 long name directory entry + */ +struct winentry { + u_int8_t weCnt; +#define WIN_LAST 0x40 +#define WIN_CNT 0x3f + u_int8_t wePart1[10]; + u_int8_t weAttributes; +#define ATTR_WIN95 0x0f + u_int8_t weReserved1; + u_int8_t weChksum; + u_int8_t wePart2[12]; + u_int16_t weReserved2; + u_int8_t wePart3[4]; +}; +#define WIN_CHARS 13 /* Number of chars per winentry */ + +/* + * Maximum filename length in Win95 + * Note: Must be < sizeof(dirent.d_name) + */ +#define WIN_MAXLEN 255 + /* * This is the format of the contents of the deTime field in the direntry * structure. @@ -97,8 +126,15 @@ struct direntry { #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)); +struct dirent; +void unix2dostime __P((struct timespec *tsp, u_int16_t *ddp, + u_int16_t *dtp, u_int8_t *dhp)); +void dos2unixtime __P((u_int dd, u_int dt, u_int dh, struct timespec *tsp)); +int dos2unixfn __P((u_char dn[11], u_char *un, int lower)); +int unix2dosfn __P((const u_char *un, u_char dn[12], int unlen, u_int gen)); +int unix2winfn __P((const u_char *un, int unlen, struct winentry *wep, int cnt, int chksum)); +int winChkName __P((const u_char *un, int unlen, struct winentry *wep, int chksum)); +int win2unixfn __P((struct winentry *wep, struct dirent *dp, int chksum)); +u_int8_t winChksum __P((u_int8_t *name)); +int winSlotCnt __P((const u_char *un, int unlen)); #endif /* KERNEL */ diff --git a/sys/msdosfs/fat.h b/sys/msdosfs/fat.h index f8fdb6fd1eeb..74b05e2cf78d 100644 --- a/sys/msdosfs/fat.h +++ b/sys/msdosfs/fat.h @@ -1,9 +1,9 @@ -/* $Id$ */ -/* $NetBSD: fat.h,v 1.4 1994/08/21 18:43:57 ws Exp $ */ +/* $Id: fat.h,v 1.6 1997/02/22 09:40:45 peter Exp $ */ +/* $NetBSD: fat.h,v 1.12 1997/11/17 15:36:36 ws Exp $ */ /*- - * Copyright (C) 1994 Wolfgang Solfrank. - * Copyright (C) 1994 TooLs GmbH. + * Copyright (C) 1994, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * @@ -51,28 +51,37 @@ /* * 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 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 CLUST_FIRST 2 /* first legal cluster number */ +#define CLUST_RSRVD 0xfffffff6 /* reserved cluster range */ +#define CLUST_BAD 0xfffffff7 /* a cluster with a defect */ +#define CLUST_EOFS 0xfffffff8 /* start of eof cluster range */ +#define CLUST_EOFE 0xffffffff /* 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 */ +#define FAT12_MASK 0x00000fff /* mask for 12 bit cluster numbers */ +#define FAT16_MASK 0x0000ffff /* mask for 16 bit cluster numbers */ +#define FAT32_MASK 0x0fffffff /* mask for FAT32 cluster numbers */ /* + * MSDOSFS: * 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. + * than 4078 ((CLUST_RSRVS - CLUST_FIRST) & FAT12_MASK) then we've got a + * 16 bit fat filesystem. While mounting, the result of this test is stored + * in pm_fatentrysize. + * GEMDOS-flavour (atari): + * If the filesystem is on floppy we've got a 12 bit fat filesystem, otherwise + * 16 bit. We check the d_type field in the disklabel struct while mounting + * and store the result in the pm_fatentrysize. Note that this kind of + * detection gets flakey when mounting a vnd-device. */ -#define FAT12(pmp) (pmp->pm_maxcluster <= 4086) -#define FAT16(pmp) (pmp->pm_maxcluster > 4086) +#define FAT12(pmp) (pmp->pm_fatmask == FAT12_MASK) +#define FAT16(pmp) (pmp->pm_fatmask == FAT16_MASK) +#define FAT32(pmp) (pmp->pm_fatmask == FAT32_MASK) -#define MSDOSFSEOF(cn) (((cn) & 0xfff8) == 0xfff8) +#define MSDOSFSEOF(pmp, cn) ((((cn) | ~(pmp)->pm_fatmask) & CLUST_EOFS) == CLUST_EOFS) #ifdef KERNEL /* @@ -88,7 +97,7 @@ */ #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 pcbmap __P((struct denode *dep, u_long findcn, daddr_t *bnp, u_long *cnp, int* sp)); 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)); @@ -96,15 +105,4 @@ 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)); void fc_purge __P((struct denode *dep, u_int frcn)); -int readep __P((struct msdosfsmount *pmp, u_long dirclu, u_long dirofs, struct buf **bpp, struct direntry **epp)); -int readde __P((struct denode *dep, struct buf **bpp, struct direntry **epp)); -int deextend __P((struct denode *dep, off_t length, struct ucred *cred)); -int fillinusemap __P((struct msdosfsmount *pmp)); -int reinsert __P((struct denode *dep)); -int dosdirempty __P((struct denode *dep)); -int createde __P((struct denode *dep, struct denode *ddep, struct denode **depp)); -int deupdat __P((struct denode *dep, struct timespec *tp, int waitfor)); -int removede __P((struct denode *pdep, struct denode *dep)); -int detrunc __P((struct denode *dep, u_long length, int flags, struct ucred *cred, struct proc *p)); -int doscheckpath __P(( struct denode *source, struct denode *target)); #endif /* KERNEL */ diff --git a/sys/msdosfs/msdosfs_conv.c b/sys/msdosfs/msdosfs_conv.c index 59f4d2c1457c..727cacd80304 100644 --- a/sys/msdosfs/msdosfs_conv.c +++ b/sys/msdosfs/msdosfs_conv.c @@ -1,6 +1,37 @@ -/* $Id: msdosfs_conv.c,v 1.13 1997/02/22 09:40:46 peter Exp $ */ -/* $NetBSD: msdosfs_conv.c,v 1.6.2.1 1994/08/30 02:27:57 cgd Exp $ */ +/* $Id: msdosfs_conv.c,v 1.14 1998/02/09 06:09:50 eivind Exp $ */ +/* $NetBSD: msdosfs_conv.c,v 1.25 1997/11/17 15:36:40 ws Exp $ */ +/*- + * Copyright (C) 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1995, 1997 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) * @@ -23,8 +54,9 @@ #include #include #include /* defines tz */ -#include /* defines tz */ +#include #include +#include /* * MSDOSFS include files. @@ -61,10 +93,11 @@ static u_short lastdtime; * file timestamps. The passed in unix time is assumed to be in GMT. */ void -unix2dostime(tsp, ddp, dtp) +unix2dostime(tsp, ddp, dtp, dhp) struct timespec *tsp; - u_short *ddp; - u_short *dtp; + u_int16_t *ddp; + u_int16_t *dtp; + u_int8_t *dhp; { u_long t; u_long days; @@ -80,9 +113,10 @@ unix2dostime(tsp, ddp, dtp) t = tsp->tv_sec - (tz.tz_minuteswest * 60) - (wall_cmos_clock ? adjkerntz : 0); /* - daylight savings time correction */ + t &= ~1; if (lasttime != t) { lasttime = t; - lastdtime = (((t % 60) >> 1) << DT_2SECONDS_SHIFT) + lastdtime = (((t / 2) % 30) << DT_2SECONDS_SHIFT) + (((t / 60) % 60) << DT_MINUTES_SHIFT) + (((t / 3600) % 24) << DT_HOURS_SHIFT); @@ -117,7 +151,11 @@ unix2dostime(tsp, ddp, dtp) lastddate += (year - 1980) << DD_YEAR_SHIFT; } } - *dtp = lastdtime; + if (dtp) + *dtp = lastdtime; + if (dhp) + *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000; + *ddp = lastddate; } @@ -136,9 +174,10 @@ static u_long lastseconds; * not be too efficient. */ void -dos2unixtime(dd, dt, tsp) - u_short dd; - u_short dt; +dos2unixtime(dd, dt, dh, tsp) + u_int dd; + u_int dt; + u_int dh; struct timespec *tsp; { u_long seconds; @@ -147,9 +186,18 @@ dos2unixtime(dd, dt, tsp) u_long days; u_short *months; + if (dd == 0) { + /* + * Uninitialized field, return the epoch. + */ + tsp->tv_sec = 0; + tsp->tv_nsec = 0; + return; + } seconds = (((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) << 1) + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60 - + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600; + + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600 + + dh / 100; /* * If the year, month, and day from the last conversion are the * same then use the saved value. @@ -165,8 +213,7 @@ dos2unixtime(dd, dt, tsp) months = year & 0x03 ? regyear : leapyear; month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT; if (month < 1 || month > 12) { - printf( - "dos2unixtime(): month value out of range (%ld)\n", + printf("dos2unixtime(): month value out of range (%ld)\n", month); month = 1; } @@ -178,17 +225,116 @@ dos2unixtime(dd, dt, tsp) tsp->tv_sec = seconds + lastseconds + (tz.tz_minuteswest * 60) + adjkerntz; /* + daylight savings time correction */ - tsp->tv_nsec = 0; + tsp->tv_nsec = (dh % 100) * 10000000; } -/* - * 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 | ' ') +static u_char +unix2dos[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 00-07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 08-0f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 10-17 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 18-1f */ + 0, 0x21, 0, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */ + 0x28, 0x29, 0, 0, 0, 0x2d, 0, 0, /* 28-2f */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */ + 0x38, 0x39, 0, 0, 0, 0, 0, 0, /* 38-3f */ + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 40-47 */ + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 48-4f */ + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 50-57 */ + 0x58, 0x59, 0x5a, 0, 0, 0, 0x5e, 0x5f, /* 58-5f */ + 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 60-67 */ + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 68-6f */ + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 70-77 */ + 0x58, 0x59, 0x5a, 0x7b, 0, 0x7d, 0x7e, 0, /* 78-7f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 80-87 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 88-8f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 90-97 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 98-9f */ + 0, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, /* a0-a7 */ + 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee, /* a8-af */ + 0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, /* b0-b7 */ + 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8, /* b8-bf */ + 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* c0-c7 */ + 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* c8-cf */ + 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, /* d0-d7 */ + 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1, /* d8-df */ + 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* e0-e7 */ + 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* e8-ef */ + 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0xf6, /* f0-f7 */ + 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0x98, /* f8-ff */ +}; + +static u_char +dos2unix[256] = { + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 00-07 */ + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 08-0f */ + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 10-17 */ + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 18-1f */ + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 28-2f */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */ + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 38-3f */ + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 40-47 */ + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 48-4f */ + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 50-57 */ + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 58-5f */ + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 60-67 */ + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 68-6f */ + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 70-77 */ + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 78-7f */ + 0xc7, 0xfc, 0xe9, 0xe2, 0xe4, 0xe0, 0xe5, 0xe7, /* 80-87 */ + 0xea, 0xeb, 0xe8, 0xef, 0xee, 0xec, 0xc4, 0xc5, /* 88-8f */ + 0xc9, 0xe6, 0xc6, 0xf4, 0xf6, 0xf2, 0xfb, 0xf9, /* 90-97 */ + 0xff, 0xd6, 0xdc, 0xf8, 0xa3, 0xd8, 0xd7, 0x3f, /* 98-9f */ + 0xe1, 0xed, 0xf3, 0xfa, 0xf1, 0xd1, 0xaa, 0xba, /* a0-a7 */ + 0xbf, 0xae, 0xac, 0xbd, 0xbc, 0xa1, 0xab, 0xbb, /* a8-af */ + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xc1, 0xc2, 0xc0, /* b0-b7 */ + 0xa9, 0x3f, 0x3f, 0x3f, 0x3f, 0xa2, 0xa5, 0x3f, /* b8-bf */ + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xe3, 0xc3, /* c0-c7 */ + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xa4, /* c8-cf */ + 0xf0, 0xd0, 0xca, 0xcb, 0xc8, 0x3f, 0xcd, 0xce, /* d0-d7 */ + 0xcf, 0x3f, 0x3f, 0x3f, 0x3f, 0xa6, 0xcc, 0x3f, /* d8-df */ + 0xd3, 0xdf, 0xd4, 0xd2, 0xf5, 0xd5, 0xb5, 0xfe, /* e0-e7 */ + 0xde, 0xda, 0xdb, 0xd9, 0xfd, 0xdd, 0xaf, 0x3f, /* e8-ef */ + 0xad, 0xb1, 0x3f, 0xbe, 0xb6, 0xa7, 0xf7, 0xb8, /* f0-f7 */ + 0xb0, 0xa8, 0xb7, 0xb9, 0xb3, 0xb2, 0x3f, 0x3f, /* f8-ff */ +}; + +static u_char +u2l[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 00-07 */ + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 08-0f */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 10-17 */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 18-1f */ + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 28-2f */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */ + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 38-3f */ + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 40-47 */ + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 48-4f */ + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 50-57 */ + 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 58-5f */ + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 60-67 */ + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 68-6f */ + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 70-77 */ + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 78-7f */ + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 80-87 */ + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 88-8f */ + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 90-97 */ + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 98-9f */ + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* a0-a7 */ + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* a8-af */ + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* b0-b7 */ + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* b8-bf */ + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* c0-c7 */ + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* c8-cf */ + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* d0-d7 */ + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* d8-df */ + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* e0-e7 */ + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* e8-ef */ + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* f0-f7 */ + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* f8-ff */ +}; /* * DOS filenames are made of 2 parts, the name part and the extension part. @@ -203,58 +349,14 @@ dos2unixtime(dd, dt, tsp) * null. */ int -dos2unixfn(dn, un) +dos2unixfn(dn, un, lower) u_char dn[11]; u_char *un; + int lower; { int i; - int ni; - int ei; - int thislong = 0; + int thislong = 1; 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 @@ -262,31 +364,71 @@ dos2unixfn(dn, un) * 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; + if (*dn == SLOT_E5) + c = dos2unix[0xe5]; + else + c = dos2unix[*dn]; + *un++ = lower ? u2l[c] : c; + dn++; - return thislong; + /* + * Copy the name portion into the unix filename string. + */ + for (i = 1; i < 8 && *dn != ' '; i++) { + c = dos2unix[*dn++]; + *un++ = lower ? u2l[c] : c; + thislong++; + } + dn += 8 - i; + + /* + * Now, if there is an extension then put in a period and copy in + * the extension. + */ + if (*dn != ' ') { + *un++ = '.'; + thislong++; + for (i = 0; i < 3 && *dn != ' '; i++) { + c = dos2unix[*dn++]; + *un++ = lower ? u2l[c] : c; + thislong++; + } + } + *un++ = 0; + + return (thislong); } /* - * Convert a unix filename to a DOS filename. This function does not ensure - * that valid characters for a dos filename are supplied. + * Convert a unix filename to a DOS filename according to Win95 rules. + * If applicable and gen is not 0, it is inserted into the converted + * filename as a generation number. + * Returns + * 0 if name couldn't be converted + * 1 if the converted name is the same as the original + * (no long filename entry necessary for Win95) + * 2 if conversion was successful + * 3 if conversion was successful and generation number was inserted */ -void -unix2dosfn(un, dn, unlen) - u_char *un; - u_char dn[11]; +int +unix2dosfn(un, dn, unlen, gen) + const u_char *un; + u_char dn[12]; int unlen; + u_int gen; { - int i; - u_char c; + int i, j, l; + int conv = 1; + const u_char *cp, *dp, *dp1; + u_char gentext[6], *wcp; /* * Fill the dos filename string with blanks. These are DOS's pad * characters. */ - for (i = 0; i <= 10; i++) + for (i = 0; i < 11; i++) dn[i] = ' '; + dn[11] = 0; /* * The filenames "." and ".." are handled specially, since they @@ -294,65 +436,393 @@ unix2dosfn(un, dn, unlen) */ if (un[0] == '.' && unlen == 1) { dn[0] = '.'; - return; + return gen <= 1; } if (un[0] == '.' && un[1] == '.' && unlen == 2) { dn[0] = '.'; dn[1] = '.'; - return; + return gen <= 1; } /* - * 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. + * Filenames with only blanks and dots are not allowed! */ - for (i = 0; i <= 7 && unlen && (c = *un) && c != '.'; i++) { - dn[i] = islower(c) ? toupper(c) : c; - un++; - unlen--; + for (cp = un, i = unlen; --i >= 0; cp++) + if (*cp != ' ' && *cp != '.') + break; + if (i < 0) + return 0; + + /* + * Now find the extension + * Note: dot as first char doesn't start extension + * and trailing dots and blanks are ignored + */ + dp = dp1 = 0; + for (cp = un + 1, i = unlen - 1; --i >= 0;) { + switch (*cp++) { + case '.': + if (!dp1) + dp1 = cp; + break; + case ' ': + break; + default: + if (dp1) + dp = dp1; + dp1 = 0; + break; + } } /* - * 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. + * Now convert it */ - if (dn[0] == SLOT_DELETED) + if (dp) { + if (dp1) + l = dp1 - dp; + else + l = unlen - (dp - un); + for (i = 0, j = 8; i < l && j < 11; i++, j++) { + if (dp[i] != (dn[j] = unix2dos[dp[i]]) + && conv != 3) + conv = 2; + if (!dn[j]) { + conv = 3; + dn[j--] = ' '; + } + } + if (i < l) + conv = 3; + dp--; + } else { + for (dp = cp; *--dp == ' ' || *dp == '.';); + dp++; + } + + /* + * Now convert the rest of the name + */ + for (i = j = 0; un < dp && j < 8; i++, j++, un++) { + if (*un != (dn[j] = unix2dos[*un]) + && conv != 3) + conv = 2; + if (!dn[j]) { + conv = 3; + dn[j--] = ' '; + } + } + if (un < dp) + conv = 3; + /* + * If we didn't have any chars in filename, + * generate a default + */ + if (!j) + dn[0] = '_'; + + /* + * The first character cannot be E5, + * because that means a deleted entry + */ + if (dn[0] == 0xe5) dn[0] = SLOT_E5; /* - * Strip any further characters up to a '.' or the end of the - * string. + * If there wasn't any char dropped, + * there is no place for generation numbers */ - while (unlen && (c = *un)) { - un++; - unlen--; - /* Make sure we've skipped over the dot before stopping. */ - if (c == '.') - break; + if (conv != 3) { + if (gen > 1) + return 0; + return conv; } /* - * 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. + * Now insert the generation number into the filename part */ - for (i = 8; i <= 10 && unlen && (c = *un); i++) { - dn[i] = islower(c) ? toupper(c) : c; - un++; - unlen--; - } + for (wcp = gentext + sizeof(gentext); wcp > gentext && gen; gen /= 10) + *--wcp = gen % 10 + '0'; + if (gen) + return 0; + for (i = 8; dn[--i] == ' ';); + i++; + if (gentext + sizeof(gentext) - wcp + 1 > 8 - i) + i = 8 - (gentext + sizeof(gentext) - wcp + 1); + dn[i++] = '~'; + while (wcp < gentext + sizeof(gentext)) + dn[i++] = *wcp++; + return 3; } /* - * Get rid of these macros before someone discovers we are using such - * hideous things. + * Create a Win95 long name directory entry + * Note: assumes that the filename is valid, + * i.e. doesn't consist solely of blanks and dots */ -#undef isupper -#undef islower -#undef toupper -#undef tolower +int +unix2winfn(un, unlen, wep, cnt, chksum) + const u_char *un; + int unlen; + struct winentry *wep; + int cnt; + int chksum; +{ + const u_int8_t *cp; + u_int8_t *wcp; + int i; + + /* + * Drop trailing blanks and dots + */ + for (cp = un + unlen; *--cp == ' ' || *cp == '.'; unlen--); + + un += (cnt - 1) * WIN_CHARS; + unlen -= (cnt - 1) * WIN_CHARS; + + /* + * Initialize winentry to some useful default + */ + for (wcp = (u_int8_t *)wep, i = sizeof(*wep); --i >= 0; *wcp++ = 0xff); + wep->weCnt = cnt; + wep->weAttributes = ATTR_WIN95; + wep->weReserved1 = 0; + wep->weChksum = chksum; + wep->weReserved2 = 0; + + /* + * Now convert the filename parts + */ + for (wcp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0;) { + if (--unlen < 0) + goto done; + *wcp++ = *un++; + *wcp++ = 0; + } + for (wcp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) { + if (--unlen < 0) + goto done; + *wcp++ = *un++; + *wcp++ = 0; + } + for (wcp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) { + if (--unlen < 0) + goto done; + *wcp++ = *un++; + *wcp++ = 0; + } + if (!unlen) + wep->weCnt |= WIN_LAST; + return unlen; + +done: + *wcp++ = 0; + *wcp++ = 0; + wep->weCnt |= WIN_LAST; + return 0; +} + +/* + * Compare our filename to the one in the Win95 entry + * Returns the checksum or -1 if no match + */ +int +winChkName(un, unlen, wep, chksum) + const u_char *un; + int unlen; + struct winentry *wep; + int chksum; +{ + u_int8_t *cp; + int i; + + /* + * First compare checksums + */ + if (wep->weCnt&WIN_LAST) + chksum = wep->weChksum; + else if (chksum != wep->weChksum) + chksum = -1; + if (chksum == -1) + return -1; + + /* + * Offset of this entry + */ + i = ((wep->weCnt&WIN_CNT) - 1) * WIN_CHARS; + un += i; + if ((unlen -= i) <= 0) + return -1; + if ((wep->weCnt&WIN_LAST) && unlen > WIN_CHARS) + return -1; + + /* + * Compare the name parts + */ + for (cp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0;) { + if (--unlen < 0) { + if (!*cp++ && !*cp) + return chksum; + return -1; + } + if (u2l[*cp++] != u2l[*un++] || *cp++) + return -1; + } + for (cp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) { + if (--unlen < 0) { + if (!*cp++ && !*cp) + return chksum; + return -1; + } + if (u2l[*cp++] != u2l[*un++] || *cp++) + return -1; + } + for (cp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) { + if (--unlen < 0) { + if (!*cp++ && !*cp) + return chksum; + return -1; + } + if (u2l[*cp++] != u2l[*un++] || *cp++) + return -1; + } + return chksum; +} + +/* + * Convert Win95 filename to dirbuf. + * Returns the checksum or -1 if impossible + */ +int +win2unixfn(wep, dp, chksum) + struct winentry *wep; + struct dirent *dp; + int chksum; +{ + u_int8_t *cp; + u_int8_t *np, *ep = dp->d_name + WIN_MAXLEN; + int i; + + if ((wep->weCnt&WIN_CNT) > howmany(WIN_MAXLEN, WIN_CHARS) + || !(wep->weCnt&WIN_CNT)) + return -1; + + /* + * First compare checksums + */ + if (wep->weCnt&WIN_LAST) { + chksum = wep->weChksum; + /* + * This works even though d_namlen is one byte! + */ + dp->d_namlen = (wep->weCnt&WIN_CNT) * WIN_CHARS; + } else if (chksum != wep->weChksum) + chksum = -1; + if (chksum == -1) + return -1; + + /* + * Offset of this entry + */ + i = ((wep->weCnt&WIN_CNT) - 1) * WIN_CHARS; + np = (u_int8_t *)dp->d_name + i; + + /* + * Convert the name parts + */ + for (cp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0;) { + switch (*np++ = *cp++) { + case 0: + dp->d_namlen -= sizeof(wep->wePart2)/2 + + sizeof(wep->wePart3)/2 + i + 1; + return chksum; + case '/': + np[-1] = 0; + return -1; + } + /* + * The size comparison should result in the compiler + * optimizing the whole if away + */ + if (WIN_MAXLEN % WIN_CHARS < sizeof(wep->wePart1) / 2 + && np > ep) { + np[-1] = 0; + return -1; + } + if (*cp++) + return -1; + } + for (cp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) { + switch (*np++ = *cp++) { + case 0: + dp->d_namlen -= sizeof(wep->wePart3)/2 + i + 1; + return chksum; + case '/': + np[-1] = 0; + return -1; + } + /* + * The size comparisons should be optimized away + */ + if (WIN_MAXLEN % WIN_CHARS >= sizeof(wep->wePart1) / 2 + && WIN_MAXLEN % WIN_CHARS < (sizeof(wep->wePart1) + sizeof(wep->wePart2)) / 2 + && np > ep) { + np[-1] = 0; + return -1; + } + if (*cp++) + return -1; + } + for (cp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) { + switch (*np++ = *cp++) { + case 0: + dp->d_namlen -= i + 1; + return chksum; + case '/': + np[-1] = 0; + return -1; + } + /* + * See above + */ + if (WIN_MAXLEN % WIN_CHARS >= (sizeof(wep->wePart1) + sizeof(wep->wePart2)) / 2 + && np > ep) { + np[-1] = 0; + return -1; + } + if (*cp++) + return -1; + } + return chksum; +} + +/* + * Compute the checksum of a DOS filename for Win95 use + */ +u_int8_t +winChksum(name) + u_int8_t *name; +{ + int i; + u_int8_t s; + + for (s = 0, i = 11; --i >= 0; s += *name++) + s = (s << 7)|(s >> 1); + return s; +} + +/* + * Determine the number of slots necessary for Win95 names + */ +int +winSlotCnt(un, unlen) + const u_char *un; + int unlen; +{ + for (un += unlen; unlen > 0; unlen--) + if (*--un != ' ' && *un != '.') + break; + if (unlen > WIN_MAXLEN) + return 0; + return howmany(unlen, WIN_CHARS); +} diff --git a/sys/msdosfs/msdosfs_denode.c b/sys/msdosfs/msdosfs_denode.c index 81e06992a6e9..6feabbb17697 100644 --- a/sys/msdosfs/msdosfs_denode.c +++ b/sys/msdosfs/msdosfs_denode.c @@ -1,9 +1,9 @@ -/* $Id: msdosfs_denode.c,v 1.30 1998/02/06 12:13:46 eivind Exp $ */ -/* $NetBSD: msdosfs_denode.c,v 1.9 1994/08/21 18:44:00 ws Exp $ */ +/* $Id: msdosfs_denode.c,v 1.31 1998/02/09 06:09:51 eivind Exp $ */ +/* $NetBSD: msdosfs_denode.c,v 1.28 1998/02/10 14:10:00 mrg Exp $ */ /*- - * Copyright (C) 1994 Wolfgang Solfrank. - * Copyright (C) 1994 TooLs GmbH. + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * @@ -69,8 +69,9 @@ static MALLOC_DEFINE(M_MSDOSFSNODE, "MSDOSFS node", "MSDOSFS vnode private part"); static struct denode **dehashtbl; -static u_long dehash; /* size of hash table - 1 */ -#define DEHASH(dev, deno) (dehashtbl[((dev) + (deno)) & dehash]) +static u_long dehash; /* size of hash table - 1 */ +#define DEHASH(dev, dcl, doff) (dehashtbl[((dev) + (dcl) + (doff) / \ + sizeof(struct direntry)) & dehash]) static struct simplelock dehash_slock; union _qcvt { @@ -96,12 +97,14 @@ static struct denode * static void msdosfs_hashins __P((struct denode *dep)); static void msdosfs_hashrem __P((struct denode *dep)); -int msdosfs_init(vfsp) +/*ARGSUSED*/ +int +msdosfs_init(vfsp) struct vfsconf *vfsp; { dehashtbl = hashinit(desiredvnodes/2, M_MSDOSFSMNT, &dehash); simple_lock_init(&dehash_slock); - return 0; + return (0); } static struct denode * @@ -116,7 +119,7 @@ msdosfs_hashget(dev, dirclust, diroff) loop: simple_lock(&dehash_slock); - for (dep = DEHASH(dev, dirclust + diroff); dep; dep = dep->de_next) { + for (dep = DEHASH(dev, dirclust, diroff); dep; dep = dep->de_next) { if (dirclust == dep->de_dirclust && diroff == dep->de_diroffset && dev == dep->de_dev @@ -140,7 +143,7 @@ msdosfs_hashins(dep) struct denode **depp, *deq; simple_lock(&dehash_slock); - depp = &DEHASH(dep->de_dev, dep->de_dirclust + dep->de_diroffset); + depp = &DEHASH(dep->de_dev, dep->de_dirclust, dep->de_diroffset); deq = *depp; if (deq) deq->de_prev = &dep->de_next; @@ -178,48 +181,41 @@ msdosfs_hashrem(dep) * 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) +deget(pmp, dirclust, diroffset, 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; + struct direntry *direntptr; struct denode *ldep; struct vnode *nvp; struct buf *bp; struct proc *p = curproc; /* XXX */ #ifdef MSDOSFS_DEBUG - printf("deget(pmp %p, dirclust %ld, diroffset %x, direntptr %p, depp %p)\n", - pmp, dirclust, diroffset, direntptr, depp); + printf("deget(pmp %p, dirclust %lu, diroffset %lx, depp %p)\n", + pmp, dirclust, diroffset, depp); #endif /* - * If dir entry is given and refers to a directory, convert to - * canonical form + * On FAT32 filesystems, root is a (more or less) normal + * directory */ - if (direntptr && (direntptr->deAttributes & ATTR_DIRECTORY)) { - dirclust = getushort(direntptr->deStartCluster); - if (dirclust == MSDOSFSROOT) - diroffset = MSDOSFSROOT_OFS; - else - diroffset = 0; - } + if (FAT32(pmp) && dirclust == MSDOSFSROOT) + dirclust = pmp->pm_rootdirblk; /* * 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 + * address of "." entry. For root dir (if not FAT32) 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. @@ -230,7 +226,7 @@ deget(pmp, dirclust, diroffset, direntptr, depp) ldep = msdosfs_hashget(dev, dirclust, diroffset); if (ldep) { *depp = ldep; - return 0; + return (0); } /* @@ -277,10 +273,15 @@ deget(pmp, dirclust, diroffset, direntptr, depp) */ msdosfs_hashins(ldep); + ldep->de_pmp = pmp; + ldep->de_devvp = pmp->pm_devvp; + ldep->de_refcnt = 1; /* * Copy the directory entry into the denode area of the vnode. */ - if (dirclust == MSDOSFSROOT && diroffset == MSDOSFSROOT_OFS) { + if ((dirclust == MSDOSFSROOT + || (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)) + && diroffset == MSDOSFSROOT_OFS) { /* * Directory entry for the root directory. There isn't one, * so we manufacture one. We should probably rummage @@ -288,39 +289,42 @@ deget(pmp, dirclust, diroffset, direntptr, depp) * exists), and then use the time and date from that entry * as the time and date for the root denode. */ + nvp->v_flag |= VROOT; /* should be further down XXX */ + ldep->de_Attributes = ATTR_DIRECTORY; - ldep->de_StartCluster = MSDOSFSROOT; - ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec; + if (FAT32(pmp)) + ldep->de_StartCluster = pmp->pm_rootdirblk; + /* de_FileSize will be filled in further down */ + else { + 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) + ldep->de_CHun = 0; + ldep->de_CTime = 0x0000; /* 00:00:00 */ + ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT) | (1 << DD_DAY_SHIFT); /* Jan 1, 1980 */ + ldep->de_ADate = ldep->de_CDate; + ldep->de_MTime = ldep->de_CTime; + ldep->de_MDate = ldep->de_CDate; /* leave the other fields as garbage */ } else { - bp = NULL; - if (!direntptr) { - error = readep(pmp, dirclust, diroffset, &bp, - &direntptr); - if (error) - return error; - } + error = readep(pmp, dirclust, diroffset, &bp, &direntptr); + if (error) + return (error); DE_INTERNALIZE(ldep, direntptr); - if (bp) - brelse(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 @@ -331,12 +335,10 @@ deget(pmp, dirclust, diroffset, direntptr, depp) u_long size; nvp->v_type = VDIR; - if (ldep->de_StartCluster == MSDOSFSROOT) - nvp->v_flag |= VROOT; - else { - error = pcbmap(ldep, 0xffff, 0, &size); + if (ldep->de_StartCluster != MSDOSFSROOT) { + error = pcbmap(ldep, 0xffff, 0, &size, 0); if (error == E2BIG) { - ldep->de_FileSize = size << pmp->pm_cnshift; + ldep->de_FileSize = de_cn2off(pmp, size); error = 0; } else printf("deget(): pcbmap returned %d\n", error); @@ -347,78 +349,40 @@ deget(pmp, dirclust, diroffset, direntptr, depp) SETLOW(ldep->de_modrev, mono_time.tv_usec * 4294); VREF(ldep->de_devvp); *depp = ldep; - return 0; + return (0); } int -deupdat(dep, tp, waitfor) +deupdat(dep, waitfor) struct denode *dep; - struct timespec *tp; int waitfor; { int error; struct buf *bp; struct direntry *dirp; - struct vnode *vp = DETOV(dep); + struct timespec ts; -#ifdef MSDOSFS_DEBUG - printf("deupdat(): dep %p\n", dep); -#endif - - /* - * If the denode-modified and update-mtime bits are 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_MODIFIED | 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 (DETOV(dep)->v_mount->mnt_flag & MNT_RDONLY) + return (0); + TIMEVAL_TO_TIMESPEC(&time, &ts); + DETIMES(dep, &ts, &ts, &ts); + if ((dep->de_flag & DE_MODIFIED) == 0) + return (0); + dep->de_flag &= ~DE_MODIFIED; + if (dep->de_Attributes & ATTR_DIRECTORY) + return (0); + if (dep->de_refcnt <= 0) + return (0); error = readde(dep, &bp, &dirp); if (error) - return error; - - /* - * If the mtime is to be updated, put the passed in time into the - * directory entry. - */ - if (dep->de_flag & DE_UPDATE) { - dep->de_Attributes |= ATTR_ARCHIVE; - unix2dostime(tp, &dep->de_Date, &dep->de_Time); - } - - /* - * The mtime is now up to date. The denode will be unmodifed soon. - */ - dep->de_flag &= ~(DE_MODIFIED | DE_UPDATE); - - /* - * Copy the directory entry out of the denode into the cluster it - * came from. - */ + return (error); 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 + return (bwrite(bp)); + else { bdwrite(bp); - return error; + return (0); + } } /* @@ -445,7 +409,7 @@ detrunc(dep, length, flags, cred, p) struct timespec ts; #ifdef MSDOSFS_DEBUG - printf("detrunc(): file %s, length %d, flags %d\n", dep->de_Name, length, flags); + printf("detrunc(): file %s, length %lu, flags %x\n", dep->de_Name, length, flags); #endif /* @@ -456,11 +420,10 @@ detrunc(dep, length, flags, cred, p) * 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 %ld, offset %ld\n", + if ((DETOV(dep)->v_flag & VROOT) && !FAT32(pmp)) { + printf("detrunc(): can't truncate root directory, clust %ld, offset %ld\n", dep->de_dirclust, dep->de_diroffset); - return EINVAL; + return (EINVAL); } @@ -483,16 +446,17 @@ detrunc(dep, length, flags, cred, p) dep->de_StartCluster = 0; eofentry = ~0; } else { - error = pcbmap(dep, de_clcount(pmp, length) - 1, 0, &eofentry); + error = pcbmap(dep, de_clcount(pmp, length) - 1, 0, + &eofentry, 0); if (error) { #ifdef MSDOSFS_DEBUG printf("detrunc(): pcbmap fails %d\n", error); #endif - return error; + return (error); } } - fc_purge(dep, (length + pmp->pm_crbomask) >> pmp->pm_cnshift); + fc_purge(dep, de_clcount(pmp, length)); /* * If the new length is not a multiple of the cluster size then we @@ -500,10 +464,6 @@ detrunc(dep, length, flags, cred, p) * 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, @@ -521,10 +481,11 @@ detrunc(dep, length, flags, cred, p) NOCRED, &bp); } if (error) { + brelse(bp); #ifdef MSDOSFS_DEBUG printf("detrunc(): bread fails %d\n", error); #endif - return error; + return (error); } /* * is this the right place for it? @@ -541,14 +502,14 @@ detrunc(dep, length, flags, cred, p) * we free the trailing clusters. */ dep->de_FileSize = length; - dep->de_flag |= DE_UPDATE; + if (!isadir) + dep->de_flag |= DE_UPDATE|DE_MODIFIED; vflags = (length > 0 ? V_SAVE : 0) | V_SAVEMETA; vinvalbuf(DETOV(dep), vflags, cred, p, 0, 0); vnode_pager_setsize(DETOV(dep), length); - TIMEVAL_TO_TIMESPEC(&time, &ts); - allerror = deupdat(dep, &ts, 1); + allerror = deupdat(dep, 1); #ifdef MSDOSFS_DEBUG - printf("detrunc(): allerror %d, eofentry %d\n", + printf("detrunc(): allerror %d, eofentry %lu\n", allerror, eofentry); #endif @@ -563,9 +524,9 @@ detrunc(dep, length, flags, cred, p) #ifdef MSDOSFS_DEBUG printf("detrunc(): fatentry errors %d\n", error); #endif - return error; + return (error); } - fc_setcache(dep, FC_LASTFC, (length - 1) >> pmp->pm_cnshift, + fc_setcache(dep, FC_LASTFC, de_cluster(pmp, length - 1), eofentry); } @@ -573,10 +534,10 @@ detrunc(dep, length, flags, cred, p) * Now free the clusters removed from the file because of the * truncation. */ - if (chaintofree != 0 && !MSDOSFSEOF(chaintofree)) + if (chaintofree != 0 && !MSDOSFSEOF(pmp, chaintofree)) freeclusterchain(pmp, chaintofree); - return allerror; + return (allerror); } /* @@ -585,7 +546,7 @@ detrunc(dep, length, flags, cred, p) int deextend(dep, length, cred) struct denode *dep; - off_t length; + u_long length; struct ucred *cred; { struct msdosfsmount *pmp = dep->de_pmp; @@ -596,18 +557,14 @@ deextend(dep, length, cred) /* * The root of a DOS filesystem cannot be extended. */ - if (DETOV(dep)->v_flag & VROOT) - return EINVAL; + if ((DETOV(dep)->v_flag & VROOT) && !FAT32(pmp)) + return (EINVAL); /* - * Directories can only be extended by the superuser. - * Is this really important? + * Directories cannot be extended. */ - if (dep->de_Attributes & ATTR_DIRECTORY) { - error = suser(cred, NULL); - if (error) - return error; - } + if (dep->de_Attributes & ATTR_DIRECTORY) + return (EISDIR); if (length <= dep->de_FileSize) panic("deextend: file too large"); @@ -618,26 +575,25 @@ deextend(dep, length, cred) count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize); if (count > 0) { if (count > pmp->pm_freeclustercount) - return ENOSPC; + return (ENOSPC); error = extendfile(dep, count, NULL, NULL, DE_CLEAR); if (error) { /* truncate the added clusters away again */ (void) detrunc(dep, dep->de_FileSize, 0, cred, NULL); - return error; + return (error); } } - - dep->de_flag |= DE_UPDATE; dep->de_FileSize = length; - TIMEVAL_TO_TIMESPEC(&time, &ts); - return deupdat(dep, &ts, 1); + dep->de_flag |= DE_UPDATE|DE_MODIFIED; + return (deupdat(dep, 1)); } /* * Move a denode to its correct hash queue after the file it represents has * been moved to a new directory. */ -int reinsert(dep) +void +reinsert(dep) struct denode *dep; { /* @@ -648,11 +604,10 @@ int reinsert(dep) * 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; + if (dep->de_Attributes & ATTR_DIRECTORY) + return; + msdosfs_hashrem(dep); + msdosfs_hashins(dep); } int @@ -671,27 +626,25 @@ msdosfs_reclaim(ap) if (prtactive && vp->v_usecount != 0) vprint("msdosfs_reclaim(): pushing active", vp); - /* - * Remove the denode from the denode hash chain we are in. + * Remove the denode from its hash chain. */ msdosfs_hashrem(dep); - - cache_purge(vp); /* - * Indicate that one less file on the filesystem is open. + * Purge old data structures associated with the denode. */ + cache_purge(vp); if (dep->de_devvp) { vrele(dep->de_devvp); dep->de_devvp = 0; } - +#if 0 /* XXX */ dep->de_flag = 0; - +#endif FREE(dep, M_MSDOSFSNODE); vp->v_data = NULL; - return 0; + return (0); } int @@ -715,7 +668,7 @@ msdosfs_inactive(ap) vprint("msdosfs_inactive(): pushing active", vp); /* - * Ignore inodes related to stale file handles. + * Ignore denodes related to stale file handles. */ if (dep->de_Name[0] == SLOT_DELETED) goto out; @@ -734,17 +687,13 @@ msdosfs_inactive(ap) dep->de_flag |= DE_UPDATE; dep->de_Name[0] = SLOT_DELETED; } - if (dep->de_flag & (DE_MODIFIED | DE_UPDATE)) { - TIMEVAL_TO_TIMESPEC(&time, &ts); - deupdat(dep, &ts, 0); - } + deupdat(dep, 0); + out: VOP_UNLOCK(vp, 0, p); - dep->de_flag = 0; - /* - * If we are done with the denode, then reclaim it so that it can - * be reused now. + * If we are done with the denode, reclaim it + * so that it can be reused immediately. */ #ifdef MSDOSFS_DEBUG printf("msdosfs_inactive(): v_usecount %d, de_Name[0] %x\n", vp->v_usecount, @@ -752,5 +701,5 @@ out: #endif if (dep->de_Name[0] == SLOT_DELETED) vrecycle(vp, (struct simplelock *)0, p); - return error; + return (error); } diff --git a/sys/msdosfs/msdosfs_fat.c b/sys/msdosfs/msdosfs_fat.c index bb25ec22779a..f5568605d74d 100644 --- a/sys/msdosfs/msdosfs_fat.c +++ b/sys/msdosfs/msdosfs_fat.c @@ -1,9 +1,9 @@ -/* $Id: msdosfs_fat.c,v 1.15 1998/02/06 12:13:46 eivind Exp $ */ -/* $NetBSD: msdosfs_fat.c,v 1.12 1994/08/21 18:44:04 ws Exp $ */ +/* $Id: msdosfs_fat.c,v 1.16 1998/02/09 06:09:52 eivind Exp $ */ +/* $NetBSD: msdosfs_fat.c,v 1.28 1997/11/17 15:36:49 ws Exp $ */ /*- - * Copyright (C) 1994 Wolfgang Solfrank. - * Copyright (C) 1994 TooLs GmbH. + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * @@ -56,6 +56,7 @@ #include #include /* to define statfs structure */ #include /* to define vattr structure */ +#include /* * msdosfs include files. @@ -79,9 +80,6 @@ static int fc_lmdistance[LMMAX];/* counters for how far off the last * cluster mapped entry was. */ static 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) - static int chainalloc __P((struct msdosfsmount *pmp, u_long start, u_long count, u_long fillwith, u_long *retcluster, u_long *got)); @@ -111,7 +109,8 @@ fatblock(pmp, ofs, bnp, sizep, bop) bn = ofs / pmp->pm_fatblocksize * pmp->pm_fatblocksec; size = min(pmp->pm_fatblocksec, pmp->pm_FATsecs - bn) * pmp->pm_BytesPerSec; - bn += pmp->pm_fatblk; + bn += pmp->pm_fatblk + pmp->pm_curfat * pmp->pm_FATsecs; + if (bnp) *bnp = bn; if (sizep) @@ -139,16 +138,17 @@ fatblock(pmp, ofs, bnp, sizep, bop) * If cnp is null, nothing is returned. */ int -pcbmap(dep, findcn, bnp, cnp) +pcbmap(dep, findcn, bnp, cnp, sp) 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 *sp; /* returned block size */ { int error; u_long i; u_long cn; - u_long prevcn; + u_long prevcn = 0; /* XXX: prevcn could be used unititialized */ u_long byteoffset; u_long bn; u_long bo; @@ -156,7 +156,6 @@ pcbmap(dep, findcn, bnp, cnp) u_long bp_bn = -1; struct msdosfsmount *pmp = dep->de_pmp; u_long bsize; - int fat12 = FAT12(pmp); /* 12 bit fat */ fc_bmapcalls++; @@ -164,8 +163,8 @@ pcbmap(dep, findcn, bnp, cnp) * If they don't give us someplace to return a value then don't * bother doing anything. */ - if (bnp == NULL && cnp == NULL) - return 0; + if (bnp == NULL && cnp == NULL && sp == NULL) + return (0); cn = dep->de_StartCluster; /* @@ -176,23 +175,32 @@ pcbmap(dep, findcn, bnp, cnp) */ if (cn == MSDOSFSROOT) { if (dep->de_Attributes & ATTR_DIRECTORY) { - if (findcn * pmp->pm_SectPerClust >= pmp->pm_rootdirsize) { + if (de_cn2off(pmp, findcn) >= dep->de_FileSize) { if (cnp) - *cnp = pmp->pm_rootdirsize / pmp->pm_SectPerClust; - return E2BIG; + *cnp = de_bn2cn(pmp, pmp->pm_rootdirsize); + return (E2BIG); } if (bnp) - *bnp = pmp->pm_rootdirblk + (findcn * pmp->pm_SectPerClust); + *bnp = pmp->pm_rootdirblk + de_cn2bn(pmp, findcn); if (cnp) *cnp = MSDOSFSROOT; - return 0; + if (sp) + *sp = min(pmp->pm_bpcluster, + dep->de_FileSize - de_cn2off(pmp, findcn)); + return (0); } else { /* just an empty file */ if (cnp) *cnp = 0; - return E2BIG; + return (E2BIG); } } + /* + * All other files do I/O in cluster sized blocks + */ + if (sp) + *sp = pmp->pm_bpcluster; + /* * Rummage around in the fat cache, maybe we can avoid tromping * thru every fat entry for the file. And, keep track of how far @@ -208,9 +216,11 @@ pcbmap(dep, findcn, bnp, cnp) /* * Handle all other files or directories the normal way. */ - prevcn = 0; for (; i < findcn; i++) { - if (MSDOSFSEOF(cn)) + /* + * Stop with all reserved clusters, not just with EOF. + */ + if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD) goto hiteof; byteoffset = FATOFS(pmp, cn); fatblock(pmp, byteoffset, &bn, &bsize, &bo); @@ -218,28 +228,32 @@ pcbmap(dep, findcn, bnp, cnp) if (bp) brelse(bp); error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); - if (error) - return error; + if (error) { + brelse(bp); + 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 (FAT32(pmp)) + cn = getulong(&bp->b_data[bo]); + else + cn = getushort(&bp->b_data[bo]); + if (FAT12(pmp) && (prevcn & 1)) + cn >>= 4; + cn &= pmp->pm_fatmask; + + /* + * Force the special cluster numbers + * to be the same for all cluster sizes + * to let the rest of msdosfs handle + * all cases the same. + */ + if ((cn | ~pmp->pm_fatmask) >= CLUST_RSRVD) + cn |= ~pmp->pm_fatmask; } - if (!MSDOSFSEOF(cn)) { + if (!MSDOSFSEOF(pmp, cn)) { if (bp) brelse(bp); if (bnp) @@ -247,7 +261,7 @@ pcbmap(dep, findcn, bnp, cnp) if (cnp) *cnp = cn; fc_setcache(dep, FC_LASTMAP, i, cn); - return 0; + return (0); } hiteof:; @@ -257,7 +271,7 @@ hiteof:; brelse(bp); /* update last file cluster entry in the fat cache */ fc_setcache(dep, FC_LASTFC, i - 1, prevcn); - return E2BIG; + return (E2BIG); } /* @@ -292,7 +306,8 @@ fc_lookup(dep, findcn, frcnp, fsrcnp) * Purge the fat cache in denode dep of all entries relating to file * relative cluster frcn and beyond. */ -void fc_purge(dep, frcn) +void +fc_purge(dep, frcn) struct denode *dep; u_int frcn; { @@ -307,7 +322,9 @@ void fc_purge(dep, frcn) } /* - * Update all copies of the fat. The first copy is updated last. + * Update the fat. + * If mirroring the fat, update all copies, with the first copy as last. + * Else update only the current fat (ignoring the others). * * pmp - msdosfsmount structure for filesystem to update * bp - addr of modified fat block @@ -323,36 +340,80 @@ updatefats(pmp, bp, fatbn) struct buf *bpn; #ifdef MSDOSFS_DEBUG - printf("updatefats(pmp %p, bp %p, fatbn %ld)\n", pmp, bp, fatbn); + printf("updatefats(pmp %p, bp %p, fatbn %lu)\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. + * If we have an FSInfo block, update it. */ - 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); + if (pmp->pm_fsinfo) { + u_long cn = pmp->pm_nxtfree; + + if (pmp->pm_freeclustercount + && (pmp->pm_inusemap[cn / N_INUSEBITS] + & (1 << (cn % N_INUSEBITS)))) { + /* + * The cluster indicated in FSInfo isn't free + * any longer. Got get a new free one. + */ + for (cn = 0; cn < pmp->pm_maxcluster;) + if (pmp->pm_inusemap[cn / N_INUSEBITS] != (u_int)-1) + break; + pmp->pm_nxtfree = cn + + ffs(pmp->pm_inusemap[cn / N_INUSEBITS] + ^ (u_int)-1) - 1; + } + if (bread(pmp->pm_devvp, pmp->pm_fsinfo, 1024, NOCRED, &bpn) != 0) { + /* + * Ignore the error, but turn off FSInfo update for the future. + */ + pmp->pm_fsinfo = 0; + brelse(bpn); + } else { + struct fsinfo *fp = (struct fsinfo *)bpn->b_data; + + putulong(fp->fsinfree, pmp->pm_freeclustercount); + putulong(fp->fsinxtfree, pmp->pm_nxtfree); + if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT) + bwrite(bpn); + else + bdwrite(bpn); + } } + + if (pmp->pm_flags & MSDOSFS_FATMIRROR) { + /* + * 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_flags & MSDOSFSMNT_WAITONFAT) + bwrite(bpn); + else + bdwrite(bpn); + } + } + /* - * Write out the first fat last. + * Write out the first (or current) fat last. */ - if (pmp->pm_waitonfat) + if (pmp->pm_flags & MSDOSFSMNT_WAITONFAT) bwrite(bp); else bdwrite(bp); + /* + * Maybe update fsinfo sector here? + */ } /* @@ -379,8 +440,8 @@ usemap_alloc(pmp, cn) struct msdosfsmount *pmp; u_long cn; { - pmp->pm_inusemap[cn / N_INUSEBITS] - |= 1 << (cn % N_INUSEBITS); + + pmp->pm_inusemap[cn / N_INUSEBITS] |= 1 << (cn % N_INUSEBITS); pmp->pm_freeclustercount--; } @@ -389,6 +450,7 @@ usemap_free(pmp, cn) struct msdosfsmount *pmp; u_long cn; { + pmp->pm_freeclustercount++; pmp->pm_inusemap[cn / N_INUSEBITS] &= ~(1 << (cn % N_INUSEBITS)); } @@ -402,18 +464,20 @@ clusterfree(pmp, cluster, oldcnp) int error; u_long oldcn; + usemap_free(pmp, cluster); 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; + if (error) { + usemap_alloc(pmp, cluster); + return (error); } - return error; + /* + * 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. + */ + if (oldcnp) + *oldcnp = oldcn; + return (0); } /* @@ -448,10 +512,10 @@ fatentry(function, pmp, cn, oldcontents, newcontents) 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 MSDOSFS_DEBUG + printf("fatentry(func %d, pmp %p, clust %lu, oldcon %p, newcon %lx)\n", + function, pmp, cn, oldcontents, newcontents); +#endif #ifdef DIAGNOSTIC /* @@ -459,7 +523,7 @@ fatentry(function, pmp, cn, oldcontents, newcontents) */ if ((function & (FAT_SET | FAT_GET)) == 0) { printf("fatentry(): function code doesn't specify get or set\n"); - return EINVAL; + return (EINVAL); } /* @@ -468,7 +532,7 @@ fatentry(function, pmp, cn, oldcontents, newcontents) */ if ((function & FAT_GET) && oldcontents == NULL) { printf("fatentry(): get function with no place to put result\n"); - return EINVAL; + return (EINVAL); } #endif @@ -476,28 +540,32 @@ fatentry(function, pmp, cn, oldcontents, newcontents) * Be sure the requested cluster is in the filesystem. */ if (cn < CLUST_FIRST || cn > pmp->pm_maxcluster) - return EINVAL; + return (EINVAL); byteoffset = FATOFS(pmp, cn); fatblock(pmp, byteoffset, &bn, &bsize, &bo); error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); - if (error) - return error; + if (error) { + brelse(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; - } + if (FAT32(pmp)) + readcn = getulong(&bp->b_data[bo]); + else + readcn = getushort(&bp->b_data[bo]); + if (FAT12(pmp) & (cn & 1)) + readcn >>= 4; + readcn &= pmp->pm_fatmask; + /* map reserved fat entries to same values for all fats */ + if ((readcn | ~pmp->pm_fatmask) >= CLUST_RSRVD) + readcn |= ~pmp->pm_fatmask; *oldcontents = readcn; } if (function & FAT_SET) { - if (FAT12(pmp)) { + switch (pmp->pm_fatmask) { + case FAT12_MASK: readcn = getushort(&bp->b_data[bo]); if (cn & 1) { readcn &= 0x000f; @@ -507,15 +575,28 @@ fatentry(function, pmp, cn, oldcontents, newcontents) readcn |= newcontents & 0xfff; } putushort(&bp->b_data[bo], readcn); - } else + break; + case FAT16_MASK: putushort(&bp->b_data[bo], newcontents); + break; + case FAT32_MASK: + /* + * According to spec we have to retain the + * high order bits of the fat entry. + */ + readcn = getulong(&bp->b_data[bo]); + readcn &= ~FAT32_MASK; + readcn |= newcontents & FAT32_MASK; + putulong(&bp->b_data[bo], readcn); + break; + } updatefats(pmp, bp, bn); bp = NULL; pmp->pm_fmod = 1; } if (bp) brelse(bp); - return 0; + return (0); } /* @@ -538,25 +619,28 @@ fatchain(pmp, start, count, fillwith) struct buf *bp; #ifdef MSDOSFS_DEBUG - printf("fatchain(pmp %p, start %ld, count %ld, fillwith %ld)\n", - pmp, start, count, fillwith); + printf("fatchain(pmp %p, start %lu, count %lu, fillwith %lx)\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; + return (EINVAL); while (count > 0) { byteoffset = FATOFS(pmp, start); fatblock(pmp, byteoffset, &bn, &bsize, &bo); error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); - if (error) - return error; + if (error) { + brelse(bp); + return (error); + } while (count > 0) { start++; newc = --count > 0 ? start : fillwith; - if (FAT12(pmp)) { + switch (pmp->pm_fatmask) { + case FAT12_MASK: readcn = getushort(&bp->b_data[bo]); if (start & 1) { readcn &= 0xf000; @@ -569,9 +653,18 @@ fatchain(pmp, start, count, fillwith) bo++; if (!(start & 1)) bo++; - } else { + break; + case FAT16_MASK: putushort(&bp->b_data[bo], newc); bo += 2; + break; + case FAT32_MASK: + readcn = getulong(&bp->b_data[bo]); + readcn &= ~pmp->pm_fatmask; + readcn |= newc & pmp->pm_fatmask; + putulong(&bp->b_data[bo], readcn); + bo += 4; + break; } if (bo >= bsize) break; @@ -579,7 +672,7 @@ fatchain(pmp, start, count, fillwith) updatefats(pmp, bp, bn); } pmp->pm_fmod = 1; - return 0; + return (0); } /* @@ -606,11 +699,11 @@ chainlength(pmp, start, count) map &= ~((1 << start) - 1); if (map) { len = ffs(map) - 1 - start; - return len > count ? count : len; + return (len > count ? count : len); } len = N_INUSEBITS - start; if (len >= count) - return count; + return (count); while (++idx <= max_idx) { if (len >= count) break; @@ -621,7 +714,7 @@ chainlength(pmp, start, count) } len += N_INUSEBITS; } - return len > count ? count : len; + return (len > count ? count : len); } /* @@ -645,21 +738,23 @@ chainalloc(pmp, start, count, fillwith, retcluster, got) u_long *got; { int error; + u_long cl, n; + + for (cl = start, n = count; n-- > 0;) + usemap_alloc(pmp, cl++); error = fatchain(pmp, start, count, fillwith); - if (error == 0) { + if (error != 0) + return (error); #ifdef MSDOSFS_DEBUG - printf("clusteralloc(): allocated cluster chain at %ld (%ld clusters)\n", - start, count); + printf("clusteralloc(): allocated cluster chain at %lu (%lu clusters)\n", + start, count); #endif - if (retcluster) - *retcluster = start; - if (got) - *got = count; - while (count-- > 0) - usemap_alloc(pmp, start++); - } - return error; + if (retcluster) + *retcluster = start; + if (got) + *got = count; + return (0); } /* @@ -683,15 +778,16 @@ clusteralloc(pmp, start, count, fillwith, retcluster, got) u_long *got; { u_long idx; - u_long len, newst, foundcn, foundl, cn, l; + u_long len, newst, foundl, cn, l; + u_long foundcn = 0; /* XXX: foundcn could be used unititialized */ u_int map; #ifdef MSDOSFS_DEBUG - printf("clusteralloc(): find %d clusters\n",count); + printf("clusteralloc(): find %lu clusters\n",count); #endif if (start) { if ((len = chainlength(pmp, start, count)) >= count) - return chainalloc(pmp, start, count, fillwith, retcluster, got); + return (chainalloc(pmp, start, count, fillwith, retcluster, got)); } else { /* * This is a new file, initialize start @@ -699,7 +795,7 @@ clusteralloc(pmp, start, count, fillwith, retcluster, got) struct timeval tv; microtime(&tv); - start = (tv.tv_usec >> 10)|tv.tv_usec; + start = (tv.tv_usec >> 10) | tv.tv_usec; len = 0; } @@ -707,7 +803,7 @@ clusteralloc(pmp, start, count, fillwith, retcluster, got) * Start at a (pseudo) random place to maximize cluster runs * under multiple writers. */ - foundcn = newst = (start * 1103515245 + 12345) % (pmp->pm_maxcluster + 1); + newst = (start * 1103515245 + 12345) % (pmp->pm_maxcluster + 1); foundl = 0; for (cn = newst; cn <= pmp->pm_maxcluster;) { @@ -717,7 +813,7 @@ clusteralloc(pmp, start, count, fillwith, retcluster, got) 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); + return (chainalloc(pmp, cn, count, fillwith, retcluster, got)); if (l > foundl) { foundcn = cn; foundl = l; @@ -734,7 +830,7 @@ clusteralloc(pmp, start, count, fillwith, retcluster, got) 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); + return (chainalloc(pmp, cn, count, fillwith, retcluster, got)); if (l > foundl) { foundcn = cn; foundl = l; @@ -746,12 +842,12 @@ clusteralloc(pmp, start, count, fillwith, retcluster, got) } if (!foundl) - return ENOSPC; + return (ENOSPC); if (len) - return chainalloc(pmp, start, len, fillwith, retcluster, got); + return (chainalloc(pmp, start, len, fillwith, retcluster, got)); else - return chainalloc(pmp, foundcn, foundl, fillwith, retcluster, got); + return (chainalloc(pmp, foundcn, foundl, fillwith, retcluster, got)); } @@ -768,7 +864,7 @@ freeclusterchain(pmp, cluster) struct msdosfsmount *pmp; u_long cluster; { - int error = 0; + int error; struct buf *bp = NULL; u_long bn, bo, bsize, byteoffset; u_long readcn, lbn = -1; @@ -780,13 +876,16 @@ freeclusterchain(pmp, cluster) if (bp) updatefats(pmp, bp, lbn); error = bread(pmp->pm_devvp, bn, bsize, NOCRED, &bp); - if (error) - return error; + if (error) { + brelse(bp); + return (error); + } lbn = bn; } usemap_free(pmp, cluster); - readcn = getushort(&bp->b_data[bo]); - if (FAT12(pmp)) { + switch (pmp->pm_fatmask) { + case FAT12_MASK: + readcn = getushort(&bp->b_data[bo]); if (cluster & 1) { cluster = readcn >> 4; readcn &= 0x000f; @@ -797,17 +896,24 @@ freeclusterchain(pmp, cluster) readcn |= MSDOSFSFREE & 0xfff; } putushort(&bp->b_data[bo], readcn); - cluster &= 0x0fff; - if ((cluster&0x0ff0) == 0x0ff0) - cluster |= 0xf000; - } else { - cluster = readcn; + break; + case FAT16_MASK: + cluster = getushort(&bp->b_data[bo]); putushort(&bp->b_data[bo], MSDOSFSFREE); + break; + case FAT32_MASK: + cluster = getulong(&bp->b_data[bo]); + putulong(&bp->b_data[bo], + (MSDOSFSFREE & FAT32_MASK) | (cluster & ~FAT32_MASK)); + break; } + cluster &= pmp->pm_fatmask; + if ((cluster | ~pmp->pm_fatmask) >= CLUST_RSRVD) + cluster |= pmp->pm_fatmask; } if (bp) updatefats(pmp, bp, bn); - return error; + return (0); } /* @@ -821,7 +927,6 @@ fillinusemap(pmp) struct buf *bp = NULL; u_long cn, readcn; int error; - int fat12 = FAT12(pmp); u_long bn, bo, bsize, byteoffset; /* @@ -846,21 +951,24 @@ fillinusemap(pmp) 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 (error) { + brelse(bp); + return (error); + } } + if (FAT32(pmp)) + readcn = getulong(&bp->b_data[bo]); + else + readcn = getushort(&bp->b_data[bo]); + if (FAT12(pmp) && (cn & 1)) + readcn >>= 4; + readcn &= pmp->pm_fatmask; if (readcn == 0) usemap_free(pmp, cn); } brelse(bp); - return 0; + return (0); } /* @@ -886,7 +994,7 @@ extendfile(dep, count, bpp, ncp, flags) u_long *ncp; int flags; { - int error = 0; + int error; u_long frcn; u_long cn, got; struct msdosfsmount *pmp = dep->de_pmp; @@ -895,9 +1003,10 @@ extendfile(dep, count, bpp, ncp, flags) /* * Don't try to extend the root directory */ - if (DETOV(dep)->v_flag & VROOT) { + if (dep->de_StartCluster == MSDOSFSROOT + && (dep->de_Attributes & ATTR_DIRECTORY)) { printf("extendfile(): attempt to extend root directory\n"); - return ENOSPC; + return (ENOSPC); } /* @@ -908,21 +1017,21 @@ extendfile(dep, count, bpp, ncp, flags) if (dep->de_fc[FC_LASTFC].fc_frcn == FCE_EMPTY && dep->de_StartCluster != 0) { fc_lfcempty++; - error = pcbmap(dep, 0xffff, 0, &cn); + error = pcbmap(dep, 0xffff, 0, &cn, 0); /* we expect it to return E2BIG */ if (error != E2BIG) - return error; - error = 0; + return (error); } 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). + * 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; @@ -930,7 +1039,7 @@ extendfile(dep, count, bpp, ncp, flags) cn = dep->de_fc[FC_LASTFC].fc_fsrcn + 1; error = clusteralloc(pmp, cn, count, CLUST_EOFE, &cn, &got); if (error) - return error; + return (error); count -= got; @@ -947,13 +1056,13 @@ extendfile(dep, count, bpp, ncp, flags) dep->de_StartCluster = cn; frcn = 0; } else { - error = fatentry(FAT_SET, pmp, dep->de_fc[FC_LASTFC].fc_fsrcn, + error = fatentry(FAT_SET, pmp, + dep->de_fc[FC_LASTFC].fc_fsrcn, 0, cn); if (error) { clusterfree(pmp, cn, NULL); - return error; + return (error); } - frcn = dep->de_fc[FC_LASTFC].fc_frcn + 1; } @@ -972,11 +1081,14 @@ extendfile(dep, count, bpp, ncp, flags) bp = getblk(pmp->pm_devvp, cntobn(pmp, cn++), pmp->pm_bpcluster, 0, 0); else { - bp = getblk(DETOV(dep), frcn++, pmp->pm_bpcluster, 0, 0); + bp = getblk(DETOV(dep), de_cn2bn(pmp, frcn++), + pmp->pm_bpcluster, 0, 0); /* * Do the bmap now, as in msdosfs_write */ - if (pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0)) + if (pcbmap(dep, + de_bn2cn(pmp, bp->b_lblkno), + &bp->b_blkno, 0, 0)) bp->b_blkno = -1; if (bp->b_blkno == -1) panic("extendfile: pcbmap"); @@ -985,13 +1097,11 @@ extendfile(dep, count, bpp, ncp, flags) if (bpp) { *bpp = bp; bpp = NULL; - } else { - bp->b_flags |= B_AGE; - bawrite(bp); - } + } else + bdwrite(bp); } } } - return 0; + return (0); } diff --git a/sys/msdosfs/msdosfs_lookup.c b/sys/msdosfs/msdosfs_lookup.c index b757cf059982..49914179a990 100644 --- a/sys/msdosfs/msdosfs_lookup.c +++ b/sys/msdosfs/msdosfs_lookup.c @@ -1,9 +1,9 @@ -/* $Id: msdosfs_lookup.c,v 1.13 1997/09/02 20:06:17 bde Exp $ */ -/* $NetBSD: msdosfs_lookup.c,v 1.14 1994/08/21 18:44:07 ws Exp $ */ +/* $Id: msdosfs_lookup.c,v 1.14 1997/09/10 19:44:36 phk Exp $ */ +/* $NetBSD: msdosfs_lookup.c,v 1.37 1997/11/17 15:36:54 ws Exp $ */ /*- - * Copyright (C) 1994 Wolfgang Solfrank. - * Copyright (C) 1994 TooLs GmbH. + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * @@ -94,16 +94,13 @@ msdosfs_lookup(ap) int error; int lockparent; int wantparent; - int slotstatus; - -#define NONE 0 -#define FOUND 1 - int slotoffset = -1; - int slotcluster = -1; + int slotcount; + int slotoffset = 0; int frcn; u_long cluster; - int rootreloff; + int blkoff; int diroff; + int blsize; int isadir; /* ~0 if found direntry is a directory */ u_long scn; /* starting cluster number */ struct vnode *pdp; @@ -118,6 +115,10 @@ msdosfs_lookup(ap) int nameiop = cnp->cn_nameiop; struct proc *p = cnp->cn_proc; + int wincnt = 1; + int chksum = -1; + int olddos = 1; + #ifdef MSDOSFS_DEBUG printf("msdosfs_lookup(): looking for %s\n", cnp->cn_nameptr); #endif @@ -127,8 +128,8 @@ msdosfs_lookup(ap) 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); + printf("msdosfs_lookup(): vdp %p, dp %p, Attr %02x\n", + vdp, dp, dp->de_Attributes); #endif /* @@ -145,25 +146,43 @@ msdosfs_lookup(ap) printf("msdosfs_lookup(): looking for . or .. in root directory\n"); #endif cluster = MSDOSFSROOT; - diroff = MSDOSFSROOT_OFS; + blkoff = 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; + switch (unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename, + cnp->cn_namelen, 0)) { + case 0: + return (EINVAL); + case 1: + break; + case 2: + wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, + cnp->cn_namelen) + 1; + break; + case 3: + olddos = 0; + wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, + cnp->cn_namelen) + 1; + break; } + if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) + wincnt = 1; + + /* + * Suppress search for slots unless creating + * file and at end of pathname, in which case + * we watch for a place to put the new file in + * case it doesn't already exist. + */ + slotcount = wincnt; + if ((nameiop == CREATE || nameiop == RENAME) && + (flags & ISLASTCN)) + slotcount = 0; - 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); + printf("msdosfs_lookup(): dos version of filename %s, length %ld\n", + dosfilename, cnp->cn_namelen); #endif /* * Search the directory pointed at by vdp for the name pointed at @@ -177,20 +196,23 @@ msdosfs_lookup(ap) * part of the pool of allocatable clusters. So, we treat it a * little differently. The root directory starts at "cluster" 0. */ - rootreloff = 0; + diroff = 0; for (frcn = 0;; frcn++) { - error = pcbmap(dp, frcn, &bn, &cluster); + error = pcbmap(dp, frcn, &bn, &cluster, &blsize); if (error) { if (error == E2BIG) break; - return error; + return (error); } - error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,NOCRED,&bp); - if (error) - return error; - for (diroff = 0; diroff < pmp->pm_depclust; diroff++) { - dep = (struct direntry *) bp->b_data + diroff; - + error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } + for (blkoff = 0; blkoff < blsize; + blkoff += sizeof(struct direntry), + diroff += sizeof(struct direntry)) { + dep = (struct direntry *)(bp->b_data + blkoff); /* * If the slot is empty and we are still looking * for an empty then remember this one. If the @@ -202,218 +224,313 @@ msdosfs_lookup(ap) */ 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; + /* + * Drop memory of previous long matches + */ + chksum = -1; + + if (slotcount < wincnt) { + slotcount++; + slotoffset = diroff; } if (dep->deName[0] == SLOT_EMPTY) { brelse(bp); goto notfound; } } else { + /* + * If there wasn't enough space for our winentries, + * forget about the empty space + */ + if (slotcount < wincnt) + slotcount = 0; + + /* + * Check for Win95 long filename entry + */ + if (dep->deAttributes == ATTR_WIN95) { + if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) + continue; + + chksum = winChkName((const u_char *)cnp->cn_nameptr, + cnp->cn_namelen, + (struct winentry *)dep, + chksum); + continue; + } + /* * 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; + if (dep->deAttributes & ATTR_VOLUME) { + chksum = -1; + continue; } + + /* + * Check for a checksum or name match + */ + if (chksum != winChksum(dep->deName) + && (!olddos || bcmp(dosfilename, dep->deName, 11))) { + chksum = -1; + continue; + } +#ifdef MSDOSFS_DEBUG + printf("msdosfs_lookup(): match blkoff %d, diroff %d\n", + blkoff, diroff); +#endif + /* + * Remember where this directory + * entry came from for whoever did + * this lookup. + */ + dp->de_fndoffset = diroff; + dp->de_fndcnt = 0; /* unused anyway */ + + goto found; } - rootreloff++; - } /* for (diroff = 0; .... */ + } /* for (blkoff = 0; .... */ /* * Release the buffer holding the directory cluster just * searched. */ brelse(bp); - } /* for (frcn = 0; ; frcn++) */ -notfound:; + } /* for (frcn = 0; ; frcn++) */ + +notfound: /* * We hold no disk buffers at this point. */ + /* + * Fixup the slot description to point to the place where + * we might put the new DOS direntry (putting the Win95 + * long name entries before that) + */ + if (!slotcount) { + slotcount = 1; + slotoffset = diroff; + } + if (wincnt > slotcount) + slotoffset += sizeof(struct direntry) * (wincnt - slotcount); + /* * 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); + printf("msdosfs_lookup(): op %d, refcnt %ld\n", + nameiop, dp->de_refcnt); + printf(" slotcount %d, slotoffset %d\n", + slotcount, slotoffset); #endif if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN) && dp->de_refcnt != 0) { - error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc); + /* + * Access for write is interpreted as allowing + * creation of files in the directory. + */ + error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_proc); if (error) - return error; - 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 */ + return (error); + /* + * Return an indication of where the new directory + * entry should be put. + */ + dp->de_fndoffset = slotoffset; + dp->de_fndcnt = wincnt - 1; + + /* + * We return with the directory locked, so that + * the parameters we set up above will still be + * valid if we actually decide to do a direnter(). + * We return ni_vp == NULL to indicate that the entry + * does not currently exist; we leave a pointer to + * the (locked) directory inode in ndp->ni_dvp. + * The pathname buffer is saved so that the name + * can be obtained later. + * + * NB - if the directory is unlocked, then this + * information cannot be used. + */ cnp->cn_flags |= SAVENAME; - if (!lockparent)/* leave searched dir locked? */ + if (!lockparent) VOP_UNLOCK(vdp, 0, p); - return EJUSTRETURN; + return (EJUSTRETURN); } /* - * Insert name in cache as non-existant if not trying to create it. + * Insert name into cache (as non-existent) if appropriate. */ if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) cache_enter(vdp, *vpp, cnp); - return ENOENT; + return (ENOENT); -found: ; +found: /* * NOTE: We still have the buffer with matched directory entry at * this point. */ isadir = dep->deAttributes & ATTR_DIRECTORY; scn = getushort(dep->deStartCluster); + if (FAT32(pmp)) { + scn |= getushort(dep->deHighClust) << 16; + if (scn == pmp->pm_rootdirblk) { + /* + * There should actually be 0 here. + * Just ignore the error. + */ + scn = MSDOSFSROOT; + } + } -foundroot:; + if (isadir) { + cluster = scn; + if (cluster == MSDOSFSROOT) + blkoff = MSDOSFSROOT_OFS; + else + blkoff = 0; + } else if (cluster == MSDOSFSROOT) + blkoff = diroff; + + /* + * Now release buf to allow deget to read the entry again. + * Reserving it here and giving it to deget could result + * in a deadlock. + */ + brelse(bp); + bp = 0; + +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. + * setup before jumping here. And, bp is already null. */ + if (FAT32(pmp) && scn == MSDOSFSROOT) + scn = pmp->pm_rootdirblk; /* - * 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 deleting, and at end of pathname, return + * parameters which can be used to remove file. + * If the wantparent flag isn't set, we return only + * the directory (in ndp->ni_dvp), otherwise we go + * on and lock the inode, being careful with ".". */ if (nameiop == DELETE && (flags & ISLASTCN)) { - error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc); - if (error) { - if (bp) - brelse(bp); - return error; - } + /* + * Don't allow deleting the root. + */ + if (blkoff == MSDOSFSROOT_OFS) + return EROFS; /* really? XXX */ + + /* + * Write access to directory required to delete files. + */ + error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_proc); + if (error) + return (error); + + /* + * Return pointer to current entry in dp->i_offset. + * Save directory inode pointer in ndp->ni_dvp for dirremove(). + */ 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; + return (0); } + error = deget(pmp, cluster, blkoff, &tdp); + if (error) + return (error); *vpp = DETOV(tdp); if (!lockparent) VOP_UNLOCK(vdp, 0, p); - if (bp) - brelse(bp); - return 0; + return (0); } /* - * If renaming. + * If rewriting (RENAME), return the inode and the + * information required to rewrite the present directory + * Must get inode of directory entry to verify it's a + * regular file, or empty directory. */ - if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) { - error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc); - if (error) { - if (bp) - brelse(bp); - return error; - } - 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; - } + if (nameiop == RENAME && wantparent && + (flags & ISLASTCN)) { + if (blkoff == MSDOSFSROOT_OFS) + return EROFS; /* really? XXX */ + + error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_proc); + if (error) + return (error); + + /* + * Careful about locking second inode. + * This can only occur if the target is ".". + */ + if (dp->de_StartCluster == scn && isadir) + return (EISDIR); + + if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0) + return (error); *vpp = DETOV(tdp); cnp->cn_flags |= SAVENAME; if (!lockparent) VOP_UNLOCK(vdp, 0, p); - if (bp) - brelse(bp); - return 0; + return (0); } /* - * ? + * Step through the translation in the name. We do not `vput' the + * directory because we may need it again if a symbolic link + * is relative to the current directory. Instead we save it + * unlocked as "pdp". We must get the target inode before unlocking + * the directory to insure that the inode will not be removed + * before we get it. We prevent deadlock by always fetching + * inodes from the root, moving down the directory tree. Thus + * when following backward pointers ".." we must unlock the + * parent directory before getting the requested directory. + * There is a potential race condition here if both the current + * and parent directories are removed before the VFS_VGET for the + * inode associated with ".." returns. We hope that this occurs + * infrequently since we cannot avoid this race condition without + * implementing a sophisticated deadlock detection algorithm. + * Note also that this simple deadlock detection scheme will not + * work if the file system has any hard links other than ".." + * that point backwards in the directory structure. */ pdp = vdp; if (flags & ISDOTDOT) { VOP_UNLOCK(pdp, 0, p); - error = deget(pmp, cluster, diroff, dep, &tdp); + error = deget(pmp, cluster, blkoff, &tdp); if (error) { - vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p); - if (bp) - brelse(bp); - return error; + vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p); + return (error); } - if (lockparent && (flags & ISLASTCN) - && (error = vn_lock(pdp, LK_EXCLUSIVE, p))) { + if (lockparent && (flags & ISLASTCN) && + (error = vn_lock(pdp, LK_EXCLUSIVE, p))) { vput(DETOV(tdp)); - return error; + return (error); } *vpp = DETOV(tdp); - } else if (dp->de_StartCluster == scn && isadir) { /* "." */ - VREF(vdp); + } else if (dp->de_StartCluster == scn && isadir) { + VREF(vdp); /* we want ourself, ie "." */ *vpp = vdp; } else { - error = deget(pmp, cluster, diroff, dep, &tdp); - if (error) { - if (bp) - brelse(bp); - return error; - } + if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0) + return (error); if (!lockparent || !(flags & ISLASTCN)) VOP_UNLOCK(pdp, 0, p); *vpp = DETOV(tdp); } - if (bp) - brelse(bp); /* - * Insert name in cache if wanted. + * Insert name into cache if appropriate. */ if (cnp->cn_flags & MAKEENTRY) cache_enter(vdp, *vpp, cnp); - return 0; + return (0); } /* @@ -421,21 +538,26 @@ foundroot:; * ddep - directory to add to * depp - return the address of the denode for the created directory entry * if depp != 0 + * cnp - componentname needed for Win95 long filenames */ int -createde(dep, ddep, depp) +createde(dep, ddep, depp, cnp) struct denode *dep; struct denode *ddep; struct denode **depp; + struct componentname *cnp; { int error; u_long dirclust, diroffset; struct direntry *ndep; struct msdosfsmount *pmp = ddep->de_pmp; struct buf *bp; + daddr_t bn; + int blsize; #ifdef MSDOSFS_DEBUG - printf("createde(dep %08x, ddep %08x, depp %08x)\n", dep, ddep, depp); + printf("createde(dep %p, ddep %p, depp %p, cnp %p)\n", + dep, ddep, depp, cnp); #endif /* @@ -446,106 +568,102 @@ createde(dep, ddep, depp) * to extend the root directory. We just return an error in that * case. */ - if (ddep->de_fndclust == (u_long)-1) { - error = extendfile(ddep, 1, &bp, &dirclust, DE_CLEAR); - if (error) + if (ddep->de_fndoffset >= ddep->de_FileSize) { + diroffset = ddep->de_fndoffset + sizeof(struct direntry) + - ddep->de_FileSize; + dirclust = de_clcount(pmp, diroffset); + error = extendfile(ddep, dirclust, 0, 0, DE_CLEAR); + if (error) { + (void)detrunc(ddep, ddep->de_FileSize, 0, NOCRED, NULL); 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; + ddep->de_FileSize += de_cn2off(pmp, dirclust); } + + /* + * 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. + */ + error = pcbmap(ddep, de_cluster(pmp, ddep->de_fndoffset), + &bn, &dirclust, &blsize); + if (error) + return error; + diroffset = ddep->de_fndoffset; + if (dirclust != MSDOSFSROOT) + diroffset &= pmp->pm_crbomask; + if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp)) != 0) { + brelse(bp); + return error; + } + ndep = bptoep(pmp, bp, ddep->de_fndoffset); + DE_EXTERNALIZE(ndep, dep); + /* + * Now write the Win95 long name + */ + if (ddep->de_fndcnt > 0) { + u_int8_t chksum = winChksum(ndep->deName); + const u_char *un = (const u_char *)cnp->cn_nameptr; + int unlen = cnp->cn_namelen; + int cnt = 1; + + while (--ddep->de_fndcnt >= 0) { + if (!(ddep->de_fndoffset & pmp->pm_crbomask)) { + if ((error = bwrite(bp)) != 0) + return error; + + ddep->de_fndoffset -= sizeof(struct direntry); + error = pcbmap(ddep, + de_cluster(pmp, + ddep->de_fndoffset), + &bn, 0, &blsize); + if (error) + return error; + + error = bread(pmp->pm_devvp, bn, blsize, + NOCRED, &bp); + if (error) { + brelse(bp); + return error; + } + ndep = bptoep(pmp, bp, ddep->de_fndoffset); + } else { + ndep--; + ddep->de_fndoffset -= sizeof(struct direntry); + } + if (!unix2winfn(un, unlen, (struct winentry *)ndep, cnt++, chksum)) + break; + } + } + + if ((error = bwrite(bp)) != 0) + return error; + /* * If they want us to return with the denode gotten. */ if (depp) { - error = deget(pmp, dirclust, diroffset, ndep, depp); - if (error) - return error; - } - error = bwrite(bp); - if (error) { - vput(DETOV(*depp)); /* free the vnode we got on error */ - return error; + if (dep->de_Attributes & ATTR_DIRECTORY) { + dirclust = dep->de_StartCluster; + if (FAT32(pmp) && dirclust == pmp->pm_rootdirblk) + dirclust = MSDOSFSROOT; + if (dirclust == MSDOSFSROOT) + diroffset = MSDOSFSROOT_OFS; + else + diroffset = 0; + } + return deget(pmp, dirclust, diroffset, depp); } + return 0; } -/* - * Read in a directory entry and mark it as being deleted. - */ -static 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. @@ -554,7 +672,7 @@ int dosdirempty(dep) struct denode *dep; { - int dei; + int blsize; int error; u_long cn; daddr_t bn; @@ -568,16 +686,21 @@ dosdirempty(dep) * 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) { + if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) { + if (error == E2BIG) + return (1); /* it's empty */ + return (0); + } + error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); + if (error) { + brelse(bp); + return (0); + } + for (dentp = (struct direntry *)bp->b_data; + (char *)dentp < bp->b_data + blsize; + dentp++) { + if (dentp->deName[0] != SLOT_DELETED && + (dentp->deAttributes & ATTR_VOLUME) == 0) { /* * In dos directories an entry whose name * starts with SLOT_EMPTY (0) starts the @@ -587,7 +710,7 @@ dosdirempty(dep) */ if (dentp->deName[0] == SLOT_EMPTY) { brelse(bp); - return 1; + return (1); } /* * Any names other than "." and ".." in a @@ -597,13 +720,12 @@ dosdirempty(dep) bcmp(dentp->deName, ".. ", 11)) { brelse(bp); #ifdef MSDOSFS_DEBUG - printf("dosdirempty(): entry %d found %02x, %02x\n", - dei, dentp->deName[0], dentp->deName[1]); + printf("dosdirempty(): entry found %02x, %02x\n", + dentp->deName[0], dentp->deName[1]); #endif - return 0; /* not empty */ + return (0); /* not empty */ } } - dentp++; } brelse(bp); } @@ -647,18 +769,25 @@ doscheckpath(source, target) } if (dep->de_StartCluster == MSDOSFSROOT) goto out; + pmp = dep->de_pmp; +#ifdef DIAGNOSTIC + if (pmp != source->de_pmp) + panic("doscheckpath: source and target on different filesystems"); +#endif + if (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk) + 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; } + 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) { @@ -666,28 +795,38 @@ doscheckpath(source, target) break; } scn = getushort(ep->deStartCluster); + if (FAT32(pmp)) + scn |= getushort(ep->deHighClust) << 16; + if (scn == source->de_StartCluster) { error = EINVAL; break; } if (scn == MSDOSFSROOT) break; + if (FAT32(pmp) && scn == pmp->pm_rootdirblk) { + /* + * scn should be 0 in this case, + * but we silently ignore the error. + */ + break; + } + vput(DETOV(dep)); - /* NOTE: deget() clears dep on error */ - error = deget(pmp, scn, 0, ep, &dep); brelse(bp); bp = NULL; - if (error) + /* NOTE: deget() clears dep on error */ + if ((error = deget(pmp, scn, 0, &dep)) != 0) break; } -out: ; +out:; if (bp) brelse(bp); if (error == ENOTDIR) printf("doscheckpath(): .. not a directory?\n"); if (dep != NULL) vput(DETOV(dep)); - return error; + return (error); } /* @@ -696,27 +835,33 @@ out: ; * directory entry within the block. */ int -readep(pmp, dirclu, dirofs, bpp, epp) +readep(pmp, dirclust, diroffset, bpp, epp) struct msdosfsmount *pmp; - u_long dirclu, dirofs; + u_long dirclust, diroffset; struct buf **bpp; struct direntry **epp; { int error; daddr_t bn; + int blsize; + u_long boff; - bn = detobn(pmp, dirclu, dirofs); - error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, bpp); - if (error) { + boff = diroffset & ~pmp->pm_crbomask; + blsize = pmp->pm_bpcluster; + if (dirclust == MSDOSFSROOT + && de_blk(pmp, diroffset + blsize) > pmp->pm_rootdirsize) + blsize = de_bn2off(pmp, pmp->pm_rootdirsize) & pmp->pm_crbomask; + bn = detobn(pmp, dirclust, diroffset); + if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, bpp)) != 0) { + brelse(*bpp); *bpp = NULL; - return error; + return (error); } if (epp) - *epp = bptoep(pmp, *bpp, dirofs); - return 0; + *epp = bptoep(pmp, *bpp, diroffset); + 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 @@ -728,6 +873,195 @@ readde(dep, bpp, epp) struct buf **bpp; struct direntry **epp; { - return readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset, - bpp, epp); + + return (readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset, + bpp, epp)); +} + +/* + * Remove a directory entry. At this point the file represented by the + * directory entry to be removed is still full length until noone 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 */ +{ + int error; + struct direntry *ep; + struct buf *bp; + daddr_t bn; + int blsize; + struct msdosfsmount *pmp = pdep->de_pmp; + u_long offset = pdep->de_fndoffset; + +#ifdef MSDOSFS_DEBUG + printf("removede(): filename %s, dep %p, offset %08lx\n", + dep->de_Name, dep, offset); +#endif + + dep->de_refcnt--; + offset += sizeof(struct direntry); + do { + offset -= sizeof(struct direntry); + error = pcbmap(pdep, de_cluster(pmp, offset), &bn, 0, &blsize); + if (error) + return error; + error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); + if (error) { + brelse(bp); + return error; + } + ep = bptoep(pmp, bp, offset); + /* + * Check whether, if we came here the second time, i.e. + * when underflowing into the previous block, the last + * entry in this block is a longfilename entry, too. + */ + if (ep->deAttributes != ATTR_WIN95 + && offset != pdep->de_fndoffset) { + brelse(bp); + break; + } + offset += sizeof(struct direntry); + while (1) { + /* + * We are a bit agressive here in that we delete any Win95 + * entries preceding this entry, not just the ones we "own". + * Since these presumably aren't valid anyway, + * there should be no harm. + */ + offset -= sizeof(struct direntry); + ep--->deName[0] = SLOT_DELETED; + if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) + || !(offset & pmp->pm_crbomask) + || ep->deAttributes != ATTR_WIN95) + break; + } + if ((error = bwrite(bp)) != 0) + return error; + } while (!(pmp->pm_flags & MSDOSFSMNT_NOWIN95) + && !(offset & pmp->pm_crbomask) + && offset); + return 0; +} + +/* + * Create a unique DOS name in dvp + */ +int +uniqdosname(dep, cnp, cp) + struct denode *dep; + struct componentname *cnp; + u_char *cp; +{ + struct msdosfsmount *pmp = dep->de_pmp; + struct direntry *dentp; + int gen; + int blsize; + u_long cn; + daddr_t bn; + struct buf *bp; + int error; + + for (gen = 1;; gen++) { + /* + * Generate DOS name with generation number + */ + if (!unix2dosfn((const u_char *)cnp->cn_nameptr, cp, + cnp->cn_namelen, gen)) + return gen == 1 ? EINVAL : EEXIST; + + /* + * Now look for a dir entry with this exact name + */ + for (cn = error = 0; !error; cn++) { + if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) { + if (error == E2BIG) /* EOF reached and not found */ + return 0; + return error; + } + error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); + if (error) { + brelse(bp); + return error; + } + for (dentp = (struct direntry *)bp->b_data; + (char *)dentp < bp->b_data + blsize; + dentp++) { + if (dentp->deName[0] == SLOT_EMPTY) { + /* + * Last used entry and not found + */ + brelse(bp); + return 0; + } + /* + * Ignore volume labels and Win95 entries + */ + if (dentp->deAttributes & ATTR_VOLUME) + continue; + if (!bcmp(dentp->deName, cp, 11)) { + error = EEXIST; + break; + } + } + brelse(bp); + } + } +} + +/* + * Find any Win'95 long filename entry in directory dep + */ +int +findwin95(dep) + struct denode *dep; +{ + struct msdosfsmount *pmp = dep->de_pmp; + struct direntry *dentp; + int blsize; + u_long cn; + daddr_t bn; + struct buf *bp; + + /* + * Read through the directory looking for Win'95 entries + * Note: Error currently handled just as EOF XXX + */ + for (cn = 0;; cn++) { + if (pcbmap(dep, cn, &bn, 0, &blsize)) + return 0; + if (bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp)) { + brelse(bp); + return 0; + } + for (dentp = (struct direntry *)bp->b_data; + (char *)dentp < bp->b_data + blsize; + dentp++) { + if (dentp->deName[0] == SLOT_EMPTY) { + /* + * Last used entry and not found + */ + brelse(bp); + return 0; + } + if (dentp->deName[0] == SLOT_DELETED) { + /* + * Ignore deleted files + * Note: might be an indication of Win'95 anyway XXX + */ + continue; + } + if (dentp->deAttributes == ATTR_WIN95) { + brelse(bp); + return 1; + } + } + brelse(bp); + } } diff --git a/sys/msdosfs/msdosfs_vfsops.c b/sys/msdosfs/msdosfs_vfsops.c index 1fc73f637e67..f552266d8afb 100644 --- a/sys/msdosfs/msdosfs_vfsops.c +++ b/sys/msdosfs/msdosfs_vfsops.c @@ -1,9 +1,9 @@ -/* $Id: msdosfs_vfsops.c,v 1.22 1997/10/12 20:25:01 phk Exp $ */ -/* $NetBSD: msdosfs_vfsops.c,v 1.19 1994/08/21 18:44:10 ws Exp $ */ +/* $Id: msdosfs_vfsops.c,v 1.23 1997/11/12 05:42:19 julian Exp $ */ +/* $NetBSD: msdosfs_vfsops.c,v 1.51 1997/11/17 15:36:58 ws Exp $ */ /*- - * Copyright (C) 1994 Wolfgang Solfrank. - * Copyright (C) 1994 TooLs GmbH. + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * @@ -59,6 +59,7 @@ #include #include #include +#include /* defines ALLPERMS */ #include #include @@ -70,8 +71,9 @@ MALLOC_DEFINE(M_MSDOSFSMNT, "MSDOSFS mount", "MSDOSFS mount structure"); static MALLOC_DEFINE(M_MSDOSFSFAT, "MSDOSFS FAT", "MSDOSFS file allocation table"); +static int update_mp __P((struct mount *mp, struct msdosfs_args *argp)); static int mountmsdosfs __P((struct vnode *devvp, struct mount *mp, - struct proc *p)); + struct proc *p, struct msdosfs_args *argp)); static int msdosfs_fhtovp __P((struct mount *, struct fid *, struct sockaddr *, struct vnode **, int *, struct ucred **)); @@ -90,6 +92,111 @@ static int msdosfs_vget __P((struct mount *mp, ino_t ino, struct vnode **vpp)); static int msdosfs_vptofh __P((struct vnode *, struct fid *)); +static int +update_mp(mp, argp) + struct mount *mp; + struct msdosfs_args *argp; +{ + struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); + int error; + + pmp->pm_gid = argp->gid; + pmp->pm_uid = argp->uid; + pmp->pm_mask = argp->mask & ALLPERMS; + pmp->pm_flags |= argp->flags & MSDOSFSMNT_MNTOPT; + +#ifndef __FreeBSD__ + /* + * GEMDOS knows nothing (yet) about win95 + */ + if (pmp->pm_flags & MSDOSFSMNT_GEMDOSFS) + pmp->pm_flags |= MSDOSFSMNT_NOWIN95; +#endif + + if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) + pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; + else if (!(pmp->pm_flags & + (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME))) { + struct vnode *rootvp; + + /* + * Try to divine whether to support Win'95 long filenames + */ + if (FAT32(pmp)) + pmp->pm_flags |= MSDOSFSMNT_LONGNAME; + else { + if ((error = msdosfs_root(mp, &rootvp)) != 0) + return error; + pmp->pm_flags |= findwin95(VTODE(rootvp)) + ? MSDOSFSMNT_LONGNAME + : MSDOSFSMNT_SHORTNAME; + vput(rootvp); + } + } + return 0; +} + +#ifndef __FreeBSD__ +int +msdosfs_mountroot() +{ + register struct mount *mp; + struct proc *p = curproc; /* XXX */ + size_t size; + int error; + struct msdosfs_args args; + + if (root_device->dv_class != DV_DISK) + return (ENODEV); + + /* + * Get vnodes for swapdev and rootdev. + */ + if (bdevvp(rootdev, &rootvp)) + panic("msdosfs_mountroot: can't setup rootvp"); + + mp = malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK); + bzero((char *)mp, (u_long)sizeof(struct mount)); + mp->mnt_op = &msdosfs_vfsops; + mp->mnt_flag = 0; + LIST_INIT(&mp->mnt_vnodelist); + + args.flags = 0; + args.uid = 0; + args.gid = 0; + args.mask = 0777; + + if ((error = mountmsdosfs(rootvp, mp, p, &args)) != 0) { + free(mp, M_MOUNT); + return (error); + } + + if ((error = update_mp(mp, &args)) != 0) { + (void)msdosfs_unmount(mp, 0, p); + free(mp, M_MOUNT); + return (error); + } + + if ((error = vfs_lock(mp)) != 0) { + (void)msdosfs_unmount(mp, 0, p); + free(mp, M_MOUNT); + return (error); + } + + CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list); + mp->mnt_vnodecovered = NULLVP; + (void) copystr("/", mp->mnt_stat.f_mntonname, MNAMELEN - 1, + &size); + bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); + (void) copystr(ROOTNAME, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, + &size); + bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); + (void)msdosfs_statfs(mp, &mp->mnt_stat, p); + vfs_unlock(mp); + return (0); +} +#endif + /* * 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 @@ -105,29 +212,27 @@ msdosfs_mount(mp, path, data, ndp, 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 */ + /* msdosfs specific mount control block */ + struct msdosfsmount *pmp = NULL; + size_t size; int error, flags; - u_int size; - struct ucred *cred, *scred; - struct vattr va; + mode_t accessmode; - /* - * Copy in the args for the mount request. - */ - error = copyin(data, (caddr_t) & args, sizeof(struct msdosfs_args)); + error = copyin(data, (caddr_t)&args, sizeof(struct msdosfs_args)); if (error) - return error; - + return (error); + if (args.magic != MSDOSFS_ARGSMAGIC) { + printf("Old mount_msdosfs, flags=%d\n", args.flags); + args.flags = 0; + } /* - * 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 updating, check whether changing from read-only to + * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { - pmp = (struct msdosfsmount *) mp->mnt_data; + pmp = VFSTOMSDOSFS(mp); error = 0; - if (pmp->pm_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) { + if (!(pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_flag & MNT_RDONLY)) { flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; @@ -135,214 +240,226 @@ msdosfs_mount(mp, path, data, ndp, p) } if (!error && (mp->mnt_flag & MNT_RELOAD)) /* not yet implemented */ - error = EINVAL; + error = EOPNOTSUPP; if (error) - return error; - if (pmp->pm_ronly && (mp->mnt_kern_flag & MNTK_WANTRDWR)) - pmp->pm_ronly = 0; + return (error); + if ((pmp->pm_flags & MSDOSFSMNT_RONLY) && (mp->mnt_kern_flag & MNTK_WANTRDWR)) { + /* + * If upgrade to read-write by non-root, then verify + * that user has necessary permissions on the device. + */ + if (p->p_ucred->cr_uid != 0) { + devvp = pmp->pm_devvp; + vn_lock(devvp, LK_EXCLUSIVE, p); + error = VOP_ACCESS(devvp, VREAD | VWRITE, + p->p_ucred, p); + if (error) { + VOP_UNLOCK(devvp, 0, p); + return (error); + } + VOP_UNLOCK(devvp, 0, p); + } + pmp->pm_flags &= ~MSDOSFSMNT_RONLY; + } if (args.fspec == 0) { +#ifdef __notyet__ /* doesn't work correctly with current mountd XXX */ + if (args.flags & MSDOSFSMNT_MNTOPT) { + pmp->pm_flags &= ~MSDOSFSMNT_MNTOPT; + pmp->pm_flags |= args.flags & MSDOSFSMNT_MNTOPT; + if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) + pmp->pm_flags |= MSDOSFSMNT_SHORTNAME; + } +#endif /* * Process export requests. */ - return vfs_export(mp, &pmp->pm_export, &args.export); + 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. + * Not an update, or updating the name: look up the name + * and verify that it refers to a sensible block device. */ 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. - */ + if (error) + return (error); devvp = ndp->ni_vp; + if (devvp->v_type != VBLK) { vrele(devvp); - return ENOTBLK; + return (ENOTBLK); } if (major(devvp->v_rdev) >= nblkdev) { vrele(devvp); - return ENXIO; + 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 mount by non-root, then verify that user has necessary + * permissions on the device. */ - if (mp->mnt_flag & MNT_UPDATE) { + if (p->p_ucred->cr_uid != 0) { + accessmode = VREAD; + if ((mp->mnt_flag & MNT_RDONLY) == 0) + accessmode |= VWRITE; + vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); + error = VOP_ACCESS(devvp, accessmode, p->p_ucred, p); + if (error) { + vput(devvp); + return (error); + } + VOP_UNLOCK(devvp, 0, p); + } + if ((mp->mnt_flag & MNT_UPDATE) == 0) { + error = mountmsdosfs(devvp, mp, p, &args); +#ifdef MSDOSFS_DEBUG /* only needed for the printf below */ + pmp = VFSTOMSDOSFS(mp); +#endif + } else { if (devvp != pmp->pm_devvp) - error = EINVAL; + error = EINVAL; /* XXX needs translation */ 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); + } + + error = update_mp(mp, &args); + if (error) { + msdosfs_unmount(mp, MNT_FORCE, p); 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) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size); + bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size); + (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, + &size); + bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); (void) msdosfs_statfs(mp, &mp->mnt_stat, p); #ifdef MSDOSFS_DEBUG printf("msdosfs_mount(): mp %p, pmp %p, inusemap %p\n", mp, pmp, pmp->pm_inusemap); #endif - return 0; + return (0); } static int -mountmsdosfs(devvp, mp, p) +mountmsdosfs(devvp, mp, p, argp) struct vnode *devvp; struct mount *mp; struct proc *p; + struct msdosfs_args *argp; { - int i; - int bpc; - int bit; - int error; - int needclose; - int ronly = (mp->mnt_flag & MNT_RDONLY) != 0; + struct msdosfsmount *pmp; + struct buf *bp; dev_t dev = devvp->v_rdev; +#ifndef __FreeBSD__ + struct partinfo dpart; +#endif union bootsector *bsp; - struct msdosfsmount *pmp = NULL; - struct buf *bp0 = NULL; struct byte_bpb33 *b33; struct byte_bpb50 *b50; #ifdef PC98 u_int pc98_wrk; u_int Phy_Sector_Size; #endif + struct byte_bpb710 *b710; + u_int8_t SecPerClust; + int ronly, error; + int bsize = 0, dtype = 0, tmp; /* - * 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. + * Disallow multiple mounts of the same device. + * Disallow mounting of a device that is currently in use + * (except for root, which might share swap device for miniroot). + * Flush out any old buffers remaining from a previous use. */ error = vfs_mountedon(devvp); if (error) - return error; - if (vcount(devvp) > 1) - return EBUSY; + return (error); + if (vcount(devvp) > 1 && devvp != rootvp) + return (EBUSY); + vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0); + VOP_UNLOCK(devvp, 0, p); if (error) - return error; + return (error); - /* - * Now open the block special file. - */ - error = VOP_OPEN(devvp, ronly ? FREAD : FREAD | FWRITE, FSCRED, p); + ronly = (mp->mnt_flag & MNT_RDONLY) != 0; + error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p); if (error) - 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) { + return (error); + + bp = NULL; /* both used in error_exit */ + pmp = NULL; + +#ifndef __FreeBSD__ + if (argp->flags & MSDOSFSMNT_GEMDOSFS) { + /* + * We need the disklabel to calculate the size of a FAT entry + * later on. Also make sure the partition contains a filesystem + * of type FS_MSDOS. This doesn't work for floppies, so we have + * to check for them too. + * + * At least some parts of the msdos fs driver seem to assume + * that the size of a disk block will always be 512 bytes. + * Let's check it... + */ + error = VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, + FREAD, NOCRED, p); + if (error) + goto error_exit; + tmp = dpart.part->p_fstype; + dtype = dpart.disklab->d_type; + bsize = dpart.disklab->d_secsize; + if (bsize != 512 || (dtype!=DTYPE_FLOPPY && tmp!=FS_MSDOS)) { + error = EINVAL; + goto error_exit; + } } #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" + * Read the boot sector of the filesystem, and then check the + * boot signature. If not a dos boot sector then error out. */ #ifdef PC98 devvp->v_flag &= 0xffff; - error = bread(devvp, 0, 1024, NOCRED, &bp0); + error = bread(devvp, 0, 1024, NOCRED, &bp); #else - error = bread(devvp, 0, 512, NOCRED, &bp0); + error = bread(devvp, 0, 512, NOCRED, &bp); #endif if (error) 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 -#ifdef PC98 - if (bsp->bs50.bsBootSectSig != BOOTSIG && - bsp->bs50.bsBootSectSig != 0 && /* PC98 DOS 3.3x */ - bsp->bs50.bsBootSectSig != 15760 && /* PC98 DOS 5.0 */ - bsp->bs50.bsBootSectSig != 64070) { /* PC98 DOS 3.3B */ + bp->b_flags |= B_AGE; + bsp = (union bootsector *)bp->b_data; + b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB; + b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB; + b710 = (struct byte_bpb710 *)bsp->bs710.bsPBP; + +#ifndef __FreeBSD__ + if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) { +#endif +#ifdef PC98 + if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 + || bsp->bs50.bsBootSectSig1 != BOOTSIG1 + && bsp->bs50.bsBootSectSig0 != 0 /* PC98 DOS 3.3x */ + || bsp->bs50.bsBootSectSig1 != 0 + && bsp->bs50.bsBootSectSig0 != 0x90 /* PC98 DOS 5.0 */ + || bsp->bs50.bsBootSectSig1 != 0x3d + && bsp->bs50.bsBootSectSig0 != 0x46 /* PC98 DOS 3.3B */ + || bsp->bs50.bsBootSectSig1 != 0xfa) { #else - if (bsp->bs50.bsBootSectSig != BOOTSIG) { + if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 + || bsp->bs50.bsBootSectSig1 != BOOTSIG1) { #endif - error = EINVAL; - goto error_exit; + error = EINVAL; + goto error_exit; + } +#ifndef __FreeBSD__ } #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); @@ -353,28 +470,34 @@ mountmsdosfs(devvp, mp, p) * 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. */ + SecPerClust = b50->bpbSecPerClust; 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); + pmp->pm_Media = b50->bpbMedia; - /* XXX - We should probably check more values here */ - if (!pmp->pm_BytesPerSec || !pmp->pm_SectPerClust || - !pmp->pm_Heads || pmp->pm_Heads > 255 || -#ifdef PC98 - !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 255) { -#else - !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) { +#ifndef __FreeBSD__ + if (!(argp->flags & MSDOSFSMNT_GEMDOSFS)) { #endif - error = EINVAL; - goto error_exit; + /* XXX - We should probably check more values here */ + if (!pmp->pm_BytesPerSec || !SecPerClust + || !pmp->pm_Heads || pmp->pm_Heads > 255 +#ifdef PC98 + || !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 255) { +#else + || !pmp->pm_SecPerTrack || pmp->pm_SecPerTrack > 63) { +#endif + error = EINVAL; + goto error_exit; + } +#ifndef __FreeBSD__ } +#endif if (pmp->pm_Sectors == 0) { pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); @@ -411,86 +534,193 @@ mountmsdosfs(devvp, mp, p) } pc98_wrk = pmp->pm_BytesPerSec / Phy_Sector_Size; pmp->pm_BytesPerSec = Phy_Sector_Size; - pmp->pm_SectPerClust = pmp->pm_SectPerClust * pc98_wrk; + SecPerClust = SecPerClust * pc98_wrk; pmp->pm_HugeSectors = pmp->pm_HugeSectors * pc98_wrk; pmp->pm_ResSectors = pmp->pm_ResSectors * pc98_wrk; pmp->pm_FATsecs = pmp->pm_FATsecs * pc98_wrk; pmp->pm_SecPerTrack = pmp->pm_SecPerTrack * pc98_wrk; pmp->pm_HiddenSects = pmp->pm_HiddenSects * pc98_wrk; #endif /* */ + if (pmp->pm_HugeSectors > 0xffffffff / pmp->pm_BytesPerSec + 1) { + /* + * We cannot deal currently with this size of disk + * due to fileid limitations (see msdosfs_getattr and + * msdosfs_readdir) + */ + error = EINVAL; + goto error_exit; + } + + if (pmp->pm_RootDirEnts == 0) { + if (bsp->bs710.bsBootSectSig2 != BOOTSIG2 + || bsp->bs710.bsBootSectSig3 != BOOTSIG3 + || pmp->pm_Sectors + || pmp->pm_FATsecs + || getushort(b710->bpbFSVers)) { + error = EINVAL; + goto error_exit; + } + pmp->pm_fatmask = FAT32_MASK; + pmp->pm_fatmult = 4; + pmp->pm_fatdiv = 1; + pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs); + if (getushort(b710->bpbExtFlags) & FATMIRROR) + pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM; + else + pmp->pm_flags |= MSDOSFS_FATMIRROR; + } else + pmp->pm_flags |= MSDOSFS_FATMIRROR; + +#ifndef __FreeBSD__ + if (argp->flags & MSDOSFSMNT_GEMDOSFS) { + if (FAT32(pmp)) { + /* + * GEMDOS doesn't know fat32. + */ + error = EINVAL; + goto error_exit; + } + + /* + * Check a few values (could do some more): + * - logical sector size: power of 2, >= block size + * - sectors per cluster: power of 2, >= 1 + * - number of sectors: >= 1, <= size of partition + */ + if ( (SecPerClust == 0) + || (SecPerClust & (SecPerClust - 1)) + || (pmp->pm_BytesPerSec < bsize) + || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1)) + || (pmp->pm_HugeSectors == 0) + || (pmp->pm_HugeSectors * (pmp->pm_BytesPerSec / bsize) + > dpart.part->p_size) + ) { + error = EINVAL; + goto error_exit; + } + /* + * XXX - Many parts of the msdos fs driver seem to assume that + * the number of bytes per logical sector (BytesPerSec) will + * always be the same as the number of bytes per disk block + * Let's pretend it is. + */ + tmp = pmp->pm_BytesPerSec / bsize; + pmp->pm_BytesPerSec = bsize; + pmp->pm_HugeSectors *= tmp; + pmp->pm_HiddenSects *= tmp; + pmp->pm_ResSectors *= tmp; + pmp->pm_Sectors *= tmp; + pmp->pm_FATsecs *= tmp; + SecPerClust *= tmp; + } +#endif 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; + if (FAT32(pmp)) { + pmp->pm_rootdirblk = getulong(b710->bpbRootClust); + pmp->pm_firstcluster = pmp->pm_fatblk + + (pmp->pm_FATs * pmp->pm_FATsecs); + pmp->pm_fsinfo = getushort(b710->bpbFSInfo); + } else { + pmp->pm_rootdirblk = pmp->pm_fatblk + + (pmp->pm_FATs * pmp->pm_FATsecs); + pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry) + + pmp->pm_BytesPerSec - 1) + / 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; + SecPerClust; pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1; pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec; + +#ifndef __FreeBSD__ + if (argp->flags & MSDOSFSMNT_GEMDOSFS) { + if ((pmp->pm_nmbrofclusters <= (0xff0 - 2)) + && ((dtype == DTYPE_FLOPPY) || ((dtype == DTYPE_VNODE) + && ((pmp->pm_Heads == 1) || (pmp->pm_Heads == 2)))) + ) { + pmp->pm_fatmask = FAT12_MASK; + pmp->pm_fatmult = 3; + pmp->pm_fatdiv = 2; + } else { + pmp->pm_fatmask = FAT16_MASK; + pmp->pm_fatmult = 2; + pmp->pm_fatdiv = 1; + } + } else +#endif + if (pmp->pm_fatmask == 0) { + if (pmp->pm_maxcluster + <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) { + /* + * This will usually be a floppy disk. This size makes + * sure that one fat entry will not be split across + * multiple blocks. + */ + pmp->pm_fatmask = FAT12_MASK; + pmp->pm_fatmult = 3; + pmp->pm_fatdiv = 2; + } else { + pmp->pm_fatmask = FAT16_MASK; + pmp->pm_fatmult = 2; + pmp->pm_fatdiv = 1; + } + } 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(): Warning: root directory is not a multiple of the clustersize in length\n"); + pmp->pm_bnshift = ffs(pmp->pm_BytesPerSec) - 1; /* * 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) { + pmp->pm_bpcluster = SecPerClust * pmp->pm_BytesPerSec; + pmp->pm_crbomask = pmp->pm_bpcluster - 1; + pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1; + + /* + * Check for valid cluster size + * must be a power of 2 + */ + if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) { 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; - } - -#ifdef PC98 - if (Phy_Sector_Size == 512) { - pmp->pm_brbomask = 0x01ff; /* 512 byte blocks only (so far) */ - pmp->pm_bnshift = 9; /* shift right 9 bits to get bn */ - } else { - pmp->pm_brbomask = 0x03ff; - pmp->pm_bnshift = 10; - } -#else - pmp->pm_brbomask = 0x01ff; /* 512 byte blocks only (so far) */ - pmp->pm_bnshift = 9; /* shift right 9 bits to get bn */ -#endif /* * Release the bootsector buffer. */ - brelse(bp0); - bp0 = NULL; + brelse(bp); + bp = NULL; + + /* + * Check FSInfo. + */ + if (pmp->pm_fsinfo) { + struct fsinfo *fp; + + if ((error = bread(devvp, pmp->pm_fsinfo, 1024, NOCRED, &bp)) != 0) + goto error_exit; + fp = (struct fsinfo *)bp->b_data; + if (!bcmp(fp->fsisig1, "RRaA", 4) + && !bcmp(fp->fsisig2, "rrAa", 4) + && !bcmp(fp->fsisig3, "\0\0\125\252", 4) + && !bcmp(fp->fsisig4, "\0\0\125\252", 4)) + pmp->pm_nxtfree = getulong(fp->fsinxtfree); + else + pmp->pm_fsinfo = 0; + brelse(bp); + bp = NULL; + } + + /* + * Check and validate (or perhaps invalidate?) the fsinfo structure? XXX + */ /* * Allocate memory for the bitmap of allocated clusters, and then @@ -510,8 +740,7 @@ mountmsdosfs(devvp, mp, p) /* * Have the inuse map filled in. */ - error = fillinusemap(pmp); - if (error) + if ((error = fillinusemap(pmp)) != 0) goto error_exit; /* @@ -520,13 +749,15 @@ mountmsdosfs(devvp, mp, p) * 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; + if (mp->mnt_flag & MNT_SYNCHRONOUS) + pmp->pm_flags |= MSDOSFSMNT_WAITONFAT; /* * Finish up. */ - pmp->pm_ronly = ronly; - if (ronly == 0) + if (ronly) + pmp->pm_flags |= MSDOSFSMNT_RONLY; + else pmp->pm_fmod = 1; mp->mnt_data = (qaddr_t) pmp; mp->mnt_stat.f_fsid.val[0] = (long)dev; @@ -536,19 +767,17 @@ mountmsdosfs(devvp, mp, p) return 0; -error_exit:; - if (bp0) - brelse(bp0); - if (needclose) - (void) VOP_CLOSE(devvp, ronly ? FREAD : FREAD | FWRITE, - NOCRED, p); +error_exit: + if (bp) + brelse(bp); + (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; + free(pmp->pm_inusemap, M_MSDOSFSFAT); + free(pmp, M_MSDOSFSMNT); + mp->mnt_data = (qaddr_t)0; } - return error; + return (error); } static int @@ -557,7 +786,8 @@ msdosfs_start(mp, flags, p) int flags; struct proc *p; { - return 0; + + return (0); } /* @@ -569,30 +799,47 @@ msdosfs_unmount(mp, mntflags, p) int mntflags; struct proc *p; { - int flags = 0; - int error; - struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; + struct msdosfsmount *pmp; + int error, flags; - /* 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) { + flags = 0; + if (mntflags & MNT_FORCE) flags |= FORCECLOSE; - } error = vflush(mp, NULLVP, flags); if (error) return error; + pmp = VFSTOMSDOSFS(mp); pmp->pm_devvp->v_specflags &= ~SI_MOUNTEDON; - error = VOP_CLOSE(pmp->pm_devvp, pmp->pm_ronly ? FREAD : FREAD | FWRITE, +#ifdef MSDOSFS_DEBUG + { + struct vnode *vp = pmp->pm_devvp; + + printf("msdosfs_umount(): just before calling VOP_CLOSE()\n"); + printf("flag %08lx, usecount %d, writecount %d, holdcnt %ld\n", + vp->v_flag, vp->v_usecount, vp->v_writecount, vp->v_holdcnt); + printf("lastr %d, id %lu, mount %p, op %p\n", + vp->v_lastr, vp->v_id, vp->v_mount, vp->v_op); + printf("freef %p, freeb %p, mount %p\n", + vp->v_freelist.tqe_next, vp->v_freelist.tqe_prev, + vp->v_mount); + printf("cleanblkhd %p, dirtyblkhd %p, numoutput %ld, type %d\n", + vp->v_cleanblkhd.lh_first, + vp->v_dirtyblkhd.lh_first, + vp->v_numoutput, vp->v_type); + printf("union %p, tag %d, data[0] %08x, data[1] %08x\n", + vp->v_socket, vp->v_tag, + ((u_int *)vp->v_data)[0], + ((u_int *)vp->v_data)[1]); + } +#endif + error = VOP_CLOSE(pmp->pm_devvp, (pmp->pm_flags&MSDOSFSMNT_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; + free(pmp->pm_inusemap, M_MSDOSFSFAT); + free(pmp, M_MSDOSFSMNT); + mp->mnt_data = (qaddr_t)0; mp->mnt_flag &= ~MNT_LOCAL; - return error; + return (error); } static int @@ -600,18 +847,18 @@ msdosfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { + struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); 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 %p, pmp %p, ndep %p, vp %p\n", - mp, pmp, ndep, DETOV(ndep)); + printf("msdosfs_root(); mp %p, pmp %p\n", mp, pmp); #endif - if (error == 0) - *vpp = DETOV(ndep); - return error; + error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep); + if (error) + return (error); + *vpp = DETOV(ndep); + return (0); } static int @@ -631,11 +878,9 @@ msdosfs_statfs(mp, sbp, p) struct statfs *sbp; struct proc *p; { - struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; + struct msdosfsmount *pmp; - /* - * Fill in the stat block. - */ + pmp = VFSTOMSDOSFS(mp); sbp->f_bsize = pmp->pm_bpcluster; sbp->f_iosize = pmp->pm_bpcluster; sbp->f_blocks = pmp->pm_nmbrofclusters; @@ -643,23 +888,13 @@ msdosfs_statfs(mp, sbp, p) 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) { sbp->f_type = mp->mnt_vfc->vfc_typenum; - 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); + bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); + bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } -#if 0 - strncpy(&sbp->f_fstypename[0], mp->mnt_op->vfs_name, MFSNAMELEN); - sbp->f_fstypename[MFSNAMELEN] = '\0'; -#endif - return 0; + strncpy(sbp->f_fstypename, mp->mnt_vfc->vfc_name, MFSNAMELEN); + return (0); } static int @@ -669,39 +904,42 @@ msdosfs_sync(mp, waitfor, cred, p) struct ucred *cred; struct proc *p; { - struct vnode *vp; + struct vnode *vp, *nvp; struct denode *dep; - struct msdosfsmount *pmp; - int error; - int allerror = 0; - - pmp = (struct msdosfsmount *) mp->mnt_data; + struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); + int error, allerror = 0; /* * 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) + if (pmp->pm_fmod != 0) + if (pmp->pm_flags & MSDOSFSMNT_RONLY) panic("msdosfs_sync: rofs mod"); else { /* update fats here */ } - /* - * Go thru in memory denodes and write them out along with - * unwritten file blocks. + * Write back each (modified) denode. */ simple_lock(&mntvnode_slock); loop: - for (vp = mp->mnt_vnodelist.lh_first; vp; - vp = vp->v_mntvnodes.le_next) { - if (vp->v_mount != mp) /* not ours anymore */ + for (vp = mp->mnt_vnodelist.lh_first; + vp != NULL; + vp = nvp) { + /* + * If the vnode that we are about to sync is no longer + * assoicated with this mount point, start over. + */ + if (vp->v_mount != mp) goto loop; + simple_lock(&vp->v_interlock); + nvp = vp->v_mntvnodes.le_next; dep = VTODE(vp); - if ((dep->de_flag & (DE_MODIFIED | DE_UPDATE)) == 0 && - vp->v_dirtyblkhd.lh_first == NULL) { + if (vp->v_type == VNON || ((dep->de_flag & + (DE_ACCESS | DE_CREATE | DE_UPDATE | DE_MODIFIED)) == 0) + && vp->v_dirtyblkhd.lh_first == NULL) { simple_unlock(&vp->v_interlock); continue; } @@ -728,7 +966,7 @@ loop: error = VOP_FSYNC(pmp->pm_devvp, cred, waitfor, p); if (error) allerror = error; - return allerror; + return (allerror); } static int @@ -740,7 +978,7 @@ msdosfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) int *exflagsp; struct ucred **credanonp; { - struct msdosfsmount *pmp = (struct msdosfsmount *) mp->mnt_data; + struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); struct defid *defhp = (struct defid *) fhp; struct denode *dep; struct netcred *np; @@ -748,33 +986,33 @@ msdosfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) 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); + return (EACCES); + error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, &dep); if (error) { *vpp = NULLVP; - return error; + return (error); } *vpp = DETOV(dep); *exflagsp = np->netc_exflags; *credanonp = &np->netc_anon; - return 0; + return (0); } - static int msdosfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { - struct denode *dep = VTODE(vp); - struct defid *defhp = (struct defid *) fhp; + struct denode *dep; + struct defid *defhp; + dep = VTODE(vp); + 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; + /* defhp->defid_gen = dep->de_gen; */ + return (0); } static int diff --git a/sys/msdosfs/msdosfs_vnops.c b/sys/msdosfs/msdosfs_vnops.c index 3d7b3f7f5fe5..a78a4672c4d2 100644 --- a/sys/msdosfs/msdosfs_vnops.c +++ b/sys/msdosfs/msdosfs_vnops.c @@ -1,9 +1,9 @@ -/* $Id: msdosfs_vnops.c,v 1.54 1998/02/04 22:33:01 eivind Exp $ */ -/* $NetBSD: msdosfs_vnops.c,v 1.20 1994/08/21 18:44:13 ws Exp $ */ +/* $Id: msdosfs_vnops.c,v 1.55 1998/02/06 12:13:46 eivind Exp $ */ +/* $NetBSD: msdosfs_vnops.c,v 1.68 1998/02/10 14:10:04 mrg Exp $ */ /*- - * Copyright (C) 1994 Wolfgang Solfrank. - * Copyright (C) 1994 TooLs GmbH. + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * @@ -140,9 +140,20 @@ msdosfs_create(ap) int error; #ifdef MSDOSFS_DEBUG - printf("msdosfs_create(cnp %08x, vap %08x\n", cnp, ap->a_vap); + printf("msdosfs_create(cnp %p, vap %p\n", cnp, ap->a_vap); #endif + /* + * If this is the root directory and there is no space left we + * can't do anything. This is because the root directory can not + * change size. + */ + if (pdep->de_StartCluster == MSDOSFSROOT + && pdep->de_fndoffset >= pdep->de_FileSize) { + error = ENOSPC; + goto bad; + } + /* * Create a directory entry for the file, then call createde() to * have it installed. NOTE: DOS files are always executable. We @@ -150,28 +161,37 @@ msdosfs_create(ap) * readonly. */ #ifdef DIAGNOSTIC - if ((cnp->cn_flags & SAVENAME) == 0) + if ((cnp->cn_flags & HASBUF) == 0) panic("msdosfs_create: no name"); #endif bzero(&ndirent, sizeof(ndirent)); - TIMEVAL_TO_TIMESPEC(&time, &ts); - unix2dostime(&ts, &ndirent.de_Date, &ndirent.de_Time); - unix2dosfn((u_char *)cnp->cn_nameptr, ndirent.de_Name, cnp->cn_namelen); - ndirent.de_Attributes = (ap->a_vap->va_mode & VWRITE) - ? ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY; + error = uniqdosname(pdep, cnp, ndirent.de_Name); + if (error) + goto bad; + + ndirent.de_Attributes = (ap->a_vap->va_mode & VWRITE) ? + ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY; ndirent.de_StartCluster = 0; ndirent.de_FileSize = 0; ndirent.de_dev = pdep->de_dev; ndirent.de_devvp = pdep->de_devvp; - if ((error = createde(&ndirent, pdep, &dep)) == 0) { - *ap->a_vpp = DETOV(dep); - if ((cnp->cn_flags & SAVESTART) == 0) - zfree(namei_zone, cnp->cn_pnbuf); - } else { + ndirent.de_pmp = pdep->de_pmp; + ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; + TIMEVAL_TO_TIMESPEC(&time, &ts); + DETIMES(&ndirent, &ts, &ts, &ts); + error = createde(&ndirent, pdep, &dep, cnp); + if (error) + goto bad; + if ((cnp->cn_flags & SAVESTART) == 0) zfree(namei_zone, cnp->cn_pnbuf); - } - vput(ap->a_dvp); /* release parent dir */ - return error; + vput(ap->a_dvp); + *ap->a_vpp = DETOV(dep); + return (0); + +bad: + zfree(namei_zone, cnp->cn_pnbuf); + vput(ap->a_dvp); + return (error); } static int @@ -183,24 +203,22 @@ msdosfs_mknod(ap) struct vattr *a_vap; } */ *ap; { - int error; switch (ap->a_vap->va_type) { case VDIR: - error = msdosfs_mkdir((struct vop_mkdir_args *)ap); + return (msdosfs_mkdir((struct vop_mkdir_args *)ap)); break; case VREG: - error = msdosfs_create((struct vop_create_args *)ap); + return (msdosfs_create((struct vop_create_args *)ap)); break; default: - error = EINVAL; zfree(namei_zone, ap->a_cnp->cn_pnbuf); vput(ap->a_dvp); - break; + return (EINVAL); } - return error; + /* NOTREACHED */ } static int @@ -214,10 +232,13 @@ msdosfs_close(ap) { struct vnode *vp = ap->a_vp; struct denode *dep = VTODE(vp); + struct timespec ts; simple_lock(&vp->v_interlock); - if (vp->v_usecount > 1) - DE_TIMES(dep, &time); + if (vp->v_usecount > 1) { + TIMEVAL_TO_TIMESPEC(&time, &ts); + DETIMES(dep, &ts, &ts, &ts); + } simple_unlock(&vp->v_interlock); return 0; } @@ -309,9 +330,15 @@ msdosfs_getattr(ap) { u_int cn; struct denode *dep = VTODE(ap->a_vp); + struct msdosfsmount *pmp = dep->de_pmp; struct vattr *vap = ap->a_vap; + mode_t mode; + struct timespec ts; + u_long dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry); + u_long fileid; - DE_TIMES(dep, &time); + TIMEVAL_TO_TIMESPEC(&time, &ts); + DETIMES(dep, &ts, &ts, &ts); vap->va_fsid = dep->de_dev; /* * The following computation of the fileid must be the same as that @@ -319,41 +346,44 @@ msdosfs_getattr(ap) * doesn't work. */ if (dep->de_Attributes & ATTR_DIRECTORY) { - if ((cn = dep->de_StartCluster) == MSDOSFSROOT) - cn = 1; + fileid = cntobn(pmp, dep->de_StartCluster) * dirsperblk; + if (dep->de_StartCluster == MSDOSFSROOT) + fileid = 1; } else { - if ((cn = dep->de_dirclust) == MSDOSFSROOT) - cn = 1; - cn = (cn << 16) | (dep->de_diroffset & 0xffff); + fileid = cntobn(pmp, dep->de_dirclust) * dirsperblk; + if (dep->de_dirclust == MSDOSFSROOT) + fileid = roottobn(pmp, 0) * dirsperblk; + fileid += dep->de_diroffset / sizeof(struct direntry); } - vap->va_fileid = cn; - vap->va_mode = (S_IXUSR|S_IXGRP|S_IXOTH) | (S_IRUSR|S_IRGRP|S_IROTH) | - ((dep->de_Attributes & ATTR_READONLY) ? 0 : (S_IWUSR|S_IWGRP|S_IWOTH)); - vap->va_mode &= dep->de_pmp->pm_mask; - if (dep->de_Attributes & ATTR_DIRECTORY) - vap->va_mode |= S_IFDIR; + vap->va_fileid = fileid; + if ((dep->de_Attributes & ATTR_READONLY) == 0) + mode = S_IRWXU|S_IRWXG|S_IRWXO; + else + mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; + vap->va_mode = mode & pmp->pm_mask; + vap->va_uid = pmp->pm_uid; + vap->va_gid = pmp->pm_gid; vap->va_nlink = 1; - vap->va_gid = dep->de_pmp->pm_gid; - vap->va_uid = dep->de_pmp->pm_uid; vap->va_rdev = 0; vap->va_size = dep->de_FileSize; - dos2unixtime(dep->de_Date, dep->de_Time, &vap->va_atime); - vap->va_mtime = vap->va_atime; -#if 0 -#ifndef MSDOSFS_NODIRMOD - if (vap->va_mode & S_IFDIR) - TIMEVAL_TO_TIMESPEC(&time, &vap->va_mtime); -#endif -#endif - vap->va_ctime = vap->va_atime; - vap->va_flags = (dep->de_Attributes & ATTR_ARCHIVE) ? 0 : SF_ARCHIVED; + dos2unixtime(dep->de_MDate, dep->de_MTime, 0, &vap->va_mtime); + if (pmp->pm_flags & MSDOSFSMNT_LONGNAME) { + dos2unixtime(dep->de_ADate, 0, 0, &vap->va_atime); + dos2unixtime(dep->de_CDate, dep->de_CTime, dep->de_CHun, &vap->va_ctime); + } else { + vap->va_atime = vap->va_mtime; + vap->va_ctime = vap->va_mtime; + } + vap->va_flags = 0; + if ((dep->de_Attributes & ATTR_ARCHIVE) == 0) + vap->va_flags |= SF_ARCHIVED; vap->va_gen = 0; - vap->va_blocksize = dep->de_pmp->pm_bpcluster; - vap->va_bytes = (dep->de_FileSize + dep->de_pmp->pm_crbomask) & - ~(dep->de_pmp->pm_crbomask); + vap->va_blocksize = pmp->pm_bpcluster; + vap->va_bytes = + (dep->de_FileSize + pmp->pm_crbomask) & ~pmp->pm_crbomask; vap->va_type = ap->a_vp->v_type; vap->va_filerev = dep->de_modrev; - return 0; + return (0); } static int @@ -367,10 +397,16 @@ msdosfs_setattr(ap) { struct vnode *vp = ap->a_vp; struct denode *dep = VTODE(ap->a_vp); + struct msdosfsmount *pmp = dep->de_pmp; struct vattr *vap = ap->a_vap; struct ucred *cred = ap->a_cred; int error = 0; +#ifdef MSDOSFS_DEBUG + printf("msdosfs_setattr(): vp %p, vap %p, cred %p, p %p\n", + ap->a_vp, vap, cred, ap->a_p); +#endif + /* * Check for unsettable attributes. */ @@ -378,12 +414,21 @@ msdosfs_setattr(ap) (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { +#ifdef MSDOSFS_DEBUG + printf("msdosfs_setattr(): returning EINVAL\n"); + printf(" va_type %d, va_nlink %x, va_fsid %lx, va_fileid %lx\n", + vap->va_type, vap->va_nlink, vap->va_fsid, vap->va_fileid); + printf(" va_blocksize %lx, va_rdev %x, va_bytes %qx, va_gen %lx\n", + vap->va_blocksize, vap->va_rdev, vap->va_bytes, vap->va_gen); + printf(" va_uid %x, va_gid %x\n", + vap->va_uid, vap->va_gid); +#endif return (EINVAL); } if (vap->va_flags != VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); - if (cred->cr_uid != dep->de_pmp->pm_uid && + if (cred->cr_uid != pmp->pm_uid && (error = suser(cred, &ap->a_p->p_acflag))) return (error); /* @@ -411,19 +456,26 @@ msdosfs_setattr(ap) dep->de_flag |= DE_MODIFIED; } - if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (uid_t)VNOVAL) { + if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) { + uid_t uid; + gid_t gid; + if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); - if ((cred->cr_uid != dep->de_pmp->pm_uid || - vap->va_uid != dep->de_pmp->pm_uid || - (vap->va_gid != dep->de_pmp->pm_gid && - !groupmember(vap->va_gid, cred))) && + uid = vap->va_uid; + if (uid == (uid_t)VNOVAL) + uid = pmp->pm_uid; + gid = vap->va_gid; + if (gid == (gid_t)VNOVAL) + gid = pmp->pm_gid; + if ((cred->cr_uid != pmp->pm_uid || uid != pmp->pm_uid || + (gid != pmp->pm_gid && !groupmember(gid, cred))) && (error = suser(cred, &ap->a_p->p_acflag))) return error; - if (vap->va_uid != dep->de_pmp->pm_uid || - vap->va_gid != dep->de_pmp->pm_gid) + if (uid != pmp->pm_uid || gid != pmp->pm_gid) return EINVAL; } + if (vap->va_size != VNOVAL) { /* * Disallow write attempts on read-only file systems; @@ -443,41 +495,45 @@ msdosfs_setattr(ap) if (error) return error; } - if (vap->va_mtime.tv_sec != VNOVAL) { + if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); - if (cred->cr_uid != dep->de_pmp->pm_uid && + if (cred->cr_uid != pmp->pm_uid && (error = suser(cred, &ap->a_p->p_acflag)) && ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || - (error = VOP_ACCESS(vp, VWRITE, cred, ap->a_p)))) - return error; - dep->de_flag |= DE_UPDATE; - error = deupdat(dep, &vap->va_mtime, 1); - if (error) - return error; + (error = VOP_ACCESS(ap->a_vp, VWRITE, cred, ap->a_p)))) + return (error); + if (vp->v_type != VDIR) { + if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) == 0 && + vap->va_atime.tv_sec != VNOVAL) + unix2dostime(&vap->va_atime, &dep->de_ADate, NULL, NULL); + if (vap->va_mtime.tv_sec != VNOVAL) + unix2dostime(&vap->va_mtime, &dep->de_MDate, &dep->de_MTime, NULL); + dep->de_Attributes |= ATTR_ARCHIVE; + dep->de_flag |= DE_MODIFIED; + } } - /* * DOS files only have the ability to have their writability * attribute set, so we use the owner write bit to set the readonly * attribute. */ - error = 0; - if (vap->va_mode != (u_short) VNOVAL) { + if (vap->va_mode != (mode_t)VNOVAL) { if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); - if (cred->cr_uid != dep->de_pmp->pm_uid && + if (cred->cr_uid != pmp->pm_uid && (error = suser(cred, &ap->a_p->p_acflag))) - return error; - - /* We ignore the read and execute bits */ - if (vap->va_mode & VWRITE) - dep->de_Attributes &= ~ATTR_READONLY; - else - dep->de_Attributes |= ATTR_READONLY; - dep->de_flag |= DE_MODIFIED; + return (error); + if (vp->v_type != VDIR) { + /* We ignore the read and execute bits. */ + if (vap->va_mode & VWRITE) + dep->de_Attributes &= ~ATTR_READONLY; + else + dep->de_Attributes |= ATTR_READONLY; + dep->de_flag |= DE_MODIFIED; + } } - return error; + return (deupdat(dep, 1)); } static int @@ -491,11 +547,12 @@ msdosfs_read(ap) { int error = 0; int diff; + int blsize; int isadir; long n; long on; daddr_t lbn; - daddr_t rablock; + daddr_t rablock, rablock1; int rasize; struct buf *bp; struct vnode *vp = ap->a_vp; @@ -507,34 +564,33 @@ msdosfs_read(ap) * If they didn't ask for any data, then we are done. */ if (uio->uio_resid == 0) - return 0; + return (0); if (uio->uio_offset < 0) - return EINVAL; + return (EINVAL); isadir = dep->de_Attributes & ATTR_DIRECTORY; do { - lbn = uio->uio_offset >> pmp->pm_cnshift; + lbn = de_cluster(pmp, uio->uio_offset); on = uio->uio_offset & pmp->pm_crbomask; n = min((u_long) (pmp->pm_bpcluster - on), uio->uio_resid); diff = dep->de_FileSize - uio->uio_offset; if (diff <= 0) - return 0; - /* convert cluster # to block # if a directory */ - if (isadir) { - error = pcbmap(dep, lbn, &lbn, 0); - if (error) - return error; - } + return (0); if (diff < n) n = diff; + /* convert cluster # to block # if a directory */ + if (isadir) { + error = pcbmap(dep, lbn, &lbn, 0, &blsize); + if (error) + return (error); + } /* * If we are operating on a directory file then be sure to * do i/o with the vnode for the filesystem instead of the * vnode for the directory. */ if (isadir) { - error = bread(pmp->pm_devvp, lbn, pmp->pm_bpcluster, - NOCRED, &bp); + error = bread(pmp->pm_devvp, lbn, blsize, NOCRED, &bp); } else { rablock = lbn + 1; #ifdef PC98 @@ -545,36 +601,28 @@ msdosfs_read(ap) vp->v_flag |= 0x10000; #endif if (vp->v_lastr + 1 == lbn && - rablock * pmp->pm_bpcluster < dep->de_FileSize) { + de_cn2off(pmp, rablock) < dep->de_FileSize) { + rablock1 = de_cn2bn(pmp, rablock); rasize = pmp->pm_bpcluster; - error = breadn(vp, lbn, pmp->pm_bpcluster, - &rablock, &rasize, 1, - NOCRED, &bp); - } else { - error = bread(vp, lbn, pmp->pm_bpcluster, NOCRED, - &bp); - } + error = breadn(vp, de_cn2bn(pmp, lbn), + pmp->pm_bpcluster, &rablock1, &rasize, 1, + NOCRED, &bp); + } else + error = bread(vp, de_cn2bn(pmp, lbn), + pmp->pm_bpcluster, NOCRED, &bp); vp->v_lastr = lbn; } n = min(n, pmp->pm_bpcluster - bp->b_resid); if (error) { brelse(bp); - return error; + return (error); } error = uiomove(bp->b_data + on, (int) n, uio); - /* - * If we have read everything from this block or have read - * to end of file then we are done with this block. Mark - * it to say the buffer can be reused if need be. - */ -#if 0 - if (n + on == pmp->pm_bpcluster || - uio->uio_offset == dep->de_FileSize) - bp->b_flags |= B_AGE; -#endif + if (!isadir) + dep->de_flag |= DE_ACCESS; brelse(bp); } while (error == 0 && uio->uio_resid > 0 && n != 0); - return error; + return (error); } /* @@ -590,10 +638,9 @@ msdosfs_write(ap) } */ *ap; { int n; - int isadir; int croffset; int resid; - int osize; + u_long osize; int error = 0; u_long count; daddr_t bn, lastcn; @@ -606,60 +653,42 @@ msdosfs_write(ap) struct denode *dep = VTODE(vp); struct msdosfsmount *pmp = dep->de_pmp; struct ucred *cred = ap->a_cred; - struct timespec ts; #ifdef MSDOSFS_DEBUG - printf("msdosfs_write(vp %08x, uio %08x, ioflag %08x, cred %08x\n", - vp, uio, ioflag, cred); - printf("msdosfs_write(): diroff %d, dirclust %d, startcluster %d\n", - dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster); + printf("msdosfs_write(vp %p, uio %p, ioflag %x, cred %p\n", + vp, uio, ioflag, cred); + printf("msdosfs_write(): diroff %lu, dirclust %lu, startcluster %lu\n", + dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster); #endif switch (vp->v_type) { case VREG: if (ioflag & IO_APPEND) uio->uio_offset = dep->de_FileSize; - isadir = 0; thisvp = vp; break; - case VDIR: - if ((ioflag & IO_SYNC) == 0) - panic("msdosfs_write(): non-sync directory update"); - isadir = 1; - thisvp = pmp->pm_devvp; - break; - + return EISDIR; default: panic("msdosfs_write(): bad file type"); - break; } if (uio->uio_offset < 0) - return EINVAL; + return (EINVAL); if (uio->uio_resid == 0) - return 0; + return (0); /* * If they've exceeded their filesize limit, tell them about it. */ - if (vp->v_type == VREG && p && + if (p && ((uio->uio_offset + uio->uio_resid) > - p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) { + p->p_rlimit[RLIMIT_FSIZE].rlim_cur)) { psignal(p, SIGXFSZ); - return EFBIG; + return (EFBIG); } - /* - * If attempting to write beyond the end of the root directory we - * stop that here because the root directory can not grow. - */ - if ((dep->de_Attributes & ATTR_DIRECTORY) && - dep->de_StartCluster == MSDOSFSROOT && - (uio->uio_offset + uio->uio_resid) > dep->de_FileSize) - return ENOSPC; - /* * If the offset we are starting the write at is beyond the end of * the file, then they've done a seek. Unix filesystems allow @@ -669,7 +698,7 @@ msdosfs_write(ap) if (uio->uio_offset > dep->de_FileSize) { error = deextend(dep, uio->uio_offset, cred); if (error) - return error; + return (error); } /* @@ -678,7 +707,6 @@ msdosfs_write(ap) resid = uio->uio_resid; osize = dep->de_FileSize; - #ifdef PC98 /* * 1024byte/sector support @@ -691,21 +719,17 @@ msdosfs_write(ap) * size ahead of the time to hopefully get a contiguous area. */ if (uio->uio_offset + resid > osize) { - count = de_clcount(pmp, uio->uio_offset + resid) - de_clcount(pmp, osize); - if ((error = extendfile(dep, count, NULL, NULL, 0)) - && (error != ENOSPC || (ioflag & IO_UNIT))) + count = de_clcount(pmp, uio->uio_offset + resid) - + de_clcount(pmp, osize); + error = extendfile(dep, count, NULL, NULL, 0); + if (error && (error != ENOSPC || (ioflag & IO_UNIT))) goto errexit; lastcn = dep->de_fc[FC_LASTFC].fc_frcn; } else lastcn = de_clcount(pmp, osize) - 1; do { - bn = de_blk(pmp, uio->uio_offset); - if (isadir) { - error = pcbmap(dep, bn, &bn, 0); - if (error) - break; - } else if (bn > lastcn) { + if (de_cluster(pmp, uio->uio_offset) > lastcn) { error = ENOSPC; break; } @@ -718,6 +742,7 @@ msdosfs_write(ap) vnode_pager_setsize(vp, dep->de_FileSize); } + bn = de_blk(pmp, uio->uio_offset); if ((uio->uio_offset & pmp->pm_crbomask) == 0 && (de_blk(pmp, uio->uio_offset + uio->uio_resid) > de_blk(pmp, uio->uio_offset) || uio->uio_offset + uio->uio_resid >= dep->de_FileSize)) { @@ -732,27 +757,28 @@ msdosfs_write(ap) * Do the bmap now, since pcbmap needs buffers * for the fat table. (see msdosfs_strategy) */ - if (!isadir) { - if (bp->b_blkno == bp->b_lblkno) { - error = pcbmap(dep, bp->b_lblkno, - &bp->b_blkno, 0); - if (error) - bp->b_blkno = -1; - } - if (bp->b_blkno == -1) { - brelse(bp); - if (!error) - error = EIO; /* XXX */ - break; - } + if (bp->b_blkno == bp->b_lblkno) { + error = pcbmap(dep, + de_bn2cn(pmp, bp->b_lblkno), + &bp->b_blkno, 0, 0); + if (error) + bp->b_blkno = -1; + } + if (bp->b_blkno == -1) { + brelse(bp); + if (!error) + error = EIO; /* XXX */ + break; } } else { /* * The block we need to write into exists, so read it in. */ error = bread(thisvp, bn, pmp->pm_bpcluster, cred, &bp); - if (error) + if (error) { + brelse(bp); break; + } } /* @@ -774,9 +800,9 @@ msdosfs_write(ap) */ if (ioflag & IO_SYNC) (void) bwrite(bp); - else if (n + croffset == pmp->pm_bpcluster) { + else if (n + croffset == pmp->pm_bpcluster) bawrite(bp); - } else + else bdwrite(bp); dep->de_flag |= DE_UPDATE; } while (error == 0 && uio->uio_resid > 0); @@ -796,11 +822,9 @@ errexit: if (uio->uio_resid != resid) error = 0; } - } else if (ioflag & IO_SYNC) { - TIMEVAL_TO_TIMESPEC(&time, &ts); - error = deupdat(dep, &ts, 1); - } - return error; + } else if (ioflag & IO_SYNC) + error = deupdat(dep, 1); + return (error); } /* @@ -818,12 +842,9 @@ msdosfs_fsync(ap) struct proc *a_p; } */ *ap; { - register struct vnode *vp = ap->a_vp; - register struct buf *bp; - int wait = ap->a_waitfor == MNT_WAIT; - struct timespec ts; - struct buf *nbp; + struct vnode *vp = ap->a_vp; int s; + struct buf *bp, *nbp; /* * Flush all dirty buffers associated with a vnode. @@ -853,8 +874,7 @@ loop: } #endif splx(s); - TIMEVAL_TO_TIMESPEC(&time, &ts); - return deupdat(VTODE(vp), &ts, wait); + return (deupdat(VTODE(vp), ap->a_waitfor == MNT_WAIT)); } static int @@ -869,9 +889,12 @@ msdosfs_remove(ap) struct denode *dep = VTODE(ap->a_vp); struct denode *ddep = VTODE(ap->a_dvp); - error = removede(ddep,dep); + if (ap->a_vp->v_type == VDIR) + error = EPERM; + else + error = removede(ddep, dep); #ifdef MSDOSFS_DEBUG - printf("msdosfs_remove(), dep %08x, v_usecount %d\n", dep, ap->a_vp->v_usecount); + printf("msdosfs_remove(), dep %p, v_usecount %d\n", dep, ap->a_vp->v_usecount); #endif if (ddep == dep) vrele(ap->a_vp); @@ -879,7 +902,7 @@ msdosfs_remove(ap) vput(ap->a_vp); /* causes msdosfs_inactive() to be called * via vrele() */ vput(ap->a_dvp); - return error; + return (error); } /* @@ -962,22 +985,27 @@ msdosfs_rename(ap) struct componentname *a_tcnp; } */ *ap; { - u_char toname[11]; - int error; - int newparent = 0; - int sourceisadirectory = 0; - u_long cn; - daddr_t bn; + struct vnode *tdvp = ap->a_tdvp; + struct vnode *fvp = ap->a_fvp; + struct vnode *fdvp = ap->a_fdvp; struct vnode *tvp = ap->a_tvp; + struct componentname *tcnp = ap->a_tcnp; struct componentname *fcnp = ap->a_fcnp; struct proc *p = fcnp->cn_proc; + struct denode *ip, *xp, *dp, *zp; + u_char toname[11], oldname[11]; + u_long from_diroffset, to_diroffset; + u_char to_count; + int doingdirectory = 0, newparent = 0; + int error; + u_long cn; + daddr_t bn; struct denode *fddep; /* from file's parent directory */ struct denode *fdep; /* from file or directory */ struct denode *tddep; /* to file's parent directory */ struct denode *tdep; /* to file or directory */ struct msdosfsmount *pmp; struct direntry *dotdotp; - struct direntry *ep; struct buf *bp; fddep = VTODE(ap->a_fdvp); @@ -986,28 +1014,46 @@ msdosfs_rename(ap) tdep = tvp ? VTODE(tvp) : NULL; pmp = fddep->de_pmp; - /* Check for cross-device rename */ - if ((ap->a_fvp->v_mount != ap->a_tdvp->v_mount) || - (tvp && (ap->a_fvp->v_mount != tvp->v_mount))) { + pmp = VFSTOMSDOSFS(fdvp->v_mount); + +#ifdef DIAGNOSTIC + if ((tcnp->cn_flags & HASBUF) == 0 || + (fcnp->cn_flags & HASBUF) == 0) + panic("msdosfs_rename: no name"); +#endif + /* + * Check for cross-device rename. + */ + if ((fvp->v_mount != tdvp->v_mount) || + (tvp && (fvp->v_mount != tvp->v_mount))) { error = EXDEV; - goto bad; +abortit: + VOP_ABORTOP(tdvp, tcnp); + if (tdvp == tvp) + vrele(tdvp); + else + vput(tdvp); + if (tvp) + vput(tvp); + VOP_ABORTOP(fdvp, fcnp); + vrele(fdvp); + vrele(fvp); + return (error); } /* - * Convert the filename in tcnp into a dos filename. We copy this - * into the denode and directory entry for the destination - * file/directory. + * If source and dest are the same, do nothing. */ - unix2dosfn((u_char *) ap->a_tcnp->cn_nameptr, - toname, ap->a_tcnp->cn_namelen); + if (tvp == fvp) { + error = 0; + goto abortit; + } - /* - * At this point this is the lock state of the denodes: - * fddep referenced - * fdep referenced - * tddep locked - * tdep locked if it exists - */ + error = vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p); + if (error) + goto abortit; + dp = VTODE(fdvp); + ip = VTODE(fvp); /* * Be sure we are not renaming ".", "..", or an alias of ".". This @@ -1015,218 +1061,270 @@ msdosfs_rename(ap) * "ls" or "pwd" with the "." directory entry missing, and "cd .." * doesn't work if the ".." entry is missing. */ - if (fdep->de_Attributes & ATTR_DIRECTORY) { - if ((ap->a_fcnp->cn_namelen == 1 - && ap->a_fcnp->cn_nameptr[0] == '.') - || fddep == fdep - || (ap->a_fcnp->cn_flags | ap->a_tcnp->cn_flags) - & ISDOTDOT) { - VOP_ABORTOP(ap->a_tdvp, ap->a_tcnp); - vput(ap->a_tdvp); - if (tvp) - vput(tvp); - VOP_ABORTOP(ap->a_fdvp, ap->a_fcnp); - vrele(ap->a_fdvp); - vrele(ap->a_fvp); - return EINVAL; + if (ip->de_Attributes & ATTR_DIRECTORY) { + /* + * Avoid ".", "..", and aliases of "." for obvious reasons. + */ + if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') || + dp == ip || + (fcnp->cn_flags & ISDOTDOT) || + (tcnp->cn_flags & ISDOTDOT) || + (ip->de_flag & DE_RENAME)) { + VOP_UNLOCK(fvp, 0, p); + error = EINVAL; + goto abortit; } - sourceisadirectory = 1; + ip->de_flag |= DE_RENAME; + doingdirectory++; } /* - * If we are renaming a directory, and the directory is being moved - * to another directory, then we must be sure the destination - * directory is not in the subtree of the source directory. This - * could orphan everything under the source directory. - * doscheckpath() unlocks the destination's parent directory so we - * must look it up again to relock it. + * When the target exists, both the directory + * and target vnodes are returned locked. */ - if (fddep->de_StartCluster != tddep->de_StartCluster) + dp = VTODE(tdvp); + xp = tvp ? VTODE(tvp) : NULL; + /* + * Remember direntry place to use for destination + */ + to_diroffset = dp->de_fndoffset; + to_count = dp->de_fndcnt; + + /* + * If ".." must be changed (ie the directory gets a new + * parent) then the source directory must not be in the + * directory heirarchy above the target, as this would + * orphan everything below the source directory. Also + * the user must have write permission in the source so + * as to be able to change "..". We must repeat the call + * to namei, as the parent directory is unlocked by the + * call to doscheckpath(). + */ + error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc); + VOP_UNLOCK(fvp, 0, p); + if (VTODE(fdvp)->de_StartCluster != VTODE(tdvp)->de_StartCluster) newparent = 1; - if (sourceisadirectory && newparent) { - if (tdep) { - vput(ap->a_tvp); - tdep = NULL; - } - /* doscheckpath() vput()'s tddep */ - error = doscheckpath(fdep, tddep); - tddep = NULL; - if (error) + vrele(fdvp); + if (doingdirectory && newparent) { + if (error) /* write access check above */ goto bad; - if ((ap->a_tcnp->cn_flags & SAVESTART) == 0) - panic("msdosfs_rename(): lost to startdir"); - error = relookup(ap->a_tdvp, &tvp, ap->a_tcnp); + if (xp != NULL) + vput(tvp); + /* + * doscheckpath() vput()'s dp, + * so we have to do a relookup afterwards + */ + error = doscheckpath(ip, dp); if (error) - goto bad; - tddep = VTODE(ap->a_tdvp); - tdep = tvp ? VTODE(tvp) : NULL; + goto out; + if ((tcnp->cn_flags & SAVESTART) == 0) + panic("msdosfs_rename: lost to startdir"); + error = relookup(tdvp, &tvp, tcnp); + if (error) + goto out; + dp = VTODE(tdvp); + xp = tvp ? VTODE(tvp) : NULL; } - /* - * If the destination exists, then be sure its type (file or dir) - * matches that of the source. And, if it is a directory make sure - * it is empty. Then delete the destination. - */ - if (tdep) { - if (tdep->de_Attributes & ATTR_DIRECTORY) { - if (!sourceisadirectory) { - error = ENOTDIR; - goto bad; - } - if (!dosdirempty(tdep)) { + if (xp != NULL) { + /* + * Target must be empty if a directory and have no links + * to it. Also, ensure source and target are compatible + * (both directories, or both not directories). + */ + if (xp->de_Attributes & ATTR_DIRECTORY) { + if (!dosdirempty(xp)) { error = ENOTEMPTY; goto bad; } - cache_purge(DETOV(tddep)); - } else { /* destination is file */ - if (sourceisadirectory) { - error = EISDIR; + if (!doingdirectory) { + error = ENOTDIR; goto bad; } + cache_purge(tdvp); + } else if (doingdirectory) { + error = EISDIR; + goto bad; } - error = removede(tddep,tdep); + error = removede(dp, xp); if (error) goto bad; - vput(ap->a_tvp); - tdep = NULL; + vput(tvp); + xp = NULL; } /* - * If the source and destination are in the same directory then - * just read in the directory entry, change the name in the - * directory entry and write it back to disk. + * Convert the filename in tcnp into a dos filename. We copy this + * into the denode and directory entry for the destination + * file/directory. */ - if (newparent == 0) { - /* tddep and fddep point to the same denode here */ - vn_lock(ap->a_fvp, LK_EXCLUSIVE, p); /* ap->a_fdvp is already locked */ - error = readep(fddep->de_pmp, fdep->de_dirclust, - fdep->de_diroffset, &bp, &ep); - if (error) { - VOP_UNLOCK(ap->a_fvp, 0, p); - goto bad; - } - bcopy(toname, ep->deName, 11); - error = bwrite(bp); - if (error) { - VOP_UNLOCK(ap->a_fvp, 0, p); - goto bad; - } - bcopy(toname, fdep->de_Name, 11); /* update denode */ + error = uniqdosname(VTODE(tdvp), tcnp, toname); + if (error) + goto abortit; + + /* + * Since from wasn't locked at various places above, + * have to do a relookup here. + */ + fcnp->cn_flags &= ~MODMASK; + fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; + if ((fcnp->cn_flags & SAVESTART) == 0) + panic("msdosfs_rename: lost from startdir"); + if (!newparent) + VOP_UNLOCK(tdvp, 0, p); + (void) relookup(fdvp, &fvp, fcnp); + if (fvp == NULL) { /* - * fdep locked fddep and tddep point to the same denode - * which is locked tdep is NULL + * From name has disappeared. */ + if (doingdirectory) + panic("rename: lost dir entry"); + vrele(ap->a_fvp); + if (newparent) + VOP_UNLOCK(tdvp, 0, p); + vrele(tdvp); + return 0; + } + xp = VTODE(fvp); + zp = VTODE(fdvp); + from_diroffset = zp->de_fndoffset; + + /* + * Ensure that the directory entry still exists and has not + * changed till now. If the source is a file the entry may + * have been unlinked or renamed. In either case there is + * no further work to be done. If the source is a directory + * then it cannot have been rmdir'ed or renamed; this is + * prohibited by the DE_RENAME flag. + */ + if (xp != ip) { + if (doingdirectory) + panic("rename: lost dir entry"); + vrele(ap->a_fvp); + VOP_UNLOCK(fvp, 0, p); + if (newparent) + VOP_UNLOCK(fdvp, 0, p); + xp = NULL; } else { - u_long dirsize = 0L; + vrele(fvp); + xp = NULL; /* - * If the source and destination are in different - * directories, then mark the entry in the source directory - * as deleted and write a new entry in the destination - * directory. Then move the denode to the correct hash + * First write a new entry in the destination + * directory and mark the entry in the source directory + * as deleted. Then move the denode to the correct hash * chain for its new location in the filesystem. And, if * we moved a directory, then update its .. entry to point - * to the new parent directory. If we moved a directory - * will also insure that the directory entry on disk has a - * filesize of zero. + * to the new parent directory. */ - vn_lock(ap->a_fvp, LK_EXCLUSIVE, p); - bcopy(toname, fdep->de_Name, 11); /* update denode */ - if (fdep->de_Attributes & ATTR_DIRECTORY) { - dirsize = fdep->de_FileSize; - fdep->de_FileSize = 0; - } - error = createde(fdep, tddep, (struct denode **) 0); - if (fdep->de_Attributes & ATTR_DIRECTORY) { - fdep->de_FileSize = dirsize; - } + bcopy(ip->de_Name, oldname, 11); + bcopy(toname, ip->de_Name, 11); /* update denode */ + dp->de_fndoffset = to_diroffset; + dp->de_fndcnt = to_count; + error = createde(ip, dp, (struct denode **)0, tcnp); if (error) { - /* should put back filename */ - VOP_UNLOCK(ap->a_fvp, 0, p); + bcopy(oldname, ip->de_Name, 11); + if (newparent) + VOP_UNLOCK(fdvp, 0, p); + VOP_UNLOCK(fvp, 0, p); goto bad; } - vn_lock(ap->a_fdvp, LK_EXCLUSIVE, p); - error = readep(fddep->de_pmp, fddep->de_fndclust, - fddep->de_fndoffset, &bp, &ep); + ip->de_refcnt++; + zp->de_fndoffset = from_diroffset; + error = removede(zp, ip); if (error) { - VOP_UNLOCK(ap->a_fvp, 0, p); - VOP_UNLOCK(ap->a_fdvp, 0, p); + /* XXX should really panic here, fs is corrupt */ + if (newparent) + VOP_UNLOCK(fdvp, 0, p); + VOP_UNLOCK(fvp, 0, p); goto bad; } - ep->deName[0] = SLOT_DELETED; - error = bwrite(bp); - if (error) { - VOP_UNLOCK(ap->a_fvp, 0, p); - VOP_UNLOCK(ap->a_fdvp, 0, p); - goto bad; + if (!doingdirectory) { + error = pcbmap(dp, de_cluster(pmp, to_diroffset), 0, + &ip->de_dirclust, 0); + if (error) { + /* XXX should really panic here, fs is corrupt */ + if (newparent) + VOP_UNLOCK(fdvp, 0, p); + VOP_UNLOCK(fvp, 0, p); + goto bad; + } + if (ip->de_dirclust != MSDOSFSROOT) + ip->de_diroffset = to_diroffset & pmp->pm_crbomask; } - if (!sourceisadirectory) { - fdep->de_dirclust = tddep->de_fndclust; - fdep->de_diroffset = tddep->de_fndoffset; - reinsert(fdep); - } - VOP_UNLOCK(ap->a_fdvp, 0, p); + reinsert(ip); + if (newparent) + VOP_UNLOCK(fdvp, 0, p); } - /* fdep is still locked here */ /* * If we moved a directory to a new parent directory, then we must * fixup the ".." entry in the moved directory. */ - if (sourceisadirectory && newparent) { - cn = fdep->de_StartCluster; + if (doingdirectory && newparent) { + cn = ip->de_StartCluster; if (cn == MSDOSFSROOT) { /* this should never happen */ panic("msdosfs_rename(): updating .. in root directory?"); - } else { + } else bn = cntobn(pmp, cn); - } error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp); if (error) { - /* should really panic here, fs is corrupt */ - VOP_UNLOCK(ap->a_fvp, 0, p); + /* XXX should really panic here, fs is corrupt */ + brelse(bp); + VOP_UNLOCK(fvp, 0, p); goto bad; } - dotdotp = (struct direntry *) bp->b_data + 1; - putushort(dotdotp->deStartCluster, tddep->de_StartCluster); + dotdotp = (struct direntry *)bp->b_data + 1; + putushort(dotdotp->deStartCluster, dp->de_StartCluster); + if (FAT32(pmp)) + putushort(dotdotp->deHighClust, dp->de_StartCluster >> 16); error = bwrite(bp); - VOP_UNLOCK(ap->a_fvp, 0, p); if (error) { - /* should really panic here, fs is corrupt */ + /* XXX should really panic here, fs is corrupt */ + VOP_UNLOCK(fvp, 0, p); goto bad; } - } else - VOP_UNLOCK(ap->a_fvp, 0, p); -bad: ; - vrele(DETOV(fdep)); - vrele(DETOV(fddep)); - if (tdep) - vput(DETOV(tdep)); - if (tddep) - vput(DETOV(tddep)); - return error; + } + + VOP_UNLOCK(fvp, 0, p); +bad: + if (xp) + vput(tvp); + vput(tdvp); +out: + ip->de_flag &= ~DE_RENAME; + vrele(fdvp); + vrele(fvp); + return (error); + } static struct { struct direntry dot; struct direntry dotdot; -} dosdirtemplate = { - { - ". ", " ", /* the . entry */ - ATTR_DIRECTORY, /* file attribute */ - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* resevered */ - {210, 4}, {210, 4}, /* time and date */ - {0, 0}, /* startcluster */ - {0, 0, 0, 0}, /* filesize */ - },{ - ".. ", " ", /* the .. entry */ - ATTR_DIRECTORY, /* file attribute */ - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* resevered */ - {210, 4}, {210, 4}, /* time and date */ - {0, 0}, /* startcluster */ - {0, 0, 0, 0}, /* filesize */ - } +} dosdirtemplate = { + { ". ", " ", /* the . entry */ + ATTR_DIRECTORY, /* file attribute */ + 0, /* reserved */ + 0, { 0, 0 }, { 0, 0 }, /* create time & date */ + { 0, 0 }, /* access date */ + { 0, 0 }, /* high bits of start cluster */ + { 210, 4 }, { 210, 4 }, /* modify time & date */ + { 0, 0 }, /* startcluster */ + { 0, 0, 0, 0 } /* filesize */ + }, + { ".. ", " ", /* the .. entry */ + ATTR_DIRECTORY, /* file attribute */ + 0, /* reserved */ + 0, { 0, 0 }, { 0, 0 }, /* create time & date */ + { 0, 0 }, /* access date */ + { 0, 0 }, /* high bits of start cluster */ + { 210, 4 }, { 210, 4 }, /* modify time & date */ + { 0, 0 }, /* startcluster */ + { 0, 0, 0, 0 } /* filesize */ + } }; static int @@ -1238,42 +1336,41 @@ msdosfs_mkdir(ap) struct vattr *a_vap; } */ *ap; { - int bn; - int error; - u_long newcluster; - struct denode *pdep; - struct denode *ndep; - struct direntry *denp; + struct componentname *cnp = ap->a_cnp; struct denode ndirent; - struct msdosfsmount *pmp; + struct denode *dep; + struct denode *pdep = VTODE(ap->a_dvp); + int error; + int bn; + u_long newcluster, pcl; + struct direntry *denp; + struct msdosfsmount *pmp = pdep->de_pmp; struct buf *bp; struct timespec ts; - u_short dDate, dTime; - - pdep = VTODE(ap->a_dvp); /* * If this is the root directory and there is no space left we * can't do anything. This is because the root directory can not * change size. */ - if (pdep->de_StartCluster == MSDOSFSROOT && pdep->de_fndclust == (u_long)-1) { - zfree(namei_zone, ap->a_cnp->cn_pnbuf); - vput(ap->a_dvp); - return ENOSPC; + if (pdep->de_StartCluster == MSDOSFSROOT + && pdep->de_fndoffset >= pdep->de_FileSize) { + error = ENOSPC; + goto bad2; } - pmp = pdep->de_pmp; - /* * Allocate a cluster to hold the about to be created directory. */ error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL); - if (error) { - zfree(namei_zone, ap->a_cnp->cn_pnbuf); - vput(ap->a_dvp); - return error; - } + if (error) + goto bad2; + + bzero(&ndirent, sizeof(ndirent)); + ndirent.de_pmp = pmp; + ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; + TIMEVAL_TO_TIMESPEC(&time, &ts); + DETIMES(&ndirent, &ts, &ts, &ts); /* * Now fill the cluster with the "." and ".." entries. And write @@ -1285,50 +1382,66 @@ msdosfs_mkdir(ap) bp = getblk(pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0); bzero(bp->b_data, pmp->pm_bpcluster); bcopy(&dosdirtemplate, bp->b_data, sizeof dosdirtemplate); - denp = (struct direntry *) bp->b_data; - putushort(denp->deStartCluster, newcluster); - TIMEVAL_TO_TIMESPEC(&time, &ts); - unix2dostime(&ts, &dDate, &dTime); - putushort(denp->deDate, dDate); - putushort(denp->deTime, dTime); - denp++; - putushort(denp->deStartCluster, pdep->de_StartCluster); - putushort(denp->deDate, dDate); - putushort(denp->deTime, dTime); - error = bwrite(bp); - if (error) { - clusterfree(pmp, newcluster, NULL); - zfree(namei_zone, ap->a_cnp->cn_pnbuf); - vput(ap->a_dvp); - return error; + denp = (struct direntry *)bp->b_data; + putushort(denp[0].deStartCluster, newcluster); + putushort(denp[0].deCDate, ndirent.de_CDate); + putushort(denp[0].deCTime, ndirent.de_CTime); + denp[0].deCHundredth = ndirent.de_CHun; + putushort(denp[0].deADate, ndirent.de_ADate); + putushort(denp[0].deMDate, ndirent.de_MDate); + putushort(denp[0].deMTime, ndirent.de_MTime); + pcl = pdep->de_StartCluster; + if (FAT32(pmp) && pcl == pmp->pm_rootdirblk) + pcl = 0; + putushort(denp[1].deStartCluster, pcl); + putushort(denp[1].deCDate, ndirent.de_CDate); + putushort(denp[1].deCTime, ndirent.de_CTime); + denp[1].deCHundredth = ndirent.de_CHun; + putushort(denp[1].deADate, ndirent.de_ADate); + putushort(denp[1].deMDate, ndirent.de_MDate); + putushort(denp[1].deMTime, ndirent.de_MTime); + if (FAT32(pmp)) { + putushort(denp[0].deHighClust, newcluster >> 16); + putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16); } + error = bwrite(bp); + if (error) + goto bad; + /* * Now build up a directory entry pointing to the newly allocated * cluster. This will be written to an empty slot in the parent * directory. */ - ndep = &ndirent; - bzero(ndep, sizeof(*ndep)); - unix2dosfn((u_char *)ap->a_cnp->cn_nameptr, - ndep->de_Name, ap->a_cnp->cn_namelen); - TIMEVAL_TO_TIMESPEC(&time, &ts); - unix2dostime(&ts, &ndep->de_Date, &ndep->de_Time); - ndep->de_StartCluster = newcluster; - ndep->de_Attributes = ATTR_DIRECTORY; - - error = createde(ndep, pdep, &ndep); - if (error) { - clusterfree(pmp, newcluster, NULL); - } else { - *ap->a_vpp = DETOV(ndep); - } - zfree(namei_zone, ap->a_cnp->cn_pnbuf); -#ifdef MSDOSFS_DEBUG - printf("msdosfs_mkdir(): vput(%08x)\n", ap->a_dvp); +#ifdef DIAGNOSTIC + if ((cnp->cn_flags & HASBUF) == 0) + panic("msdosfs_mkdir: no name"); #endif + error = uniqdosname(pdep, cnp, ndirent.de_Name); + if (error) + goto bad; + + ndirent.de_Attributes = ATTR_DIRECTORY; + ndirent.de_StartCluster = newcluster; + ndirent.de_FileSize = 0; + ndirent.de_dev = pdep->de_dev; + ndirent.de_devvp = pdep->de_devvp; + error = createde(&ndirent, pdep, &dep, cnp); + if (error) + goto bad; + if ((cnp->cn_flags & SAVESTART) == 0) + zfree(namei_zone, cnp->cn_pnbuf); vput(ap->a_dvp); - return error; + *ap->a_vpp = DETOV(dep); + return (0); + +bad: + clusterfree(pmp, newcluster, NULL); +bad2: + zfree(namei_zone, cnp->cn_pnbuf); + vput(ap->a_dvp); + return (error); } static int @@ -1339,21 +1452,27 @@ msdosfs_rmdir(ap) struct componentname *a_cnp; } */ *ap; { - struct denode *ddep; - struct denode *dep; - int error = 0; - - ddep = VTODE(ap->a_dvp); /* parent dir of dir to delete */ - dep = VTODE(ap->a_vp);/* directory to delete */ + register struct vnode *vp = ap->a_vp; + register struct vnode *dvp = ap->a_dvp; + register struct componentname *cnp = ap->a_cnp; + register struct denode *ip, *dp; + int error; + + ip = VTODE(vp); + dp = VTODE(dvp); /* - * Be sure the directory being deleted is empty. + * Verify the directory is empty (and valid). + * (Rmdir ".." won't be valid since + * ".." will contain a reference to + * the current directory and thus be + * non-empty.) */ - if (dosdirempty(dep) == 0) { + error = 0; + if (!dosdirempty(ip) || ip->de_flag & DE_RENAME) { error = ENOTEMPTY; goto out; } - /* * Delete the entry from the directory. For dos filesystems this * gets rid of the directory entry on disk, the in memory copy @@ -1362,30 +1481,27 @@ msdosfs_rmdir(ap) * up access and eventually msdosfs_reclaim() will be called which * will remove it from the denode cache. */ - error = removede(ddep,dep); + error = removede(dp, ip); if (error) goto out; - /* * This is where we decrement the link count in the parent * directory. Since dos filesystems don't do this we just purge * the name cache and let go of the parent directory denode. */ - cache_purge(DETOV(ddep)); - vput(ap->a_dvp); - ap->a_dvp = NULL; - + cache_purge(dvp); + vput(dvp); + dvp = NULL; /* * Truncate the directory that is being deleted. */ - error = detrunc(dep, (u_long) 0, IO_SYNC, NOCRED, NULL); - cache_purge(DETOV(dep)); - -out: ; - if (ap->a_dvp) - vput(ap->a_dvp); - vput(ap->a_vp); - return error; + error = detrunc(ip, (u_long)0, IO_SYNC, cnp->cn_cred, cnp->cn_proc); + cache_purge(vp); +out: + if (dvp) + vput(dvp); + vput(vp); + return (error); } /* @@ -1402,39 +1518,11 @@ msdosfs_symlink(ap) } */ *ap; { zfree(namei_zone, ap->a_cnp->cn_pnbuf); + /* VOP_ABORTOP(ap->a_dvp, ap->a_cnp); ??? */ vput(ap->a_dvp); - return EINVAL; + return (EOPNOTSUPP); } -/* - * Dummy dirents to simulate the "." and ".." entries of the root directory - * in a dos filesystem. Dos doesn't provide these. Note that each entry - * must be the same size as a dos directory entry (32 bytes). - */ -static struct dos_dirent { - u_long d_fileno; - u_short d_reclen; - u_char d_type; - u_char d_namlen; - u_char d_name[24]; -} rootdots[2] = { - - { - 1, /* d_fileno */ - sizeof(struct direntry), /* d_reclen */ - DT_DIR, /* d_type */ - 1, /* d_namlen */ - "." /* d_name */ - }, - { - 1, /* d_fileno */ - sizeof(struct direntry), /* d_reclen */ - DT_DIR, /* d_type */ - 2, /* d_namlen */ - ".." /* d_name */ - } -}; - static int msdosfs_readdir(ap) struct vop_readdir_args /* { @@ -1448,30 +1536,30 @@ msdosfs_readdir(ap) { int error = 0; int diff; - char pushout; long n; + int blsize; long on; long lost; long count; u_long cn; u_long fileno; + u_long dirsperblk; long bias = 0; - daddr_t bn; - daddr_t lbn; + daddr_t bn, lbn; struct buf *bp; struct denode *dep = VTODE(ap->a_vp); struct msdosfsmount *pmp = dep->de_pmp; struct direntry *dentp; - struct dirent *prev; - struct dirent *crnt; - u_char dirbuf[512]; /* holds converted dos directories */ + struct dirent dirbuf; struct uio *uio = ap->a_uio; - off_t off; + u_long *cookies = NULL; int ncookies = 0; + off_t offset, off; + int chksum = -1; #ifdef MSDOSFS_DEBUG - printf("msdosfs_readdir(): vp %08x, uio %08x, cred %08x, eofflagp %08x\n", - ap->a_vp, uio, ap->a_cred, ap->a_eofflag); + printf("msdosfs_readdir(): vp %p, uio %p, cred %p, eofflagp %p\n", + ap->a_vp, uio, ap->a_cred, ap->a_eofflag); #endif /* @@ -1481,7 +1569,12 @@ msdosfs_readdir(ap) * So, fail attempts to readdir() on a plain file. */ if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) - return ENOTDIR; + return (ENOTDIR); + + /* + * To be safe, initialize dirbuf + */ + bzero(dirbuf.d_name, sizeof(dirbuf.d_name)); /* * If the user buffer is smaller than the size of one dos directory @@ -1489,13 +1582,22 @@ msdosfs_readdir(ap) * directory entry, then we fail the read. */ count = uio->uio_resid & ~(sizeof(struct direntry) - 1); - lost = uio->uio_resid - count; + offset = uio->uio_offset; if (count < sizeof(struct direntry) || - (uio->uio_offset & (sizeof(struct direntry) - 1))) - return EINVAL; + (offset & (sizeof(struct direntry) - 1))) + return (EINVAL); + lost = uio->uio_resid - count; uio->uio_resid = count; - uio->uio_iov->iov_len = count; - off = uio->uio_offset; + + if (ap->a_ncookies) { + ncookies = uio->uio_resid / 16; + MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, + M_WAITOK); + *ap->a_cookies = cookies; + *ap->a_ncookies = ncookies; + } + + dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry); /* * If they are reading from the root directory then, we simulate @@ -1504,194 +1606,184 @@ msdosfs_readdir(ap) * simulate these entries. By this I mean that at file offset 64 we * read the first entry in the root directory that lives on disk. */ - if (dep->de_StartCluster == MSDOSFSROOT) { - /* - * printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n", - * uio->uio_offset); - */ + if (dep->de_StartCluster == MSDOSFSROOT + || (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)) { +#if 0 + printf("msdosfs_readdir(): going after . or .. in root dir, offset %d\n", + offset); +#endif bias = 2 * sizeof(struct direntry); - if (uio->uio_offset < 2 * sizeof(struct direntry)) { - if (uio->uio_offset - && uio->uio_offset != sizeof(struct direntry)) { - error = EINVAL; - goto out; + if (offset < bias) { + for (n = (int)offset / sizeof(struct direntry); + n < 2; n++) { + if (FAT32(pmp)) + dirbuf.d_fileno = cntobn(pmp, + pmp->pm_rootdirblk) + * dirsperblk; + else + dirbuf.d_fileno = 1; + dirbuf.d_type = DT_DIR; + switch (n) { + case 0: + dirbuf.d_namlen = 1; + strcpy(dirbuf.d_name, "."); + break; + case 1: + dirbuf.d_namlen = 2; + strcpy(dirbuf.d_name, ".."); + break; + } + dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf); + if (uio->uio_resid < dirbuf.d_reclen) + goto out; + error = uiomove((caddr_t) &dirbuf, + dirbuf.d_reclen, uio); + if (error) + goto out; + if (cookies) { + *cookies++ = offset; + if (--ncookies <= 0) + goto out; + } + offset += sizeof(struct direntry); } - n = 1; - if (!uio->uio_offset) { - n = 2; - ncookies++; - } - ncookies++; - error = uiomove((char *) rootdots + uio->uio_offset, - n * sizeof(struct direntry), uio); } } - while (!error && uio->uio_resid > 0) { - lbn = (uio->uio_offset - bias) >> pmp->pm_cnshift; - on = (uio->uio_offset - bias) & pmp->pm_crbomask; - n = min((u_long) (pmp->pm_bpcluster - on), uio->uio_resid); - diff = dep->de_FileSize - (uio->uio_offset - bias); + + off = offset; + while (uio->uio_resid > 0) { + lbn = de_cluster(pmp, offset - bias); + on = (offset - bias) & pmp->pm_crbomask; + n = min(pmp->pm_bpcluster - on, uio->uio_resid); + diff = dep->de_FileSize - (offset - bias); if (diff <= 0) break; - if (diff < n) - n = diff; - error = pcbmap(dep, lbn, &bn, &cn); + n = min(n, diff); + error = pcbmap(dep, lbn, &bn, &cn, &blsize); if (error) break; - error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp); - n = min(n, pmp->pm_bpcluster - bp->b_resid); + error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); if (error) { brelse(bp); - return error; + return (error); } + n = min(n, blsize - bp->b_resid); /* - * code to convert from dos directory entries to ufs - * directory entries + * Convert from dos directory entries to fs-independent + * directory entries. */ - pushout = 0; - dentp = (struct direntry *)(bp->b_data + on); - prev = 0; - crnt = (struct dirent *) dirbuf; - while ((char *) dentp < bp->b_data + on + n) { - /* - * printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n", - * dentp, prev, crnt, dentp->deName[0], dentp->deAttributes); - */ - /* - * If we have an empty entry or a slot from a - * deleted file, or a volume label entry just - * concatenate its space onto the end of the - * previous entry or, manufacture an empty entry if - * there is no previous entry. - */ - if (dentp->deName[0] == SLOT_EMPTY || - dentp->deName[0] == SLOT_DELETED || - (dentp->deAttributes & ATTR_VOLUME)) { - if (prev) { - prev->d_reclen += sizeof(struct direntry); - } else { - prev = crnt; - prev->d_fileno = 0; - prev->d_reclen = sizeof(struct direntry); - prev->d_type = DT_UNKNOWN; - prev->d_namlen = 0; - prev->d_name[0] = 0; - ncookies++; - } - } else { - /* - * this computation of d_fileno must match - * the computation of va_fileid in - * msdosfs_getattr - */ - if (dentp->deAttributes & ATTR_DIRECTORY) { - /* if this is the root directory */ - fileno = getushort(dentp->deStartCluster); - if (fileno == MSDOSFSROOT) - fileno = 1; - } else { - /* - * if the file's dirent lives in - * root dir - */ - if ((fileno = cn) == MSDOSFSROOT) - fileno = 1; - fileno = (fileno << 16) | - ((dentp - (struct direntry *) bp->b_data) & 0xffff); - } - crnt->d_fileno = fileno; - crnt->d_reclen = sizeof(struct direntry); - crnt->d_type = (dentp->deAttributes & ATTR_DIRECTORY) - ? DT_DIR : DT_REG; - crnt->d_namlen = dos2unixfn(dentp->deName, - (u_char *)crnt->d_name); - /* - * printf("readdir: file %s, fileno %08x, attr %02x, start %08x\n", - * crnt->d_name, crnt->d_fileno, dentp->deAttributes, - * dentp->deStartCluster); - */ - prev = crnt; - ncookies++; - } - dentp++; - - crnt = (struct dirent *) ((char *) crnt + sizeof(struct direntry)); - pushout = 1; - - /* - * If our intermediate buffer is full then copy its - * contents to user space. I would just use the - * buffer the buf header points to but, I'm afraid - * that when we brelse() it someone else might find - * it in the cache and think its contents are - * valid. Maybe there is a way to invalidate the - * buffer before brelse()'ing it. - */ - if ((u_char *) crnt >= &dirbuf[sizeof dirbuf]) { - pushout = 0; - error = uiomove(dirbuf, sizeof(dirbuf), uio); - if (error) - break; - prev = 0; - crnt = (struct dirent *) dirbuf; - } - } - if (pushout) { - pushout = 0; - error = uiomove(dirbuf, (char *) crnt - (char *) dirbuf, - uio); - } - + for (dentp = (struct direntry *)(bp->b_data + on); + (char *)dentp < bp->b_data + on + n; + dentp++, offset += sizeof(struct direntry)) { #if 0 - /* - * If we have read everything from this block or have read - * to end of file then we are done with this block. Mark - * it to say the buffer can be reused if need be. - */ - if (n + on == pmp->pm_bpcluster || - (uio->uio_offset - bias) == dep->de_FileSize) - bp->b_flags |= B_AGE; -#endif /* if 0 */ - brelse(bp); - if (n == 0) - break; - } -out: ; - uio->uio_resid += lost; - if (!error && ap->a_ncookies != NULL) { - struct dirent* dpStart; - struct dirent* dpEnd; - struct dirent* dp; - u_long *cookies; - u_long *cookiep; + printf("rd: dentp %08x prev %08x crnt %08x deName %02x attr %02x\n", + dentp, prev, crnt, dentp->deName[0], dentp->deAttributes); +#endif + /* + * If this is an unused entry, we can stop. + */ + if (dentp->deName[0] == SLOT_EMPTY) { + brelse(bp); + goto out; + } + /* + * Skip deleted entries. + */ + if (dentp->deName[0] == SLOT_DELETED) { + chksum = -1; + continue; + } - if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1) - panic("msdosfs_readdir: unexpected uio from NFS server"); - dpStart = (struct dirent *) - (uio->uio_iov->iov_base - (uio->uio_offset - off)); - dpEnd = (struct dirent *) uio->uio_iov->iov_base; - cookies = malloc(ncookies * sizeof(*cookies), M_TEMP, M_WAITOK); - for (dp = dpStart, cookiep = cookies; - dp < dpEnd; - dp = (struct dirent *)((caddr_t) dp + dp->d_reclen)) { - off += dp->d_reclen; - *cookiep++ = (u_long) off; + /* + * Handle Win95 long directory entries + */ + if (dentp->deAttributes == ATTR_WIN95) { + if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) + continue; + chksum = win2unixfn((struct winentry *)dentp, &dirbuf, chksum); + continue; + } + + /* + * Skip volume labels + */ + if (dentp->deAttributes & ATTR_VOLUME) { + chksum = -1; + continue; + } + /* + * This computation of d_fileno must match + * the computation of va_fileid in + * msdosfs_getattr. + */ + if (dentp->deAttributes & ATTR_DIRECTORY) { + fileno = getushort(dentp->deStartCluster); + if (FAT32(pmp)) + fileno |= getushort(dentp->deHighClust) << 16; + /* if this is the root directory */ + if (fileno == MSDOSFSROOT) + if (FAT32(pmp)) + fileno = cntobn(pmp, + pmp->pm_rootdirblk) + * dirsperblk; + else + fileno = 1; + else + fileno = cntobn(pmp, fileno) * dirsperblk; + dirbuf.d_fileno = fileno; + dirbuf.d_type = DT_DIR; + } else { + dirbuf.d_fileno = offset / sizeof(struct direntry); + dirbuf.d_type = DT_REG; + } + if (chksum != winChksum(dentp->deName)) + dirbuf.d_namlen = dos2unixfn(dentp->deName, + (u_char *)dirbuf.d_name, + pmp->pm_flags & MSDOSFSMNT_SHORTNAME); + else + dirbuf.d_name[dirbuf.d_namlen] = 0; + chksum = -1; + dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf); + if (uio->uio_resid < dirbuf.d_reclen) { + brelse(bp); + goto out; + } + error = uiomove((caddr_t) &dirbuf, + dirbuf.d_reclen, uio); + if (error) { + brelse(bp); + goto out; + } + if (cookies) { + *cookies++ = off; + off = offset + sizeof(struct direntry); + if (--ncookies <= 0) { + brelse(bp); + goto out; + } + } } - *ap->a_ncookies = ncookies; - *ap->a_cookies = cookies; + brelse(bp); } +out: + /* Subtract unused cookies */ + if (ap->a_ncookies) + *ap->a_ncookies -= ncookies; + + uio->uio_offset = offset; + uio->uio_resid += lost; /* * Set the eofflag (NFS uses it) */ if (ap->a_eofflag) - if (dep->de_FileSize - (uio->uio_offset - bias) <= 0) + if (dep->de_FileSize - (offset - bias) <= 0) *ap->a_eofflag = 1; else *ap->a_eofflag = 0; - return error; + return (error); } static int @@ -1703,7 +1795,7 @@ msdosfs_abortop(ap) { if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) zfree(namei_zone, ap->a_cnp->cn_pnbuf); - return 0; + return (0); } /* @@ -1725,11 +1817,12 @@ msdosfs_bmap(ap) } */ *ap; { struct denode *dep = VTODE(ap->a_vp); + struct msdosfsmount *pmp = dep->de_pmp; if (ap->a_vpp != NULL) *ap->a_vpp = dep->de_devvp; if (ap->a_bnp == NULL) - return 0; + return (0); if (ap->a_runp) { /* * Sequential clusters should be counted here. @@ -1739,7 +1832,7 @@ msdosfs_bmap(ap) if (ap->a_runb) { *ap->a_runb = 0; } - return pcbmap(dep, ap->a_bn, ap->a_bnp, 0); + return (pcbmap(dep, de_bn2cn(pmp, ap->a_bn), ap->a_bnp, 0, 0)); } static int @@ -1762,18 +1855,21 @@ msdosfs_strategy(ap) * don't allow files with holes, so we shouldn't ever see this. */ if (bp->b_blkno == bp->b_lblkno) { - error = pcbmap(dep, bp->b_lblkno, &bp->b_blkno, 0); - if (error) - bp->b_blkno = -1; - if (bp->b_blkno == -1) - clrbuf(bp); + error = pcbmap(dep, de_bn2cn(dep->de_pmp, bp->b_lblkno), + &bp->b_blkno, 0, 0); + if (error) { + bp->b_error = error; + bp->b_flags |= B_ERROR; + biodone(bp); + return (error); + } + if ((long)bp->b_blkno == -1) + vfs_bio_clrbuf(bp); } if (bp->b_blkno == -1) { biodone(bp); - return error; + return (0); } -#ifdef DIAGNOSTIC -#endif /* * Read/write the block from/to the disk that contains the desired * file block. @@ -1781,7 +1877,7 @@ msdosfs_strategy(ap) vp = dep->de_devvp; bp->b_dev = vp->v_rdev; VOCALL(vp->v_op, VOFFSET(vop_strategy), ap); - return 0; + return (0); } static int @@ -1798,7 +1894,7 @@ msdosfs_print(ap) printf(" dev %d, %d", major(dep->de_dev), minor(dep->de_dev)); lockmgr_printinfo(&dep->de_lock); printf("\n"); - return 0; + return (0); } static int @@ -1809,25 +1905,28 @@ msdosfs_pathconf(ap) int *a_retval; } */ *ap; { + struct msdosfsmount *pmp = VTODE(ap->a_vp)->de_pmp; + switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = 1; - return 0; + return (0); case _PC_NAME_MAX: - *ap->a_retval = 12; - return 0; + *ap->a_retval = pmp->pm_flags & MSDOSFSMNT_LONGNAME ? WIN_MAXLEN : 12; + return (0); case _PC_PATH_MAX: - *ap->a_retval = PATH_MAX; /* 255? */ - return 0; + *ap->a_retval = PATH_MAX; + return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; - return 0; + return (0); case _PC_NO_TRUNC: *ap->a_retval = 0; - return 0; + return (0); default: - return EINVAL; + return (EINVAL); } + /* NOTREACHED */ } /* Global vfs data structures for msdosfs */ diff --git a/sys/msdosfs/msdosfsmount.h b/sys/msdosfs/msdosfsmount.h index 49a93279a11f..3a0ee6e047a3 100644 --- a/sys/msdosfs/msdosfsmount.h +++ b/sys/msdosfs/msdosfsmount.h @@ -1,9 +1,9 @@ -/* $Id: msdosfsmount.h,v 1.11 1997/03/03 17:36:11 bde Exp $ */ -/* $NetBSD: msdosfsmount.h,v 1.7 1994/08/21 18:44:17 ws Exp $ */ +/* $Id: msdosfsmount.h,v 1.12 1997/10/12 20:25:02 phk Exp $ */ +/* $NetBSD: msdosfsmount.h,v 1.17 1997/11/17 15:37:07 ws Exp $ */ /*- - * Copyright (C) 1994 Wolfgang Solfrank. - * Copyright (C) 1994 TooLs GmbH. + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * @@ -63,90 +63,119 @@ MALLOC_DECLARE(M_MSDOSFSMNT); 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_FATsecs; /* actual number of fat sectors */ u_long pm_fatblk; /* block # of first FAT */ - u_long pm_rootdirblk; /* block # of root directory */ + u_long pm_rootdirblk; /* block # (cluster # for FAT32) of root directory number */ 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_bnshift; /* shift file offset right this amount to get a block number */ 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_long pm_fatmask; /* mask to use for fat numbers */ + u_long pm_fsinfo; /* fsinfo block number */ + u_long pm_nxtfree; /* next free cluster in fsinfo block */ + u_int pm_fatmult; /* these 2 values are used in fat */ + u_int pm_fatdiv; /* offset computation */ + u_int pm_curfat; /* current fat for FAT32 (0 otherwise) */ 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 complete, when 0 use bdwrite, else use bwrite */ + u_int pm_flags; /* see below */ struct netexport pm_export; /* export information */ }; +/* Byte offset in FAT on filesystem pmp, cluster cn */ +#define FATOFS(pmp, cn) ((cn) * (pmp)->pm_fatmult / (pmp)->pm_fatdiv) + + +#define VFSTOMSDOSFS(mp) ((struct msdosfsmount *)mp->mnt_data) /* 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 +/* + * Convert pointer to buffer -> pointer to direntry + */ +#define bptoep(pmp, bp, dirofs) \ + ((struct direntry *)(((bp)->b_data) \ + + ((dirofs) & (pmp)->pm_crbomask))) + +/* + * Convert block number to cluster number + */ +#define de_bn2cn(pmp, bn) \ + ((bn) >> ((pmp)->pm_cnshift - (pmp)->pm_bnshift)) + +/* + * Convert cluster number to block number + */ +#define de_cn2bn(pmp, cn) \ + ((cn) << ((pmp)->pm_cnshift - (pmp)->pm_bnshift)) + +/* + * Convert file offset to cluster number + */ +#define de_cluster(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) + +/* + * Convert file offset to block number + */ +#define de_blk(pmp, off) \ + (de_cn2bn(pmp, de_cluster((pmp), (off)))) + +/* + * Convert cluster number to file offset + */ +#define de_cn2off(pmp, cn) \ + ((cn) << (pmp)->pm_cnshift) + +/* + * Convert block number to file offset + */ +#define de_bn2off(pmp, bn) \ + ((bn) << (pmp)->pm_bnshift) /* * 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) + (de_cn2bn((pmp), (cn)-CLUST_FIRST) + (pmp)->pm_firstcluster) /* * 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) + (de_blk((pmp), (dirofs)) + (pmp)->pm_rootdirblk) /* * Calculate block number for directory entry at cluster dirclu, offset @@ -157,27 +186,8 @@ struct msdosfsmount { ? 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) - int msdosfs_init __P((struct vfsconf *vfsp)); +int msdosfs_mountroot __P((void)); #endif /* KERNEL */ @@ -190,6 +200,27 @@ struct msdosfs_args { 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 */ + int flags; /* see below */ + int magic; /* version number */ }; +/* + * Msdosfs mount options: + */ +#define MSDOSFSMNT_SHORTNAME 1 /* Force old DOS short names only */ +#define MSDOSFSMNT_LONGNAME 2 /* Force Win'95 long names */ +#define MSDOSFSMNT_NOWIN95 4 /* Completely ignore Win95 entries */ +#ifndef __FreeBSD__ +#define MSDOSFSMNT_GEMDOSFS 8 /* This is a gemdos-flavour */ +#endif +/* All flags above: */ +#define MSDOSFSMNT_MNTOPT \ + (MSDOSFSMNT_SHORTNAME|MSDOSFSMNT_LONGNAME|MSDOSFSMNT_NOWIN95 \ + /*|MSDOSFSMNT_GEMDOSFS*/) +#define MSDOSFSMNT_RONLY 0x80000000 /* mounted read-only */ +#define MSDOSFSMNT_WAITONFAT 0x40000000 /* mounted synchronous */ +#define MSDOSFS_FATMIRROR 0x20000000 /* FAT is mirrored */ + +#define MSDOSFS_ARGSMAGIC 0xe4eff300 + #endif /* !_MSDOSFS_MSDOSFSMOUNT_H_ */