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
hs_parse_dirdate(dp,tvp)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
hs_parse_longdate(dp,tvp)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
hs_date_to_gmtime(year,mon,day,gmtoff)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
hsfs_valid_dir(hd)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
hs_log_bogus_disk_warning(fsp,errtype,data)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
hsfs_kstats_update(kstat_t * ksp,int flag)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 *
hsfs_setup_named_kstats(struct hsfs * fsp,int fsid,char * name,const hsfs_ksindex_t * ksip,int (* update)(kstat_t *,int))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
hsfs_init_kstats(struct hsfs * fsp,int fsid)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
hsfs_fini_kstats(struct hsfs * fsp)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