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