diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/cmd/fs.d/pcfs/fsck/Makefile | 28 | ||||
-rw-r--r-- | usr/src/cmd/fs.d/pcfs/fsck/dir.c | 88 | ||||
-rw-r--r-- | usr/src/cmd/fs.d/pcfs/mount/mount.c | 8 | ||||
-rw-r--r-- | usr/src/uts/common/fs/pcfs/pc_alloc.c | 23 | ||||
-rw-r--r-- | usr/src/uts/common/fs/pcfs/pc_dir.c | 4 | ||||
-rw-r--r-- | usr/src/uts/common/fs/pcfs/pc_node.c | 59 | ||||
-rw-r--r-- | usr/src/uts/common/fs/pcfs/pc_subr.c | 542 | ||||
-rw-r--r-- | usr/src/uts/common/fs/pcfs/pc_vfsops.c | 138 | ||||
-rw-r--r-- | usr/src/uts/common/fs/pcfs/pc_vnops.c | 150 | ||||
-rw-r--r-- | usr/src/uts/common/sys/fs/pc_dir.h | 30 | ||||
-rw-r--r-- | usr/src/uts/common/sys/fs/pc_fs.h | 28 |
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. |