summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
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.