summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorfrankho <none@none>2006-09-13 07:42:50 -0700
committerfrankho <none@none>2006-09-13 07:42:50 -0700
commit264a6e7478846334593be7663fb6b1a8f37784a0 (patch)
tree7e45b35b1f50c9b20a6ecb1380bf0b7f17764040 /usr/src
parentb2d230eb0cff8b999f195cc609b91fc61bf31c31 (diff)
downloadillumos-joyent-264a6e7478846334593be7663fb6b1a8f37784a0.tar.gz
PSARC 2005/361 PCFS timestamp handling cleanup
4401668 RFE: pcfs should support force unmount 6248624 pcfs file timestamp handling needs cleanup 6278733 pcfs statvfs implementation is slow 6351785 pcfs should support cluster sizes over 32Kb for Fat32 despite violating the spec 6364444 pcfs can still crash in pc_getcluster with one-off overflow
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/fs.d/pcfs/fsck/Makefile28
-rw-r--r--usr/src/cmd/fs.d/pcfs/fsck/dir.c88
-rw-r--r--usr/src/cmd/fs.d/pcfs/mount/mount.c8
-rw-r--r--usr/src/uts/common/fs/pcfs/pc_alloc.c23
-rw-r--r--usr/src/uts/common/fs/pcfs/pc_dir.c4
-rw-r--r--usr/src/uts/common/fs/pcfs/pc_node.c59
-rw-r--r--usr/src/uts/common/fs/pcfs/pc_subr.c542
-rw-r--r--usr/src/uts/common/fs/pcfs/pc_vfsops.c138
-rw-r--r--usr/src/uts/common/fs/pcfs/pc_vnops.c150
-rw-r--r--usr/src/uts/common/sys/fs/pc_dir.h30
-rw-r--r--usr/src/uts/common/sys/fs/pc_fs.h28
11 files changed, 576 insertions, 522 deletions
diff --git a/usr/src/cmd/fs.d/pcfs/fsck/Makefile b/usr/src/cmd/fs.d/pcfs/fsck/Makefile
index 6d050fdc9a..deecc57ceb 100644
--- a/usr/src/cmd/fs.d/pcfs/fsck/Makefile
+++ b/usr/src/cmd/fs.d/pcfs/fsck/Makefile
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -22,8 +21,8 @@
#
#ident "%Z%%M% %I% %E% SMI"
#
-# Copyright (c) 1999,2001 by Sun Microsystems, Inc.
-# All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
#
# cmd/fs.d/pcfs/fsck/Makefile
@@ -42,18 +41,8 @@ FSCKSRCS= $(FSCKOBJS:%.o=%.c)
#DEBUGOBJS= inject.o
#DEBUGSRCS= $(DEBUGOBJS:%.o=%.c)
-#
-# We grab one file from the kernel. It has a relatively complex routine
-# for manipulating time values into the format expected within directory
-# entries in the FAT file system [pc_tvtopct()]. It seemed better not to
-# duplicate this code.
-#
-PCFSDIR= ../../../../uts/common/fs/pcfs
-PCFSOBJS= pc_subr.o
-PCFSSRCS= $(PCFSOBJS:%.o=%.c)
-
-OBJS= $(FSCKOBJS) $(PCFSOBJS) $(DEBUGOBJS)
-SRCS= $(FSCKSRCS) $(PCFSDIR)/$(PCFSSRCS) $(DEBUGSRCS)
+OBJS= $(FSCKOBJS) $(DEBUGOBJS)
+SRCS= $(FSCKSRCS) $(DEBUGSRCS)
LSRCS= $(FSCKSRCS) $(DEBUGSRCS)
# for messaging catalog
@@ -79,13 +68,10 @@ $(LIBPROG): $(OBJS)
$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
$(POST_PROCESS)
-%.o: $(PCFSDIR)/%.c
- $(COMPILE.c) -D_KERNEL $<
-
lint_LSRCS:
$(LINT.c) $(LSRCS) $(LDLIBS)
lint: lint_LSRCS
clean:
- $(RM) $(FSCKOBJS) $(PCFSOBJS) $(DEBUGOBJS)
+ $(RM) $(FSCKOBJS) $(DEBUGOBJS)
diff --git a/usr/src/cmd/fs.d/pcfs/fsck/dir.c b/usr/src/cmd/fs.d/pcfs/fsck/dir.c
index 40c6ff8331..022c9350c7 100644
--- a/usr/src/cmd/fs.d/pcfs/fsck/dir.c
+++ b/usr/src/cmd/fs.d/pcfs/fsck/dir.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,8 +19,8 @@
* CDDL HEADER END
*/
/*
- * Copyright (c) 1999,2000 by Sun Microsystems, Inc.
- * All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
@@ -35,8 +34,10 @@
#include <stdlib.h>
#include <libintl.h>
#include <ctype.h>
+#include <time.h>
#include <sys/param.h>
#include <sys/time.h>
+#include <sys/byteorder.h>
#include <sys/dktp/fdisk.h>
#include <sys/fs/pc_fs.h>
#include <sys/fs/pc_dir.h>
@@ -67,12 +68,6 @@ int RootDirModified;
int OkayToRelink = 1;
/*
- * We import these routines from the pc_subr.c file in the kernel.
- */
-extern void pc_tvtopct(timestruc_t *, struct pctime *);
-extern int pc_validchar(char);
-
-/*
* We have a bunch of routines for handling CHK names. A CHK name is
* simply a file name of the form "FILEnnnn.CHK", where the n's are the
* digits in the numbers from 1 to 9999. There are always four digits
@@ -822,40 +817,66 @@ insertDirEnt(struct pcdir *slot, struct pcdir *entry, int32_t clusterWithSlot)
markClusterModified(clusterWithSlot);
}
+/*
+ * Convert current UNIX time into a PCFS timestamp (which is in local time).
+ *
+ * Since the "seconds" field of that is only accurate to 2sec precision,
+ * we allow for the optional (used only for creation times on FAT) "msec"
+ * parameter that takes the fractional part.
+ */
static void
-getNow(timestruc_t *ts)
+getNow(struct pctime *pctp, uchar_t *msec)
{
- struct timeval tv;
+ time_t now;
+ struct tm tm;
+ ushort_t tim, dat;
- if (gettimeofday(&tv, NULL) == 0) {
- ts->tv_sec = tv.tv_sec;
- ts->tv_nsec = tv.tv_usec * 1000;
- } else {
- /* Failed to get time, set create time to the Solaris epoch */
- ts->tv_sec = 0;
- ts->tv_nsec = 0;
- }
+ /*
+ * Disable daylight savings corrections - Solaris PCFS doesn't
+ * support such conversions yet. Save timestamps in local time.
+ */
+ daylight = 0;
+
+ (void) time(&now);
+ (void) localtime_r(&now, &tm);
+
+ dat = (tm.tm_year - 80) << YEARSHIFT;
+ dat |= tm.tm_mon << MONSHIFT;
+ dat |= tm.tm_mday << DAYSHIFT;
+ tim = tm.tm_hour << HOURSHIFT;
+ tim |= tm.tm_min << MINSHIFT;
+ tim |= (tm.tm_sec / 2) << SECSHIFT;
+
+ /*
+ * Sanity check. If we overflow the PCFS timestamp range
+ * we set the time to 01/01/1980, 00:00:00
+ */
+ if (dat < 80 || dat > 227)
+ dat = tim = 0;
+
+ pctp->pct_date = LE_16(dat);
+ pctp->pct_time = LE_16(tim);
+ if (msec)
+ *msec = (tm.tm_sec & 1) ? 100 : 0;
}
/*
* FAT file systems store the following time information in a directory
* entry:
- * creation time
- * creation date
- * last access date
- * last modify time
- * last modify date
+ * timestamp member of "struct pcdir"
+ * ======================================================================
+ * creation time pcd_crtime.pct_time
+ * creation date pcd_crtime.pct_date
+ * last access date pcd_ladate
+ * last modify time pcd_mtime.pct_time
+ * last modify date pcd_mtime.pct_date
*
* No access time is kept.
*/
static void
updateDirEnt_CreatTime(struct pcdir *dp)
{
- timestruc_t ts;
-
- getNow(&ts);
- pc_tvtopct(&ts, &(dp->pcd_crtime)); /* sets creation time/date */
- dp->pcd_crtime_msec = 0;
+ getNow(&dp->pcd_crtime, &dp->pcd_crtime_msec);
markClusterModified(findImpactedCluster(dp));
}
@@ -864,9 +885,8 @@ updateDirEnt_ModTimes(struct pcdir *dp)
{
timestruc_t ts;
- getNow(&ts);
- pc_tvtopct(&ts, &(dp->pcd_mtime)); /* sets modification time/date */
- dp->pcd_ladate = dp->pcd_mtime.pct_date; /* sets access date */
+ getNow(&dp->pcd_mtime, NULL);
+ dp->pcd_ladate = dp->pcd_mtime.pct_date;
dp->pcd_attr |= PCA_ARCH;
markClusterModified(findImpactedCluster(dp));
}
diff --git a/usr/src/cmd/fs.d/pcfs/mount/mount.c b/usr/src/cmd/fs.d/pcfs/mount/mount.c
index 1e058260c0..e430a7354b 100644
--- a/usr/src/cmd/fs.d/pcfs/mount/mount.c
+++ b/usr/src/cmd/fs.d/pcfs/mount/mount.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -22,7 +21,7 @@
#pragma ident "%Z%%M% %I% %E% SMI"
/*
- * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -131,6 +130,7 @@ main(int argc, char *argv[])
"special mount_point\n"), typename);
(void) fprintf(stderr, gettext(
"\tpcfs-specific suboptions are:\n"
+ "\t clamptime,noclamptime\n"
"\t foldcase,nofoldcase\n"));
exit(32);
}
diff --git a/usr/src/uts/common/fs/pcfs/pc_alloc.c b/usr/src/uts/common/fs/pcfs/pc_alloc.c
index 2dc7705e3b..3a47153248 100644
--- a/usr/src/uts/common/fs/pcfs/pc_alloc.c
+++ b/usr/src/uts/common/fs/pcfs/pc_alloc.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -331,18 +330,26 @@ int
pc_freeclusters(struct pcfs *fsp)
{
pc_cluster32_t cn;
- int free;
+ int free = 0;
+
+ if (IS_FAT32(fsp) &&
+ fsp->fsinfo_native.fs_free_clusters != FSINFO_UNKNOWN)
+ return (fsp->fsinfo_native.fs_free_clusters);
/*
* make sure the FAT is in core
*/
- free = 0;
for (cn = PCF_FIRSTCLUSTER;
- (int)cn <= fsp->pcfs_ncluster; cn++) {
+ (int)cn < fsp->pcfs_ncluster; cn++) {
if (pc_getcluster(fsp, cn) == PCF_FREECLUSTER) {
free++;
}
}
+
+ if (IS_FAT32(fsp)) {
+ ASSERT(fsp->fsinfo_native.fs_free_clusters == FSINFO_UNKNOWN);
+ fsp->fsinfo_native.fs_free_clusters = free;
+ }
return (free);
}
@@ -446,7 +453,7 @@ pc_alloccluster(
panic("pc_addcluster: no FAT");
for (cn = fsp->pcfs_nxfrecls;
- (int)cn <= fsp->pcfs_ncluster; cn++) {
+ (int)cn < fsp->pcfs_ncluster; cn++) {
if (pc_getcluster(fsp, cn) == PCF_FREECLUSTER) {
struct buf *bp;
diff --git a/usr/src/uts/common/fs/pcfs/pc_dir.c b/usr/src/uts/common/fs/pcfs/pc_dir.c
index d058c76b25..f861c9b008 100644
--- a/usr/src/uts/common/fs/pcfs/pc_dir.c
+++ b/usr/src/uts/common/fs/pcfs/pc_dir.c
@@ -285,7 +285,9 @@ pc_makedirentry(struct pcnode *dp, struct pcdir *direntries,
ep = &direntries[ndirentries - 1];
gethrestime(&now);
- pc_tvtopct(&now, &ep->pcd_mtime);
+ if (error = pc_tvtopct(&now, &ep->pcd_mtime))
+ return (error);
+
ep->pcd_crtime = ep->pcd_mtime;
ep->pcd_ladate = ep->pcd_mtime.pct_date;
ep->pcd_crtime_msec = 0;
diff --git a/usr/src/uts/common/fs/pcfs/pc_node.c b/usr/src/uts/common/fs/pcfs/pc_node.c
index 6c466d46dd..d82788f957 100644
--- a/usr/src/uts/common/fs/pcfs/pc_node.c
+++ b/usr/src/uts/common/fs/pcfs/pc_node.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -61,7 +60,7 @@ static int syncpcp(struct pcnode *, int);
* fake entry for root directory, since this does not have a parent
* pointing to it.
*/
-static struct pcdir rootentry = {
+struct pcdir pcfs_rootdirentry = {
"",
"",
PCA_DIR
@@ -96,7 +95,7 @@ pc_getnode(
ASSERT(fsp->pcfs_flags & PCFS_LOCKED);
if (ep == (struct pcdir *)0) {
- ep = &rootentry;
+ ep = &pcfs_rootdirentry;
scluster = 0;
} else {
scluster = pc_getstartcluster(fsp, ep);
@@ -176,6 +175,7 @@ pc_getnode(
pcp->pc_size = ltohi(ep->pcd_size);
}
fsp->pcfs_nrefs++;
+ VFS_HOLD(PCFSTOVFS(fsp));
vp->v_data = (caddr_t)pcp;
vp->v_vfsp = PCFSTOVFS(fsp);
vn_exists(vp);
@@ -262,6 +262,7 @@ retry:
fsp->pcfs_frefs--;
}
fsp->pcfs_nrefs--;
+ VFS_RELE(vp->v_vfsp);
if (fsp->pcfs_nrefs < 0) {
panic("pc_rele: nrefs count");
@@ -286,7 +287,10 @@ pc_mark_mod(struct pcnode *pcp)
if (PCTOV(pcp)->v_type == VREG) {
gethrestime(&now);
- pc_tvtopct(&now, &pcp->pc_entry.pcd_mtime);
+ if (pc_tvtopct(&now, &pcp->pc_entry.pcd_mtime))
+ PC_DPRINTF1(2, "pc_mark_mod failed timestamp "
+ "conversion, curtime = %lld\n",
+ (long long)now.tv_sec);
pcp->pc_flags |= PC_CHG;
}
}
@@ -297,12 +301,15 @@ pc_mark_mod(struct pcnode *pcp)
void
pc_mark_acc(struct pcnode *pcp)
{
- struct pctime pt;
+ struct pctime pt = { 0, 0 };
timestruc_t now;
if (PCTOV(pcp)->v_type == VREG) {
gethrestime(&now);
- pc_tvtopct(&now, &pt);
+ if (pc_tvtopct(&now, &pcp->pc_entry.pcd_mtime))
+ PC_DPRINTF1(2, "pc_mark_acc failed timestamp "
+ "conversion, curtime = %lld\n",
+ (long long)now.tv_sec);
pcp->pc_entry.pcd_ladate = pt.pct_date;
pcp->pc_flags |= PC_CHG;
}
@@ -619,10 +626,11 @@ pc_mark_irrecov(struct pcfs *fsp)
void
pc_diskchanged(struct pcfs *fsp)
{
- struct pcnode *pcp, *npcp = NULL;
- struct pchead *hp;
- struct vnode *vp;
- extern vfs_t EIO_vfs;
+ struct pcnode *pcp, *npcp = NULL;
+ struct pchead *hp;
+ struct vnode *vp;
+ extern vfs_t EIO_vfs;
+ struct vfs *vfsp;
/*
* Eliminate all pcnodes (dir & file) associated with this fs.
@@ -633,12 +641,14 @@ pc_diskchanged(struct pcfs *fsp)
*/
PC_DPRINTF1(1, "pc_diskchanged fsp=0x%p\n", (void *)fsp);
+ vfsp = PCFSTOVFS(fsp);
+
for (hp = pcdhead; hp < &pcdhead[NPCHASH]; hp++) {
for (pcp = hp->pch_forw;
pcp != (struct pcnode *)hp; pcp = npcp) {
npcp = pcp -> pc_forw;
vp = PCTOV(pcp);
- if (VFSTOPCFS(vp->v_vfsp) == fsp &&
+ if ((vp->v_vfsp == vfsp) &&
!(pcp->pc_flags & PC_RELEHOLD)) {
mutex_enter(&(vp)->v_lock);
if (vp->v_count > 0) {
@@ -652,10 +662,16 @@ pc_diskchanged(struct pcfs *fsp)
vp->v_vfsp = &EIO_vfs;
vp->v_type = VBAD;
VN_RELE(vp);
- if (!(pcp->pc_flags & PC_EXTERNAL))
+ if (!(pcp->pc_flags & PC_EXTERNAL)) {
+ (void) pvn_vplist_dirty(vp,
+ (u_offset_t)0, pcfs_putapage,
+ B_INVAL | B_TRUNC,
+ (struct cred *)NULL);
vn_free(vp);
+ }
kmem_free(pcp, sizeof (struct pcnode));
fsp->pcfs_nrefs --;
+ VFS_RELE(vfsp);
}
}
}
@@ -664,7 +680,7 @@ pc_diskchanged(struct pcfs *fsp)
pcp != (struct pcnode *)hp; pcp = npcp) {
npcp = pcp -> pc_forw;
vp = PCTOV(pcp);
- if (VFSTOPCFS(vp->v_vfsp) == fsp &&
+ if ((vp->v_vfsp == vfsp) &&
!(pcp->pc_flags & PC_RELEHOLD)) {
mutex_enter(&(vp)->v_lock);
if (vp->v_count > 0) {
@@ -678,11 +694,17 @@ pc_diskchanged(struct pcfs *fsp)
vp->v_vfsp = &EIO_vfs;
vp->v_type = VBAD;
VN_RELE(vp);
- if (!(pcp->pc_flags & PC_EXTERNAL))
+ if (!(pcp->pc_flags & PC_EXTERNAL)) {
+ (void) pvn_vplist_dirty(vp,
+ (u_offset_t)0, pcfs_putapage,
+ B_INVAL | B_TRUNC,
+ (struct cred *)NULL);
vn_free(vp);
+ }
kmem_free(pcp, sizeof (struct pcnode));
fsp->pcfs_frefs --;
fsp->pcfs_nrefs --;
+ VFS_RELE(vfsp);
}
}
}
@@ -696,7 +718,8 @@ pc_diskchanged(struct pcfs *fsp)
panic("pc_diskchanged: nrefs");
}
#endif
- if (fsp->pcfs_fatp != (uchar_t *)0) {
+ if (!(vfsp->vfs_flag & VFS_UNMOUNTED) &&
+ fsp->pcfs_fatp != (uchar_t *)0) {
pc_invalfat(fsp);
} else {
binval(fsp->pcfs_xdev);
diff --git a/usr/src/uts/common/fs/pcfs/pc_subr.c b/usr/src/uts/common/fs/pcfs/pc_subr.c
index 9689fe837c..31509c1d5f 100644
--- a/usr/src/uts/common/fs/pcfs/pc_subr.c
+++ b/usr/src/uts/common/fs/pcfs/pc_subr.c
@@ -1,6 +1,26 @@
/*
- * Copyright (c) 1989, 1998 by Sun Microsystems, Inc.
- * All rights reserved.
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
*/
/*
@@ -17,410 +37,214 @@
#include <sys/param.h>
#include <sys/time.h>
-#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/sysmacros.h>
-#include <sys/kmem.h>
#include <sys/vfs.h>
#include <sys/debug.h>
#include <sys/errno.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/byteorder.h>
#include <sys/fs/pc_fs.h>
#include <sys/fs/pc_label.h>
#include <sys/fs/pc_dir.h>
#include <sys/fs/pc_node.h>
/*
- * Structure returned by gmtime and localtime calls (see ctime(3)).
+ * Convert time between DOS formats:
+ * - years since 1980
+ * - months/days/hours/minutes/seconds, local TZ
+ * and the UNIX format (seconds since 01/01/1970, 00:00:00 UT).
+ *
+ * Timezones are adjusted for via mount option arg (secondswest),
+ * but daylight savings time corrections are not made. Calculated
+ * time may therefore end up being wrong by an hour, but this:
+ * a) will happen as well if media is interchanged between
+ * two DOS/Windows-based systems that use different
+ * timezone settings
+ * b) is the best option we have unless we decide to put
+ * a full ctime(3C) framework into the kernel, including
+ * all conversion tables - AND keeping them current ...
*/
-struct tm {
- short tm_sec;
- short tm_min;
- short tm_hour;
- short tm_mday;
- short tm_mon;
- short tm_year;
- short tm_wday;
- short tm_yday;
- short tm_isdst;
-};
-
-void pc_tvtopct(timestruc_t *, struct pctime *);
-void pc_pcttotv(struct pctime *, timestruc_t *);
-int pc_validchar(char);
-
-static struct tm *localtime(time_t *tim);
-static int sunday(struct tm *, int);
-static int dysize(int);
-static struct tm *gmtime(int);
-static time_t ctime(struct tm *);
-
-/* The cm struct defines tm_year relative to 1900 */
-#define YEAR_ZERO 1900
-/*
- * convert timestruct to pctime
- */
-void
-pc_tvtopct(
- timestruc_t *tvp, /* time input */
- struct pctime *pctp) /* pctime output */
-{
- struct tm *ctp;
-
- ctp = localtime(&tvp->tv_sec);
-#define setfield(S, FIELD, SFT, MSK) \
- S = (ltohs(S) & ~(MSK << SFT)) | (((FIELD) & MSK) << SFT); S = htols(S);
-
- setfield(pctp->pct_time, ctp->tm_sec / 2, SECSHIFT, SECMASK);
- setfield(pctp->pct_time, ctp->tm_min, MINSHIFT, MINMASK);
- setfield(pctp->pct_time, ctp->tm_hour, HOURSHIFT, HOURMASK);
- setfield(pctp->pct_date, ctp->tm_mday, DAYSHIFT, DAYMASK);
- setfield(pctp->pct_date, ctp->tm_mon + 1, MONSHIFT, MONMASK);
- setfield(pctp->pct_date, ctp->tm_year - 80, YEARSHIFT, YEARMASK);
-#undef setfield
-}
+int pc_tvtopct(timestruc_t *, struct pctime *);
+void pc_pcttotv(struct pctime *, int64_t *);
/*
- * convert pctime to timeval
+ * Macros/Definitons required to convert between DOS-style and
+ * UNIX-style time recording.
+ * DOS year zero is 1980.
*/
-void
-pc_pcttotv(
- struct pctime *pctp, /* ptime input */
- timestruc_t *tvp) /* tinmeval output */
+static int daysinmonth[] =
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+#define YEAR_ZERO 1980
+#define YZ_SECS (((8 * 365) + (2 * 366)) * 86400)
+#define FAT_ENDOFTIME \
+ LE_16(23 << HOURSHIFT | 59 << MINSHIFT | (59/2) << SECSHIFT)
+#define FAT_ENDOFDATE \
+ LE_16(127 << YEARSHIFT | 12 << MONSHIFT | 31 << DAYSHIFT)
+#define leap_year(y) \
+ (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+static int
+days_in_year(int y)
{
- struct tm tm;
-
-#define getfield(S, SFT, M) (((int)(ltohs(S)) >> SFT) & M)
- tm.tm_sec = getfield(pctp->pct_time, SECSHIFT, SECMASK) * 2;
- tm.tm_min = getfield(pctp->pct_time, MINSHIFT, MINMASK);
- tm.tm_hour = getfield(pctp->pct_time, HOURSHIFT, HOURMASK);
- tm.tm_mday = getfield(pctp->pct_date, DAYSHIFT, DAYMASK);
- tm.tm_mon = getfield(pctp->pct_date, MONSHIFT, MONMASK) - 1;
- tm.tm_year = 80 + getfield(pctp->pct_date, YEARSHIFT, YEARMASK);
-#undef getfield
- tvp->tv_nsec = 0;
- tvp->tv_sec = ctime(&tm);
+ return (leap_year((y)) ? 366 : 365);
}
-/*
- * This routine converts time as follows.
- * The epoch is 0000 Jan 1 1970 GMT.
- * The argument time is in seconds since then.
- * The localtime(t) entry returns a pointer to an array
- * containing
- * seconds (0-59)
- * minutes (0-59)
- * hours (0-23)
- * day of month (1-31)
- * month (0-11)
- * year-1900
- * weekday (0-6, Sun is 0)
- * day of the year
- * daylight savings flag
- *
- * The routine calls the system to determine the local
- * timezone and whether Daylight Saving Time is permitted locally.
- * (DST is then determined by the current local rules)
- *
- * The routine does not work
- * in Saudi Arabia which runs on Solar time.
- *
- */
-
-static int dmsize[12] =
+static int
+days_in_month(int m, int y)
{
- 31,
- 28,
- 31,
- 30,
- 31,
- 30,
- 31,
- 31,
- 30,
- 31,
- 30,
- 31
-};
+ if (m == 2 && leap_year(y))
+ return (29);
+ else
+ return (daysinmonth[m-1]);
+}
-/*
- * The following table is used for 1974 and 1975 and
- * gives the day number of the first day after the Sunday of the
- * change.
- */
-struct dstab {
- int dayyr;
- int daylb;
- int dayle;
-};
-
-static struct dstab usdaytab[] = {
- 1974, 5, 333, /* 1974: Jan 6 - last Sun. in Nov */
- 1975, 58, 303, /* 1975: Last Sun. in Feb - last Sun in Oct */
- 0, 119, 303, /* all other years: end Apr - end Oct */
-};
-static struct dstab ausdaytab[] = {
- 1970, 400, 0, /* 1970: no daylight saving at all */
- 1971, 303, 0, /* 1971: daylight saving from Oct 31 */
- 1972, 303, 58, /* 1972: Jan 1 -> Feb 27 & Oct 31 -> dec 31 */
- 0, 303, 65, /* others: -> Mar 7, Oct 31 -> */
-};
+struct pcfs_args pc_tz; /* this is set by pcfs_mount */
/*
- * The European tables ... based on hearsay
- * Believed correct for:
- * WE: Great Britain, Ireland, Portugal
- * ME: Belgium, Luxembourg, Netherlands, Denmark, Norway,
- * Austria, Poland, Czechoslovakia, Sweden, Switzerland,
- * DDR, DBR, France, Spain, Hungary, Italy, Jugoslavia
- * Eastern European dst is unknown, we'll make it ME until someone speaks up.
- * EE: Bulgaria, Finland, Greece, Rumania, Turkey, Western Russia
+ * Convert time from UNIX to DOS format.
+ * Return EOVERFLOW in case no valid DOS time representation
+ * exists for the given UNIX time.
*/
-static struct dstab wedaytab[] = {
- 1983, 86, 303, /* 1983: end March - end Oct */
- 1984, 86, 303, /* 1984: end March - end Oct */
- 1985, 86, 303, /* 1985: end March - end Oct */
- 0, 400, 0, /* others: no daylight saving at all ??? */
-};
-
-static struct dstab medaytab[] = {
- 1983, 86, 272, /* 1983: end March - end Sep */
- 1984, 86, 272, /* 1984: end March - end Sep */
- 1985, 86, 272, /* 1985: end March - end Sep */
- 0, 400, 0, /* others: no daylight saving at all ??? */
-};
-
-static struct dayrules {
- int dst_type; /* number obtained from system */
- int dst_hrs; /* hours to add when dst on */
- struct dstab *dst_rules; /* one of the above */
- enum {STH, NTH} dst_hemi; /* southern, northern hemisphere */
-} dayrules [] = {
- DST_USA, 1, usdaytab, NTH,
- DST_AUST, 1, ausdaytab, STH,
- DST_WET, 1, wedaytab, NTH,
- DST_MET, 1, medaytab, NTH,
- DST_EET, 1, medaytab, NTH, /* XXX */
- -1,
-};
-
-struct pcfs_args pc_tz; /* this is set by pcfs_mount */
-
-static struct tm *
-localtime(time_t *tim)
+int
+pc_tvtopct(
+ timestruc_t *tvp, /* UNIX time input */
+ struct pctime *pctp) /* pctime output */
{
- int dayno;
- struct tm *ct;
- int dalybeg, daylend;
- struct dayrules *dr;
- struct dstab *ds;
- int year;
- int copyt;
-
- copyt = *tim - (int)pc_tz.secondswest;
- ct = gmtime(copyt);
- dayno = ct->tm_yday;
- for (dr = dayrules; dr->dst_type >= 0; dr++)
- if (dr->dst_type == pc_tz.dsttime)
- break;
- if (dr->dst_type >= 0) {
- year = ct->tm_year + 1900;
- for (ds = dr->dst_rules; ds->dayyr; ds++) {
- if (ds->dayyr == year) {
- break;
- }
- }
- dalybeg = ds->daylb; /* first Sun after dst starts */
- daylend = ds->dayle; /* first Sun after dst ends */
- dalybeg = sunday(ct, dalybeg);
- daylend = sunday(ct, daylend);
- switch (dr->dst_hemi) {
- case NTH:
- if (!(
- (dayno > dalybeg ||
- (dayno == dalybeg && ct->tm_hour >= 2)) &&
- (dayno < daylend ||
- (dayno == daylend && ct->tm_hour < 1)))) {
- return (ct);
- }
- break;
- case STH:
- if (!(
- (dayno > dalybeg ||
- (dayno == dalybeg && ct->tm_hour >= 2)) ||
- (dayno < daylend ||
- (dayno == daylend && ct->tm_hour < 2)))) {
- return (ct);
- }
- break;
- default:
- return (ct);
- }
- copyt += dr->dst_hrs*60*60;
- ct = gmtime(copyt);
- ct->tm_isdst++;
+ uint_t year, month, day, hour, min, sec;
+ int64_t unixtime;
+
+ unixtime = (int64_t)tvp->tv_sec;
+ unixtime -= YZ_SECS;
+ unixtime -= pc_tz.secondswest;
+ if (unixtime <= 0) {
+ /*
+ * "before beginning of all time" for DOS ...
+ */
+ return (EOVERFLOW);
+ }
+ for (year = YEAR_ZERO; unixtime >= days_in_year(year) * 86400;
+ year++)
+ unixtime -= 86400 * days_in_year(year);
+
+ if (year > 127 + YEAR_ZERO) {
+ /*
+ * "past end of all time" for DOS - can happen
+ * on a 64bit kernel via utimes() syscall ...
+ */
+ return (EOVERFLOW);
}
- return (ct);
-}
-/*
- * The argument is a 0-origin day number.
- * The value is the day number of the first
- * Sunday on or after the day.
- */
-static int
-sunday(struct tm *t, int d)
-{
- if (d >= 58)
- d += dysize(YEAR_ZERO + t->tm_year) - 365;
- return (d - (d - t->tm_yday + t->tm_wday + 700) % 7);
-}
+ for (month = 1; unixtime >= 86400 * days_in_month(month, year);
+ month++)
+ unixtime -= 86400 * days_in_month(month, year);
-static int
-dysize(int y)
-{
- if (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
- return (366);
- return (365);
-}
+ year -= YEAR_ZERO;
-static struct tm *
-gmtime(int tim)
-{
- int d0, d1;
- int hms, day;
- short *tp;
- static struct tm xtime;
+ day = (int)(unixtime / 86400);
+ unixtime -= 86400 * day++; /* counting starts at 1 */
- /*
- * break initial number into days
- */
- hms = tim % 86400;
- day = tim / 86400;
- if (hms < 0) {
- hms += 86400;
- day -= 1;
- }
- tp = (short *)&xtime;
+ hour = (int)(unixtime / 3600);
+ unixtime -= 3600 * hour;
- /*
- * generate hours:minutes:seconds
- */
- *tp++ = hms%60;
- d1 = hms/60;
- *tp++ = d1%60;
- d1 /= 60;
- *tp++ = (short)d1;
+ min = (int)(unixtime / 60);
+ unixtime -= 60 * min;
- /*
- * day is the day number.
- * generate day of the week.
- * The addend is 4 mod 7 (1/1/1970 was Thursday)
- */
+ sec = (int)unixtime;
- xtime.tm_wday = (day+7340036)%7;
+ PC_DPRINTF3(1, "ux2pc date: %d.%d.%d\n", day, month, YEAR_ZERO + year);
+ PC_DPRINTF3(1, "ux2pc time: %dh%dm%ds\n", hour, min, sec);
+ PC_DPRINTF1(1, "ux2pc unixtime: %lld\n", (long long)(unixtime));
- /*
- * year number
- */
- if (day >= 0)
- for (d1 = 70; day >= dysize(YEAR_ZERO + d1); d1++)
- day -= dysize(YEAR_ZERO + d1);
- else
- for (d1 = 70; day < 0; d1--)
- day += dysize(YEAR_ZERO + d1 - 1);
- xtime.tm_year = (short)d1;
- xtime.tm_yday = d0 = day;
+ ASSERT(year >= 0 && year < 128);
+ ASSERT(month >= 1 && month <= 12);
+ ASSERT(day >= 1 && day <= days_in_month(month, year));
+ ASSERT(hour < 24);
+ ASSERT(min < 60);
+ ASSERT(sec < 60);
- /*
- * generate month
- */
+ pctp->pct_time =
+ LE_16(hour << HOURSHIFT | min << MINSHIFT | (sec / 2) << SECSHIFT);
+ pctp->pct_date =
+ LE_16(year << YEARSHIFT | month << MONSHIFT | day << DAYSHIFT);
- if (dysize(YEAR_ZERO + d1) == 366)
- dmsize[1] = 29;
- for (d1 = 0; d0 >= dmsize[d1]; d1++)
- d0 -= dmsize[d1];
- dmsize[1] = 28;
- *tp++ = d0+1;
- *tp++ = (short)d1;
- xtime.tm_isdst = 0;
- return (&xtime);
+ return (0);
}
/*
- * convert year, month, day, hour, minute, sec to (int)time.
+ * Convert time from DOS to UNIX time format.
+ * Since FAT timestamps cannot be expressed in 32bit time_t,
+ * the calculation is performed using 64bit values. It's up to
+ * the caller to decide what to do for out-of-UNIX-range values.
*/
-static time_t
-ctime(struct tm *tp)
+void
+pc_pcttotv(
+ struct pctime *pctp, /* DOS time input */
+ int64_t *unixtime) /* caller converts to time_t */
{
- int i;
- time_t ct;
-
- if (tp->tm_mon < 0 || tp->tm_mon > 11 ||
- tp->tm_mday < 1 || tp->tm_mday > 31 ||
- tp->tm_hour < 0 || tp->tm_hour > 23 ||
- tp->tm_min < 0 || tp->tm_min > 59 ||
- tp->tm_sec < 0 || tp->tm_sec > 59) {
- return (0);
- }
- ct = 0;
- for (i = /* 19 */ 70; i < tp->tm_year; i++)
- ct += dysize(YEAR_ZERO + i);
- /* Leap year */
- if (dysize(YEAR_ZERO + tp->tm_year) == 366 && tp->tm_mon >= 2)
- ct++;
- i = tp->tm_mon + 1;
- while (--i)
- ct += dmsize[i-1];
- ct += tp->tm_mday-1;
- ct = 24*ct + tp->tm_hour;
- ct = 60*ct + tp->tm_min;
- ct = 60*ct + tp->tm_sec;
- /* convert to GMT assuming local time */
- ct += (int)pc_tz.secondswest;
- /* now fix up local daylight time */
- if (localtime(&ct)->tm_isdst)
- ct -= 60*60;
- return (ct);
-}
+ uint_t year, month, day, hour, min, sec;
-/*
- * Determine whether a character is valid for a pc 8.3 file system file name.
- * The Windows 95 Resource Kit claims that these are valid:
- * uppercase letters and numbers
- * blank
- * ASCII characters greater than 127
- * $%'-_@~`!()^#&
- * Long file names can also have
- * lowercase letters
- * +,;=[]
- */
-int
-pc_validchar(char c)
-{
- char *cp;
- int n;
- static char valtab[] = {
- "$#&@!%()-{}<>`_^~|' "
- };
+ sec = 2 * ((LE_16(pctp->pct_time) >> SECSHIFT) & SECMASK);
+ min = (LE_16(pctp->pct_time) >> MINSHIFT) & MINMASK;
+ hour = (LE_16(pctp->pct_time) >> HOURSHIFT) & HOURMASK;
+ day = (LE_16(pctp->pct_date) >> DAYSHIFT) & DAYMASK;
+ month = (LE_16(pctp->pct_date) >> MONSHIFT) & MONMASK;
+ year = (LE_16(pctp->pct_date) >> YEARSHIFT) & YEARMASK;
+ year += YEAR_ZERO;
/*
- * Should be "$#&@!%()-{}`_^~' " ??
- * From experiment in DOSWindows, *+=|\[];:",<>.?/ are illegal.
- * See IBM DOS4.0 Tech Ref. B-57.
+ * Basic sanity checks. The FAT timestamp bitfields allow for
+ * impossible dates/times - return the "FAT epoch" for these.
*/
+ if (pctp->pct_date == 0) {
+ year = YEAR_ZERO;
+ month = 1;
+ day = 1;
+ }
+ if (month > 12 || month < 1 ||
+ day < 1 || day > days_in_month(month, year) ||
+ hour > 23 || min > 59 || sec > 59) {
+ cmn_err(CE_NOTE, "impossible FAT timestamp, "
+ "d/m/y %d/%d/%d, h:m:s %d:%d:%d",
+ day, month, year, hour, min, sec);
+ *unixtime = YZ_SECS + pc_tz.secondswest;
+ return;
+ }
- if (c >= 'A' && c <= 'Z')
- return (1);
- if (c >= '0' && c <= '9')
- return (1);
- cp = valtab;
- n = sizeof (valtab);
- while (n--) {
- if (c == *cp++)
- return (1);
+ PC_DPRINTF3(1, "pc2ux date: %d.%d.%d\n", day, month, year);
+ PC_DPRINTF3(1, "pc2ux time: %dh%dm%ds\n", hour, min, sec);
+
+ *unixtime = (int64_t)sec;
+ *unixtime += 60 * (int64_t)min;
+ *unixtime += 3600 * (int64_t)hour;
+ *unixtime += 86400 * (int64_t)day;
+ while (month > 1) {
+ month--;
+ *unixtime += 86400 * (int64_t)days_in_month(month, year);
}
- return (0);
+ while (year > YEAR_ZERO) {
+ *unixtime += 86400 * (int64_t)days_in_year(year);
+ year--;
+ }
+ /*
+ * For FAT, the beginning of all time is 01/01/1980,
+ * and years are counted relative to that.
+ * We adjust this base value by the timezone offset
+ * that is passed in to pcfs at mount time.
+ */
+ *unixtime += YZ_SECS;
+ *unixtime += pc_tz.secondswest;
+
+ /*
+ * FAT epoch is past UNIX epoch - negative UNIX times
+ * cannot result from the conversion.
+ */
+ ASSERT(*unixtime > 0);
+ PC_DPRINTF1(1, "pc2ux unixtime: %lld\n", (long long)(*unixtime));
}
/*
diff --git a/usr/src/uts/common/fs/pcfs/pc_vfsops.c b/usr/src/uts/common/fs/pcfs/pc_vfsops.c
index 881e75f99b..52d1ba3f19 100644
--- a/usr/src/uts/common/fs/pcfs/pc_vfsops.c
+++ b/usr/src/uts/common/fs/pcfs/pc_vfsops.c
@@ -63,6 +63,7 @@
#include <sys/open.h>
#include <sys/mntent.h>
#include <sys/policy.h>
+#include <sys/atomic.h>
/*
* The majority of PC media use a 512 sector size, but
@@ -84,6 +85,7 @@ static int pcfs_statvfs(struct vfs *, struct statvfs64 *);
static int pc_syncfsnodes(struct pcfs *);
static int pcfs_sync(struct vfs *, short, struct cred *);
static int pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp);
+static void pcfs_freevfs(vfs_t *vfsp);
static int pc_getfattype(struct vnode *, int, daddr_t *, int *);
static int pc_readfat(struct pcfs *fsp, uchar_t *fatp, daddr_t start,
@@ -94,24 +96,23 @@ static int pc_writefat(struct pcfs *fsp, daddr_t start);
* pcfs mount options table
*/
-static char *nohidden_cancel[] = {MNTOPT_PCFS_HIDDEN, NULL};
-static char *hidden_cancel[] = {MNTOPT_PCFS_NOHIDDEN, NULL};
-static char *nofoldcase_cancel[] = {MNTOPT_PCFS_FOLDCASE, NULL};
-static char *foldcase_cancel[] = {MNTOPT_PCFS_NOFOLDCASE, NULL};
+static char *nohidden_cancel[] = { MNTOPT_PCFS_HIDDEN, NULL };
+static char *hidden_cancel[] = { MNTOPT_PCFS_NOHIDDEN, NULL };
+static char *nofoldcase_cancel[] = { MNTOPT_PCFS_FOLDCASE, NULL };
+static char *foldcase_cancel[] = { MNTOPT_PCFS_NOFOLDCASE, NULL };
+static char *clamptime_cancel[] = { MNTOPT_PCFS_NOCLAMPTIME, NULL };
+static char *noclamptime_cancel[] = { MNTOPT_PCFS_CLAMPTIME, NULL };
static mntopt_t mntopts[] = {
/*
- * option name cancel option default arg flags
- * opt data
+ * option name cancel option default arg flags opt data
*/
- { MNTOPT_PCFS_NOHIDDEN, nohidden_cancel, NULL, 0,
- NULL },
- { MNTOPT_PCFS_HIDDEN, hidden_cancel, NULL, MO_DEFAULT,
- NULL },
- { MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT,
- NULL },
- { MNTOPT_PCFS_FOLDCASE, foldcase_cancel, NULL, 0,
- NULL }
+ { MNTOPT_PCFS_NOHIDDEN, nohidden_cancel, NULL, 0, NULL },
+ { MNTOPT_PCFS_HIDDEN, hidden_cancel, NULL, MO_DEFAULT, NULL },
+ { MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT, NULL },
+ { MNTOPT_PCFS_FOLDCASE, foldcase_cancel, NULL, 0, NULL },
+ { MNTOPT_PCFS_CLAMPTIME, clamptime_cancel, NULL, MO_DEFAULT, NULL },
+ { MNTOPT_PCFS_NOCLAMPTIME, noclamptime_cancel, NULL, NULL, NULL }
};
static mntopts_t pcfs_mntopts = {
@@ -129,9 +130,18 @@ int pcfsdebuglevel = 0;
* pcnodes_lock: protects the pcnode hash table "pcdhead", "pcfhead".
*
* Lock hierarchy: pcfslock > pcfs_lock > pcnodes_lock
+ *
+ * pcfs_mountcount: used to prevent module unloads while there is still
+ * pcfs state from a former mount hanging around. With
+ * forced umount support, the filesystem module must not
+ * be allowed to go away before the last VFS_FREEVFS()
+ * call has been made.
+ * Since this is just an atomic counter, there's no need
+ * for locking.
*/
kmutex_t pcfslock;
-krwlock_t pcnodes_lock; /* protect the pcnode hash table "pcdhead", "pcfhead" */
+krwlock_t pcnodes_lock;
+uint32_t pcfs_mountcount;
static int pcfstype;
@@ -182,6 +192,15 @@ _fini(void)
{
int error;
+ /*
+ * If a forcedly unmounted instance is still hanging around,
+ * we cannot allow the module to be unloaded because that would
+ * cause panics once the VFS framework decides it's time to call
+ * into VFS_FREEVFS().
+ */
+ if (pcfs_mountcount)
+ return (EBUSY);
+
error = mod_remove(&modlinkage);
if (error)
return (error);
@@ -213,6 +232,7 @@ pcfsinit(int fstype, char *name)
VFSNAME_STATVFS, pcfs_statvfs,
VFSNAME_SYNC, (fs_generic_func_p) pcfs_sync,
VFSNAME_VGET, pcfs_vget,
+ VFSNAME_FREEVFS, (fs_generic_func_p) pcfs_freevfs,
NULL, NULL
};
int error;
@@ -240,6 +260,7 @@ pcfsinit(int fstype, char *name)
pcfstype = fstype;
(void) pc_init();
+ pcfs_mountcount = 0;
return (0);
}
@@ -313,6 +334,7 @@ pcfs_mount(
struct pcfs_args tmp_tz;
int hidden = 0;
int foldcase = 0;
+ int noclamptime = 0;
tmp_tz.flags = 0;
if (copyin(data, &tmp_tz, datalen)) {
@@ -321,12 +343,15 @@ pcfs_mount(
if (datalen == sizeof (struct pcfs_args)) {
hidden = tmp_tz.flags & PCFS_MNT_HIDDEN;
foldcase = tmp_tz.flags & PCFS_MNT_FOLDCASE;
+ noclamptime = tmp_tz.flags & PCFS_MNT_NOCLAMPTIME;
}
if (hidden)
vfs_setmntopt(vfsp, MNTOPT_PCFS_HIDDEN, NULL, 0);
if (foldcase)
vfs_setmntopt(vfsp, MNTOPT_PCFS_FOLDCASE, NULL, 0);
+ if (noclamptime)
+ vfs_setmntopt(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL, 0);
/*
* more than one pc filesystem can be mounted on x86
* so the pc_tz structure is now a critical region
@@ -542,6 +567,8 @@ pcfs_mount(
fsp->pcfs_flags |= PCFS_HIDDEN;
if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL))
fsp->pcfs_flags |= PCFS_FOLDCASE;
+ if (vfs_optionisset(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL))
+ fsp->pcfs_flags |= PCFS_NOCLAMPTIME;
vfsp->vfs_dev = pseudodev;
vfsp->vfs_fstype = pcfstype;
vfs_make_fsid(&vfsp->vfs_fsid, pseudodev, pcfstype);
@@ -561,6 +588,7 @@ pcfs_mount(
fsp->pcfs_nxt = pc_mounttab;
pc_mounttab = fsp;
mutex_exit(&pcfslock);
+ atomic_inc_32(&pcfs_mountcount);
return (0);
}
@@ -580,38 +608,36 @@ pcfs_unmount(
if (secpolicy_fs_unmount(cr, vfsp) != 0)
return (EPERM);
- /*
- * forced unmount is not supported by this file system
- * and thus, ENOTSUP, is being returned.
- */
- if (flag & MS_FORCE)
- return (ENOTSUP);
-
PC_DPRINTF0(4, "pcfs_unmount\n");
fsp = VFSTOPCFS(vfsp);
+
/*
* We don't have to lock fsp because the VVFSLOCK in vfs layer will
* prevent lookuppn from crossing the mount point.
+ * If this is not a forced umount request and there's ongoing I/O,
+ * don't allow the mount to proceed.
*/
- if (fsp->pcfs_nrefs) {
+ if (flag & MS_FORCE)
+ vfsp->vfs_flag |= VFS_UNMOUNTED;
+ else if (fsp->pcfs_nrefs)
return (EBUSY);
- }
+
+ mutex_enter(&pcfslock);
/*
- * Allow an unmount (regardless of state) if the fs instance has
- * been marked as beyond recovery.
+ * If this is a forced umount request or if the fs instance has
+ * been marked as beyond recovery, allow the umount to proceed
+ * regardless of state. pc_diskchanged() forcibly releases all
+ * inactive vnodes/pcnodes.
*/
- if (fsp->pcfs_flags & PCFS_IRRECOV) {
- mutex_enter(&pcfslock);
+ if (flag & MS_FORCE || fsp->pcfs_flags & PCFS_IRRECOV) {
rw_enter(&pcnodes_lock, RW_WRITER);
pc_diskchanged(fsp);
rw_exit(&pcnodes_lock);
- mutex_exit(&pcfslock);
}
/* now there should be no pcp node on pcfhead or pcdhead. */
- mutex_enter(&pcfslock);
if (fsp == pc_mounttab) {
pc_mounttab = fsp->pcfs_nxt;
} else {
@@ -620,14 +646,14 @@ pcfs_unmount(
fsp1->pcfs_nxt = fsp->pcfs_nxt;
}
- if (fsp->pcfs_fatp != (uchar_t *)0) {
- pc_invalfat(fsp);
- }
mutex_exit(&pcfslock);
- VN_RELE(fsp->pcfs_devvp);
- mutex_destroy(&fsp->pcfs_lock);
- kmem_free(fsp, (uint_t)sizeof (struct pcfs));
+ /*
+ * Since we support VFS_FREEVFS(), there's no need to
+ * free the fsp right now. The framework will tell us
+ * when the right time to do so has arrived by calling
+ * into pcfs_freevfs.
+ */
return (0);
}
@@ -646,6 +672,7 @@ pcfs_root(
fsp = VFSTOPCFS(vfsp);
if (error = pc_lockfs(fsp, 0, 0))
return (error);
+
pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
PC_DPRINTF2(9, "pcfs_root(0x%p) pcp= 0x%p\n",
(void *)vfsp, (void *)pcp);
@@ -768,6 +795,8 @@ pcfs_sync(
int
pc_lockfs(struct pcfs *fsp, int diskchanged, int releasing)
{
+ int err;
+
if ((fsp->pcfs_flags & PCFS_IRRECOV) && !releasing)
return (EIO);
@@ -784,9 +813,10 @@ pc_lockfs(struct pcfs *fsp, int diskchanged, int releasing)
* had grabbed the lock and set the bit)
*/
if (!diskchanged && !(fsp->pcfs_flags & PCFS_IRRECOV)) {
- int err;
- if ((err = pc_getfat(fsp)))
+ if ((err = pc_getfat(fsp))) {
+ mutex_exit(&fsp->pcfs_lock);
return (err);
+ }
}
fsp->pcfs_flags |= PCFS_LOCKED;
fsp->pcfs_owner = curthread;
@@ -1365,7 +1395,7 @@ isBPB(uchar_t *cp, int *fattypep)
(ltohs(cp[510]) != MBB_MAGIC) ||
!VALID_SECSIZE(secsize) ||
!VALID_SPCL(bpb->spcl) ||
- (secsize * bpb->spcl >= (64 * 1024)) ||
+ (secsize * bpb->spcl > (64 * 1024)) ||
!(ltohs(bpb->res_sec[0])))
return (0);
@@ -1599,8 +1629,8 @@ pc_getfat(struct pcfs *fsp)
fsp->pcfs_spcl = (int)bootp->spcl;
fsp->pcfs_fatsec = fatsec;
fsp->pcfs_spt = (int)ltohs(bootp->spt);
- fsp->pcfs_rdirsec = (int)ltohs(bootp->rdirents[0])
- * sizeof (struct pcdir) / secsize;
+ fsp->pcfs_rdirsec = ((int)ltohs(bootp->rdirents[0])
+ * sizeof (struct pcdir) + (secsize - 1)) / secsize;
fsp->pcfs_clsize = fsp->pcfs_spcl * secsize;
fsp->pcfs_fatstart = fsp->pcfs_dosstart +
(daddr_t)ltohs(bootp->res_sec[0]);
@@ -2157,3 +2187,29 @@ pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn)
{
return (fsp->pcfs_fat_changemap[bn] == 1);
}
+
+/*
+ * Implementation of VFS_FREEVFS() to support forced umounts.
+ * This is called by the vfs framework after umount, to trigger
+ * the release of any resources still associated with the given
+ * vfs_t once the need to keep them has gone away.
+ */
+void
+pcfs_freevfs(vfs_t *vfsp)
+{
+ struct pcfs *fsp = VFSTOPCFS(vfsp);
+
+ mutex_enter(&pcfslock);
+ if (fsp->pcfs_fatp != (uchar_t *)0)
+ pc_invalfat(fsp);
+ mutex_exit(&pcfslock);
+
+ VN_RELE(fsp->pcfs_devvp);
+ mutex_destroy(&fsp->pcfs_lock);
+ kmem_free(fsp, (uint_t)sizeof (struct pcfs));
+
+ /*
+ * Allow _fini() to succeed now, if so desired.
+ */
+ atomic_dec_32(&pcfs_mountcount);
+}
diff --git a/usr/src/uts/common/fs/pcfs/pc_vnops.c b/usr/src/uts/common/fs/pcfs/pc_vnops.c
index 56c78abd22..13a48fe47f 100644
--- a/usr/src/uts/common/fs/pcfs/pc_vnops.c
+++ b/usr/src/uts/common/fs/pcfs/pc_vnops.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -55,6 +54,7 @@
#include <sys/conf.h>
#include <sys/flock.h>
#include <sys/policy.h>
+#include <sys/sdt.h>
#include <vm/seg.h>
#include <vm/page.h>
@@ -293,6 +293,12 @@ rwpcp(
rlim64_t limit = uio->uio_llimit;
int oresid = uio->uio_resid;
+ /*
+ * If the filesystem was umounted by force, return immediately.
+ */
+ if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
PC_DPRINTF4(5, "rwpcp pcp=%p off=%lld resid=%ld size=%u\n", (void *)pcp,
uio->uio_loffset, uio->uio_resid, pcp->pc_size);
@@ -514,6 +520,7 @@ pcfs_getattr(
int error;
char attr;
struct pctime atime;
+ int64_t unixtime;
PC_DPRINTF1(8, "pcfs_getattr: vp=%p\n", (void *)vp);
@@ -559,11 +566,53 @@ pcfs_getattr(
pc_getstartcluster(fsp, &pcp->pc_entry), fsp->pcfs_entps);
vap->va_nlink = 1;
vap->va_size = (u_offset_t)pcp->pc_size;
- pc_pcttotv(&pcp->pc_entry.pcd_mtime, &vap->va_mtime);
+
+ pc_pcttotv(&pcp->pc_entry.pcd_mtime, &unixtime);
+ if ((fsp->pcfs_flags & PCFS_NOCLAMPTIME) == 0) {
+ if (unixtime > INT32_MAX)
+ DTRACE_PROBE1(pcfs__mtimeclamped, int64_t, unixtime);
+ unixtime = MIN(unixtime, INT32_MAX);
+ } else if (unixtime > INT32_MAX &&
+ get_udatamodel() == DATAMODEL_ILP32) {
+ pc_unlockfs(fsp);
+ DTRACE_PROBE1(pcfs__mtimeoverflowed, int64_t, unixtime);
+ return (EOVERFLOW);
+ }
+
+ vap->va_mtime.tv_sec = (time_t)unixtime;
+ vap->va_mtime.tv_nsec = 0;
+
+ /*
+ * FAT doesn't know about POSIX ctime.
+ * Best approximation is to always set it to mtime.
+ */
vap->va_ctime = vap->va_mtime;
- atime.pct_time = 0;
+
+ /*
+ * FAT only stores "last access date". If that's the
+ * same as the date of last modification then the time
+ * of last access is known. Otherwise, use midnight.
+ */
atime.pct_date = pcp->pc_entry.pcd_ladate;
- pc_pcttotv(&atime, &vap->va_atime);
+ if (atime.pct_date == pcp->pc_entry.pcd_mtime.pct_date)
+ atime.pct_time = pcp->pc_entry.pcd_mtime.pct_time;
+ else
+ atime.pct_time = 0;
+ pc_pcttotv(&atime, &unixtime);
+ if ((fsp->pcfs_flags & PCFS_NOCLAMPTIME) == 0) {
+ if (unixtime > INT32_MAX)
+ DTRACE_PROBE1(pcfs__atimeclamped, int64_t, unixtime);
+ unixtime = MIN(unixtime, INT32_MAX);
+ } else if (unixtime > INT32_MAX &&
+ get_udatamodel() == DATAMODEL_ILP32) {
+ pc_unlockfs(fsp);
+ DTRACE_PROBE1(pcfs__atimeoverflowed, int64_t, unixtime);
+ return (EOVERFLOW);
+ }
+
+ vap->va_atime.tv_sec = (time_t)unixtime;
+ vap->va_atime.tv_nsec = 0;
+
vap->va_rdev = 0;
vap->va_nblocks = (fsblkcnt64_t)howmany((offset_t)pcp->pc_size,
DEV_BSIZE);
@@ -586,7 +635,7 @@ pcfs_setattr(
mode_t mask = vap->va_mask;
int error;
struct pcfs *fsp;
- timestruc_t now;
+ timestruc_t now, *timep;
PC_DPRINTF2(6, "pcfs_setattr: vp=%p mask=%x\n", (void *)vp, (int)mask);
/*
@@ -596,7 +645,7 @@ pcfs_setattr(
return (EINVAL);
}
/*
- * pcfs_settar is now allowed on directories to avoid silly warnings
+ * pcfs_setattr is now allowed on directories to avoid silly warnings
* from 'tar' when it tries to set times on a directory, and console
* printf's on the NFS server when it gets EINVAL back on such a
* request. One possible problem with that since a directory entry
@@ -660,7 +709,7 @@ pcfs_setattr(
/*
* Change file modified times.
*/
- if ((mask & (AT_MTIME | AT_CTIME)) && (vap->va_mtime.tv_sec != -1)) {
+ if (mask & (AT_MTIME | AT_CTIME)) {
/*
* If SysV-compatible option to set access and
* modified times if privileged, owner, or write access,
@@ -668,18 +717,25 @@ pcfs_setattr(
*
* XXX - va_mtime.tv_sec == -1 flags this.
*/
+ timep = &vap->va_mtime;
if (vap->va_mtime.tv_sec == -1) {
gethrestime(&now);
- pc_tvtopct(&now, &pcp->pc_entry.pcd_mtime);
- } else {
- pc_tvtopct(&vap->va_mtime, &pcp->pc_entry.pcd_mtime);
+ timep = &now;
+ }
+ if ((fsp->pcfs_flags & PCFS_NOCLAMPTIME) == 0 &&
+ timep->tv_sec > INT32_MAX) {
+ error = EOVERFLOW;
+ goto out;
}
+ error = pc_tvtopct(timep, &pcp->pc_entry.pcd_mtime);
+ if (error)
+ goto out;
pcp->pc_flags |= PC_CHG;
}
/*
* Change file access times.
*/
- if ((mask & (AT_ATIME)) && (vap->va_atime.tv_sec != -1)) {
+ if (mask & AT_ATIME) {
/*
* If SysV-compatible option to set access and
* modified times if privileged, owner, or write access,
@@ -689,12 +745,19 @@ pcfs_setattr(
*/
struct pctime atime;
+ timep = &vap->va_atime;
if (vap->va_atime.tv_sec == -1) {
gethrestime(&now);
- pc_tvtopct(&now, &atime);
- } else {
- pc_tvtopct(&vap->va_atime, &atime);
+ timep = &now;
}
+ if ((fsp->pcfs_flags & PCFS_NOCLAMPTIME) == 0 &&
+ timep->tv_sec > INT32_MAX) {
+ error = EOVERFLOW;
+ goto out;
+ }
+ error = pc_tvtopct(timep, &atime);
+ if (error)
+ goto out;
pcp->pc_entry.pcd_ladate = atime.pct_date;
pcp->pc_flags |= PC_CHG;
}
@@ -778,6 +841,26 @@ pcfs_inactive(
fsp = VFSTOPCFS(vp->v_vfsp);
error = pc_lockfs(fsp, 0, 1);
+ /*
+ * If the filesystem was umounted by force, all dirty
+ * pages associated with this vnode are invalidated
+ * and then the vnode will be freed.
+ */
+ if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED) {
+ pcp = VTOPC(vp);
+ if (vn_has_cached_data(vp)) {
+ (void) pvn_vplist_dirty(vp, (u_offset_t)0,
+ pcfs_putapage, B_INVAL, (struct cred *)NULL);
+ }
+ remque(pcp);
+ if (error == 0)
+ pc_unlockfs(fsp);
+ vn_free(vp);
+ kmem_free(pcp, sizeof (struct pcnode));
+ VFS_RELE(PCFSTOVFS(fsp));
+ return;
+ }
+
mutex_enter(&vp->v_lock);
ASSERT(vp->v_count >= 1);
if (vp->v_count > 1) {
@@ -793,10 +876,15 @@ pcfs_inactive(
* with a subsequent pc_diskchanged() call has released
* the pcnode. If it has then release the vnode as above.
*/
- if ((pcp = VTOPC(vp)) == NULL)
+ if ((pcp = VTOPC(vp)) == NULL) {
+ if (vn_has_cached_data(vp))
+ (void) pvn_vplist_dirty(vp, (u_offset_t)0,
+ pcfs_putapage, B_INVAL | B_TRUNC,
+ (struct cred *)NULL);
vn_free(vp);
- else
+ } else {
pc_rele(pcp);
+ }
if (!error)
pc_unlockfs(fsp);
@@ -818,6 +906,12 @@ pcfs_lookup(
int error;
/*
+ * If the filesystem was umounted by force, return immediately.
+ */
+ if (dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ /*
* verify that the dvp is still valid on the disk
*/
fsp = VFSTOPCFS(dvp->v_vfsp);
@@ -1116,6 +1210,12 @@ pcfs_readdir(
struct pc_dirent *ld = &lbp;
int error;
+ /*
+ * If the filesystem was umounted by force, return immediately.
+ */
+ if (dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
if ((uiop->uio_iovcnt != 1) ||
(uiop->uio_loffset % sizeof (struct pcdir)) != 0) {
return (EINVAL);
@@ -1249,6 +1349,12 @@ pcfs_getapage(
page_t *pagefound;
int err;
+ /*
+ * If the filesystem was umounted by force, return immediately.
+ */
+ if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
PC_DPRINTF3(5, "pcfs_getapage: vp=%p off=%lld len=%lu\n",
(void *)vp, off, len);
@@ -1434,6 +1540,12 @@ pcfs_putpage(
offset_t eoff;
int err;
+ /*
+ * If the filesystem was umounted by force, return immediately.
+ */
+ if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
PC_DPRINTF1(6, "pcfs_putpage vp=0x%p\n", (void *)vp);
if (vp->v_flag & VNOMAP)
return (ENOSYS);
diff --git a/usr/src/uts/common/sys/fs/pc_dir.h b/usr/src/uts/common/sys/fs/pc_dir.h
index 4dd2364083..dda6f515a9 100644
--- a/usr/src/uts/common/sys/fs/pc_dir.h
+++ b/usr/src/uts/common/sys/fs/pc_dir.h
@@ -242,19 +242,39 @@ struct pc_dirent {
char d_name[PCMAXNAMLEN + 1];
};
+/*
+ * Check FAT 8.3 filename characters for validity.
+ * Lacking a kernel iconv, codepage support for short filenames
+ * is not provided.
+ * Short names must be uppercase ASCII (no support for MSDOS
+ * codepages right now, sorry) and may not contain any of
+ * *+=|\[];:",<>.?/ which are explicitly forbidden by the
+ * FAT specifications.
+ */
+#define pc_validchar(_c) \
+ (((_c) >= ' ' && !((_c) & ~0177)) && \
+ (((_c) >= 'A' && (_c) <= 'Z') || \
+ ((_c) >= '0' && (_c) <= '9') || \
+ ((_c) != '"' && (_c) != '*' && (_c) != '+' && (_c) != ',' && \
+ (_c) != '.' && (_c) != '/' && (_c) != ':' && (_c) != ';' && \
+ (_c) != '<' && (_c) != '=' && (_c) != '>' && (_c) != '?' && \
+ (_c) != '[' && (_c) != '\\' && (_c) != ']' && (_c) != '|')))
+
+
#ifdef _KERNEL
/*
- * macros for converting to/from upper or lower case.
+ * macros for converting ASCII to/from upper or lower case.
* users may give and get names in lower case, but they are stored on the
- * disk in upper case to be PCDOS compatible
+ * disk in upper case to be PCDOS compatible.
+ * These would better come from some shared source in <sys/...> but
+ * there is no such place yet.
*/
#define toupper(C) (((C) >= 'a' && (C) <= 'z') ? (C) - 'a' + 'A' : (C))
#define tolower(C) (((C) >= 'A' && (C) <= 'Z') ? (C) - 'A' + 'a' : (C))
-extern void pc_tvtopct(timestruc_t *, struct pctime *); /* timeval to pctime */
-extern void pc_pcttotv(struct pctime *, timestruc_t *); /* pctime to timeval */
-extern int pc_validchar(char); /* valid filename ch */
+extern int pc_tvtopct(timestruc_t *, struct pctime *); /* timeval to pctime */
+extern void pc_pcttotv(struct pctime *, int64_t *); /* pctime to timeval */
extern int pc_valid_lfn_char(char); /* valid long filename ch */
extern int pc_read_long_fn(struct vnode *, struct uio *,
diff --git a/usr/src/uts/common/sys/fs/pc_fs.h b/usr/src/uts/common/sys/fs/pc_fs.h
index b675a56556..4891b61861 100644
--- a/usr/src/uts/common/sys/fs/pc_fs.h
+++ b/usr/src/uts/common/sys/fs/pc_fs.h
@@ -187,23 +187,24 @@ struct pcfs {
uint32_t f32fsinfo_sector; /* where to read/write fsinfo */
struct pcfs *pcfs_nxt; /* linked list of all mounts */
int pcfs_fatjustread; /* Used to flag a freshly found FAT */
+ struct vnode *pcfs_root; /* vnode for the root dir of the fs */
};
/*
* flags
*/
-#define PCFS_FATMOD 0x01 /* FAT has been modified */
-#define PCFS_LOCKED 0x02 /* fs is locked */
-#define PCFS_WANTED 0x04 /* locked fs is wanted */
-#define PCFS_FAT16 0x400 /* 16 bit FAT */
-#define PCFS_NOCHK 0x800 /* don't resync fat on error */
-#define PCFS_BOOTPART 0x1000 /* boot partition type */
-#define PCFS_HIDDEN 0x2000 /* show hidden files */
-#define PCFS_PCMCIA_NO_CIS 0x4000 /* PCMCIA psuedo floppy */
-#define PCFS_FOLDCASE 0x8000 /* fold all names from media to */
- /* lowercase */
-#define PCFS_FAT32 0x10000 /* 32 bit FAT */
-#define PCFS_IRRECOV 0x20000 /* FS was messed with during write */
+#define PCFS_FATMOD 0x01 /* FAT has been modified */
+#define PCFS_LOCKED 0x02 /* fs is locked */
+#define PCFS_WANTED 0x04 /* locked fs is wanted */
+#define PCFS_FAT16 0x400 /* 16 bit FAT */
+#define PCFS_NOCHK 0x800 /* don't resync fat on error */
+#define PCFS_BOOTPART 0x1000 /* boot partition type */
+#define PCFS_HIDDEN 0x2000 /* show hidden files */
+#define PCFS_PCMCIA_NO_CIS 0x4000 /* PCMCIA psuedo floppy */
+#define PCFS_FOLDCASE 0x8000 /* fold filenames to lowercase */
+#define PCFS_FAT32 0x10000 /* 32 bit FAT */
+#define PCFS_IRRECOV 0x20000 /* FS was messed with during write */
+#define PCFS_NOCLAMPTIME 0x40000 /* expose full FAT timestamp range */
/* for compatibility */
struct old_pcfs_args {
@@ -225,6 +226,7 @@ struct pcfs_args {
#define PCFS_MNT_HIDDEN 0x01 /* show hidden files */
#define PCFS_MNT_FOLDCASE 0x02 /* fold all names from media to */
/* lowercase */
+#define PCFS_MNT_NOCLAMPTIME 0x04 /* expose full FAT timestamp range */
/*
* pcfs mount options.
@@ -233,6 +235,8 @@ struct pcfs_args {
#define MNTOPT_PCFS_NOHIDDEN "nohidden"
#define MNTOPT_PCFS_FOLDCASE "foldcase"
#define MNTOPT_PCFS_NOFOLDCASE "nofoldcase"
+#define MNTOPT_PCFS_CLAMPTIME "clamptime"
+#define MNTOPT_PCFS_NOCLAMPTIME "noclamptime"
/*
* Disk timeout value in sec.