Home | History | Annotate | Download | only in hsfs
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Miscellaneous support subroutines for High Sierra filesystem
     23  *
     24  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
     25  * Use is subject to license terms.
     26  */
     27 
     28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     29 
     30 #include <sys/types.h>
     31 #include <sys/param.h>
     32 #include <sys/time.h>
     33 #include <sys/cmn_err.h>
     34 #include <sys/systm.h>
     35 #include <sys/sysmacros.h>
     36 #include <sys/buf.h>
     37 #include <sys/conf.h>
     38 #include <sys/user.h>
     39 #include <sys/vfs.h>
     40 #include <sys/vnode.h>
     41 #include <sys/proc.h>
     42 #include <sys/debug.h>
     43 #include <sys/kmem.h>
     44 #include <sys/uio.h>
     45 #include <vm/hat.h>
     46 #include <vm/as.h>
     47 #include <vm/seg.h>
     48 #include <vm/page.h>
     49 #include <vm/pvn.h>
     50 #include <vm/seg_map.h>
     51 #include <sys/swap.h>
     52 #include <vm/seg_kmem.h>
     53 
     54 #include <sys/fs/hsfs_spec.h>
     55 #include <sys/fs/hsfs_node.h>
     56 #include <sys/fs/hsfs_impl.h>
     57 
     58 #define	THE_EPOCH	1970
     59 #define	END_OF_TIME	2099
     60 extern int hsfs_lostpage;
     61 
     62 #ifdef __STDC__
     63 static time_t hs_date_to_gmtime(int year, int mon, int day, int gmtoff);
     64 #else
     65 static time_t hs_date_to_gmtime();
     66 #endif
     67 
     68 /*
     69  * Table used in logging non-fatal errors which should be recorded
     70  * once per mount.  Indexed by HSFS_ERR values (defined in hsfs_node.h).
     71  */
     72 struct hsfs_error {
     73 	char	*hdr_text;	/* msg prefix: general error type */
     74 				/* must contain %s for mnt pt */
     75 	char 	*err_text;	/* specific error message */
     76 	uchar_t	multiple;	/* > 1 such error per fs possible? */
     77 	uchar_t	n_printf_args;	/* if err_text printf-like, # addtl args */
     78 } hsfs_error[] = {
     79 	/* HSFS_ERR_TRAILING_JUNK */
     80 	"hsfs: Warning: the file system mounted on %s "
     81 		"does not conform to the ISO-9660 specification:",
     82 	"trailing blanks or null characters in file or directory name.\n",
     83 	1, 0,
     84 	/* HSFS_ERR_LOWER_CASE_NM */
     85 	"hsfs: Warning: the file system mounted on %s "
     86 		"does not conform to the ISO-9660 specification:",
     87 	"lower case characters in file or directory name.\n",
     88 	1, 0,
     89 	/* HSFS_ERR_BAD_ROOT_DIR */
     90 	"hsfs: Warning: the file system mounted on %s "
     91 		"does not conform to the ISO-9660 specification:",
     92 	"invalid root directory.\n",
     93 	0,  0,
     94 	/* HSFS_ERR_UNSUP_TYPE */
     95 	"hsfs: Warning: the file system mounted on %s "
     96 		"contains a file or directory with an unsupported type:",
     97 	" 0x%x.\n",
     98 	1, 1,
     99 	/* HSFS_ERR_BAD_FILE_LEN */
    100 	"hsfs: Warning: file system mounted on %s "
    101 		"does not conform to the ISO-9660 specification:",
    102 	"file name length greater than max allowed\n",
    103 	1, 0,
    104 	/* HSFS_ERR_BAD_JOLIET_FILE_LEN */
    105 	"hsfs: Warning: file system mounted on %s "
    106 		"does not conform to the Joliet specification:",
    107 	"file name length greater than max allowed\n",
    108 	1, 0,
    109 	/* HSFS_ERR_TRUNC_JOLIET_FILE_LEN */
    110 	"hsfs: Warning: file system mounted on %s "
    111 		"does not conform to the Joliet specification:",
    112 	"file name length greater than MAXNAMELEN (truncated)\n",
    113 	1, 0,
    114 	/* HSFS_ERR_BAD_DIR_ENTRY */
    115 	"hsfs: Warning: file system mounted on %s "
    116 		"has inconsistent data:",
    117 	"invalid directory or file name length (ignored)\n",
    118 	1, 0,
    119 	/* HSFS_ERR_NEG_SUA_LEN */
    120 	"hsfs: Warning: file system mounted on %s "
    121 		"has inconsistent Rock Ridge data:",
    122 	"negative SUA len\n",
    123 	1, 0,
    124 	/* HSFS_ERR_BAD_SUA_LEN */
    125 	"hsfs: Warning: file system mounted on %s "
    126 		"has inconsistent Rock Ridge data:",
    127 	"SUA len too big\n",
    128 	1, 0,
    129 };
    130 
    131 /*
    132  * Local datatype for defining tables of (Offset, Name) pairs for
    133  * kstats.
    134  */
    135 typedef struct {
    136 	offset_t	index;
    137 	char		*name;
    138 } hsfs_ksindex_t;
    139 
    140 static const hsfs_ksindex_t hsfs_kstats[] = {
    141 	{ 0,		"mountpoint"		},
    142 	{ 1,		"pages_lost"		},
    143 	{ 2,		"physical_read_pages"	},
    144 	{ 3,		"cache_read_pages"	},
    145 	{ 4,		"readahead_pages"	},
    146 	{ 5,		"coalesced_pages"	},
    147 	{ 6,		"total_pages_requested"	},
    148 	{-1,		NULL 			}
    149 };
    150 
    151 /*
    152  * hs_parse_dirdate
    153  *
    154  * Parse the short 'directory-format' date into a Unix timeval.
    155  * This is the date format used in Directory Entries.
    156  *
    157  * If the date is not representable, make something up.
    158  */
    159 void
    160 hs_parse_dirdate(dp, tvp)
    161 	uchar_t *dp;
    162 	struct timeval *tvp;
    163 {
    164 	int year, month, day, hour, minute, sec, gmtoff;
    165 
    166 	year = HDE_DATE_YEAR(dp);
    167 	month = HDE_DATE_MONTH(dp);
    168 	day = HDE_DATE_DAY(dp);
    169 	hour = HDE_DATE_HOUR(dp);
    170 	minute = HDE_DATE_MIN(dp);
    171 	sec = HDE_DATE_SEC(dp);
    172 	gmtoff = HDE_DATE_GMTOFF(dp);
    173 
    174 	tvp->tv_usec = 0;
    175 	if (year < THE_EPOCH) {
    176 		tvp->tv_sec = 0;
    177 	} else {
    178 		tvp->tv_sec = hs_date_to_gmtime(year, month, day, gmtoff);
    179 		if (tvp->tv_sec != -1) {
    180 			tvp->tv_sec += ((hour * 60) + minute) * 60 + sec;
    181 		}
    182 	}
    183 
    184 	return;
    185 
    186 }
    187 
    188 /*
    189  * hs_parse_longdate
    190  *
    191  * Parse the long 'user-oriented' date into a Unix timeval.
    192  * This is the date format used in the Volume Descriptor.
    193  *
    194  * If the date is not representable, make something up.
    195  */
    196 void
    197 hs_parse_longdate(dp, tvp)
    198 	uchar_t *dp;
    199 	struct timeval *tvp;
    200 {
    201 	int year, month, day, hour, minute, sec, gmtoff;
    202 
    203 	year = HSV_DATE_YEAR(dp);
    204 	month = HSV_DATE_MONTH(dp);
    205 	day = HSV_DATE_DAY(dp);
    206 	hour = HSV_DATE_HOUR(dp);
    207 	minute = HSV_DATE_MIN(dp);
    208 	sec = HSV_DATE_SEC(dp);
    209 	gmtoff = HSV_DATE_GMTOFF(dp);
    210 
    211 	tvp->tv_usec = 0;
    212 	if (year < THE_EPOCH) {
    213 		tvp->tv_sec = 0;
    214 	} else {
    215 		tvp->tv_sec = hs_date_to_gmtime(year, month, day, gmtoff);
    216 		if (tvp->tv_sec != -1) {
    217 			tvp->tv_sec += ((hour * 60) + minute) * 60 + sec;
    218 			tvp->tv_usec = HSV_DATE_HSEC(dp) * 10000;
    219 		}
    220 	}
    221 
    222 }
    223 
    224 /* cumulative number of seconds per month,  non-leap and leap-year versions */
    225 static time_t cum_sec[] = {
    226 	0x0, 0x28de80, 0x4dc880, 0x76a700, 0x9e3400, 0xc71280,
    227 	0xee9f80, 0x1177e00, 0x1405c80, 0x167e980, 0x190c800, 0x1b85500
    228 };
    229 static time_t cum_sec_leap[] = {
    230 	0x0, 0x28de80, 0x4f1a00, 0x77f880, 0x9f8580, 0xc86400,
    231 	0xeff100, 0x118cf80, 0x141ae00, 0x1693b00, 0x1921980, 0x1b9a680
    232 };
    233 #define	SEC_PER_DAY	0x15180
    234 #define	SEC_PER_YEAR	0x1e13380
    235 
    236 /*
    237  * hs_date_to_gmtime
    238  *
    239  * Convert year(1970-2099)/month(1-12)/day(1-31) to seconds-since-1970/1/1.
    240  *
    241  * Returns -1 if the date is out of range.
    242  */
    243 static time_t
    244 hs_date_to_gmtime(year, mon, day, gmtoff)
    245 	int year;
    246 	int mon;
    247 	int day;
    248 	int gmtoff;
    249 {
    250 	time_t sum;
    251 	time_t *cp;
    252 	int y;
    253 
    254 	if ((year < THE_EPOCH) || (year > END_OF_TIME) ||
    255 	    (mon < 1) || (mon > 12) ||
    256 	    (day < 1) || (day > 31))
    257 		return (-1);
    258 
    259 	/*
    260 	 * Figure seconds until this year and correct for leap years.
    261 	 * Note: 2000 is a leap year but not 2100.
    262 	 */
    263 	y = year - THE_EPOCH;
    264 	sum = y * SEC_PER_YEAR;
    265 	sum += ((y + 1) / 4) * SEC_PER_DAY;
    266 	/*
    267 	 * Point to the correct table for this year and
    268 	 * add in seconds until this month.
    269 	 */
    270 	cp = ((y + 2) % 4) ? cum_sec : cum_sec_leap;
    271 	sum += cp[mon - 1];
    272 	/*
    273 	 * Add in seconds until 0:00 of this day.
    274 	 * (days-per-month validation is not done here)
    275 	 */
    276 	sum += (day - 1) * SEC_PER_DAY;
    277 	sum -= (gmtoff * 15 * 60);
    278 	return (sum);
    279 }
    280 
    281 /*
    282  * Indicate whether the directory is valid.
    283  */
    284 
    285 int
    286 hsfs_valid_dir(hd)
    287 	struct hs_direntry *hd;
    288 {
    289 	/*
    290 	 * check to see if this directory is not marked as a directory.
    291 	 * check to see if data length is zero.
    292 	 */
    293 
    294 	if (hd->ext_size == 0)
    295 		return (0);
    296 
    297 	if (hd->type != VDIR)
    298 		return (0);
    299 
    300 	return (1);
    301 }
    302 
    303 
    304 
    305 /*
    306  * If we haven't complained about this error type yet, do.
    307  */
    308 void
    309 hs_log_bogus_disk_warning(fsp, errtype, data)
    310 	struct hsfs	*fsp;
    311 	int 		errtype;
    312 	uint_t		data;
    313 {
    314 
    315 	if (fsp->hsfs_err_flags & (1 << errtype))
    316 		return;		/* already complained */
    317 
    318 	cmn_err(CE_NOTE, hsfs_error[errtype].hdr_text,
    319 		fsp->hsfs_fsmnt);
    320 
    321 	switch (hsfs_error[errtype].n_printf_args) {
    322 	case 0:
    323 		cmn_err(CE_CONT, hsfs_error[errtype].err_text);
    324 		break;
    325 	case 1:
    326 		cmn_err(CE_CONT, hsfs_error[errtype].err_text, data);
    327 		break;
    328 	default:
    329 		/* don't currently handle more than 1 arg */
    330 		cmn_err(CE_CONT, "unknown problem; internal error.\n");
    331 	}
    332 	cmn_err(CE_CONT,
    333 "Due to this error, the file system may not be correctly interpreted.\n");
    334 	if (hsfs_error[errtype].multiple)
    335 		cmn_err(CE_CONT,
    336 "Other such errors in this file system will be silently ignored.\n\n");
    337 	else
    338 		cmn_err(CE_CONT, "\n");
    339 
    340 	fsp->hsfs_err_flags |= (1 << errtype);
    341 }
    342 
    343 /*
    344  * Callback from kstat framework. Grab a snapshot of the current hsfs
    345  * counters and populate the kstats.
    346  */
    347 static int
    348 hsfs_kstats_update(kstat_t *ksp, int flag)
    349 {
    350 	struct hsfs *fsp;
    351 	kstat_named_t *knp;
    352 	uint64_t pages_lost;
    353 	uint64_t physical_read_bytes;
    354 	uint64_t cache_read_pages;
    355 	uint64_t readahead_bytes;
    356 	uint64_t coalesced_bytes;
    357 	uint64_t total_pages_requested;
    358 
    359 	if (flag != KSTAT_READ)
    360 		return (EACCES);
    361 
    362 	fsp = ksp->ks_private;
    363 	knp = ksp->ks_data;
    364 
    365 	mutex_enter(&(fsp->hqueue->strategy_lock));
    366 	mutex_enter(&(fsp->hqueue->hsfs_queue_lock));
    367 
    368 	cache_read_pages = fsp->cache_read_pages;
    369 	pages_lost = hsfs_lostpage;
    370 	physical_read_bytes = fsp->physical_read_bytes;
    371 	readahead_bytes =  fsp->readahead_bytes;
    372 	coalesced_bytes = fsp->coalesced_bytes;
    373 	total_pages_requested = fsp->total_pages_requested;
    374 
    375 	mutex_exit(&(fsp->hqueue->strategy_lock));
    376 	mutex_exit(&(fsp->hqueue->hsfs_queue_lock));
    377 
    378 	knp++;
    379 	(knp++)->value.ui64 = pages_lost;
    380 	(knp++)->value.ui64 = howmany(physical_read_bytes, PAGESIZE);
    381 	(knp++)->value.ui64 = cache_read_pages;
    382 	(knp++)->value.ui64 = howmany(readahead_bytes, PAGESIZE);
    383 	(knp++)->value.ui64 = howmany(coalesced_bytes, PAGESIZE);
    384 	(knp++)->value.ui64 = total_pages_requested;
    385 
    386 	return (0);
    387 }
    388 
    389 /*
    390  * Initialize hsfs kstats, which are all name value pairs with
    391  * values being various counters.
    392  */
    393 static kstat_t *
    394 hsfs_setup_named_kstats(struct hsfs *fsp, int fsid, char *name,
    395     const hsfs_ksindex_t *ksip, int (*update)(kstat_t *, int))
    396 {
    397 	kstat_t *ksp;
    398 	kstat_named_t *knp;
    399 	char *np;
    400 	char *mntpt = fsp->hsfs_fsmnt;
    401 	size_t size;
    402 
    403 	size = (sizeof (hsfs_kstats)) / (sizeof (hsfs_ksindex_t));
    404 	ksp = kstat_create("hsfs_fs", fsid, name, "hsfs",
    405 	    KSTAT_TYPE_NAMED, size-1, KSTAT_FLAG_VIRTUAL);
    406 	if (ksp == NULL)
    407 		return (NULL);
    408 
    409 	ksp->ks_data = kmem_alloc(sizeof (kstat_named_t) * size, KM_SLEEP);
    410 	ksp->ks_private = fsp;
    411 	ksp->ks_update = update;
    412 	ksp->ks_data_size += strlen(mntpt) + 1;
    413 	knp = ksp->ks_data;
    414 	kstat_named_init(knp, ksip->name, KSTAT_DATA_STRING);
    415 	kstat_named_setstr(knp, mntpt);
    416 	knp++;
    417 	ksip++;
    418 
    419 	for (; (np = ksip->name) != NULL; ++knp, ++ksip) {
    420 		kstat_named_init(knp, np, KSTAT_DATA_UINT64);
    421 	}
    422 	kstat_install(ksp);
    423 
    424 	return (ksp);
    425 }
    426 
    427 void
    428 hsfs_init_kstats(struct hsfs *fsp, int fsid)
    429 {
    430 	fsp->hsfs_kstats = hsfs_setup_named_kstats(fsp, fsid, "hsfs_read_stats",
    431 	    hsfs_kstats, hsfs_kstats_update);
    432 }
    433 
    434 void
    435 hsfs_fini_kstats(struct hsfs *fsp)
    436 {
    437 	void *data;
    438 
    439 	if (fsp->hsfs_kstats != NULL) {
    440 		data = fsp->hsfs_kstats->ks_data;
    441 		kstat_delete(fsp->hsfs_kstats);
    442 		kmem_free(data, sizeof (kstat_named_t) *
    443 		    (sizeof (hsfs_kstats)) / (sizeof (hsfs_ksindex_t)));
    444 	}
    445 	fsp->hsfs_kstats = NULL;
    446 }
    447