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.
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.
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]
22 * Miscellaneous support subroutines for High Sierra filesystem
24 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
28 #pragma ident "%Z%%M% %I% %E% SMI"
30 #include <sys/types.h>
31 #include <sys/param.h>
33 #include <sys/cmn_err.h>
34 #include <sys/systm.h>
35 #include <sys/sysmacros.h>
40 #include <sys/vnode.h>
42 #include <sys/debug.h>
50 #include <vm/seg_map.h>
52 #include <vm/seg_kmem.h>
54 #include <sys/fs/hsfs_spec.h>
55 #include <sys/fs/hsfs_node.h>
56 #include <sys/fs/hsfs_impl.h>
58 #define THE_EPOCH 1970
59 #define END_OF_TIME 2099
60 extern int hsfs_lostpage
;
63 static time_t hs_date_to_gmtime(int year
, int mon
, int day
, int gmtoff
);
65 static time_t hs_date_to_gmtime();
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).
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 */
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",
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",
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",
94 /* HSFS_ERR_UNSUP_TYPE */
95 "hsfs: Warning: the file system mounted on %s "
96 "contains a file or directory with an unsupported type:",
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",
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",
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",
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",
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",
124 /* HSFS_ERR_BAD_SUA_LEN */
125 "hsfs: Warning: file system mounted on %s "
126 "has inconsistent Rock Ridge data:",
132 * Local datatype for defining tables of (Offset, Name) pairs for
140 static const hsfs_ksindex_t hsfs_kstats
[] = {
143 { 2, "physical_read_pages" },
144 { 3, "cache_read_pages" },
145 { 4, "readahead_pages" },
146 { 5, "coalesced_pages" },
147 { 6, "total_pages_requested" },
154 * Parse the short 'directory-format' date into a Unix timeval.
155 * This is the date format used in Directory Entries.
157 * If the date is not representable, make something up.
160 hs_parse_dirdate(dp
, tvp
)
164 int year
, month
, day
, hour
, minute
, sec
, gmtoff
;
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
);
175 if (year
< THE_EPOCH
) {
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
;
191 * Parse the long 'user-oriented' date into a Unix timeval.
192 * This is the date format used in the Volume Descriptor.
194 * If the date is not representable, make something up.
197 hs_parse_longdate(dp
, tvp
)
201 int year
, month
, day
, hour
, minute
, sec
, gmtoff
;
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
);
212 if (year
< THE_EPOCH
) {
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;
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
229 static time_t cum_sec_leap
[] = {
230 0x0, 0x28de80, 0x4f1a00, 0x77f880, 0x9f8580, 0xc86400,
231 0xeff100, 0x118cf80, 0x141ae00, 0x1693b00, 0x1921980, 0x1b9a680
233 #define SEC_PER_DAY 0x15180
234 #define SEC_PER_YEAR 0x1e13380
239 * Convert year(1970-2099)/month(1-12)/day(1-31) to seconds-since-1970/1/1.
241 * Returns -1 if the date is out of range.
244 hs_date_to_gmtime(year
, mon
, day
, gmtoff
)
254 if ((year
< THE_EPOCH
) || (year
> END_OF_TIME
) ||
255 (mon
< 1) || (mon
> 12) ||
256 (day
< 1) || (day
> 31))
260 * Figure seconds until this year and correct for leap years.
261 * Note: 2000 is a leap year but not 2100.
263 y
= year
- THE_EPOCH
;
264 sum
= y
* SEC_PER_YEAR
;
265 sum
+= ((y
+ 1) / 4) * SEC_PER_DAY
;
267 * Point to the correct table for this year and
268 * add in seconds until this month.
270 cp
= ((y
+ 2) % 4) ? cum_sec
: cum_sec_leap
;
273 * Add in seconds until 0:00 of this day.
274 * (days-per-month validation is not done here)
276 sum
+= (day
- 1) * SEC_PER_DAY
;
277 sum
-= (gmtoff
* 15 * 60);
282 * Indicate whether the directory is valid.
287 struct hs_direntry
*hd
;
290 * check to see if this directory is not marked as a directory.
291 * check to see if data length is zero.
294 if (hd
->ext_size
== 0)
297 if (hd
->type
!= VDIR
)
306 * If we haven't complained about this error type yet, do.
309 hs_log_bogus_disk_warning(fsp
, errtype
, data
)
315 if (fsp
->hsfs_err_flags
& (1 << errtype
))
316 return; /* already complained */
318 cmn_err(CE_NOTE
, hsfs_error
[errtype
].hdr_text
,
321 switch (hsfs_error
[errtype
].n_printf_args
) {
323 cmn_err(CE_CONT
, hsfs_error
[errtype
].err_text
);
326 cmn_err(CE_CONT
, hsfs_error
[errtype
].err_text
, data
);
329 /* don't currently handle more than 1 arg */
330 cmn_err(CE_CONT
, "unknown problem; internal error.\n");
333 "Due to this error, the file system may not be correctly interpreted.\n");
334 if (hsfs_error
[errtype
].multiple
)
336 "Other such errors in this file system will be silently ignored.\n\n");
338 cmn_err(CE_CONT
, "\n");
340 fsp
->hsfs_err_flags
|= (1 << errtype
);
344 * Callback from kstat framework. Grab a snapshot of the current hsfs
345 * counters and populate the kstats.
348 hsfs_kstats_update(kstat_t
*ksp
, int flag
)
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
;
359 if (flag
!= KSTAT_READ
)
362 fsp
= ksp
->ks_private
;
365 mutex_enter(&(fsp
->hqueue
->strategy_lock
));
366 mutex_enter(&(fsp
->hqueue
->hsfs_queue_lock
));
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
;
375 mutex_exit(&(fsp
->hqueue
->strategy_lock
));
376 mutex_exit(&(fsp
->hqueue
->hsfs_queue_lock
));
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
;
390 * Initialize hsfs kstats, which are all name value pairs with
391 * values being various counters.
394 hsfs_setup_named_kstats(struct hsfs
*fsp
, int fsid
, char *name
,
395 const hsfs_ksindex_t
*ksip
, int (*update
)(kstat_t
*, int))
400 char *mntpt
= fsp
->hsfs_fsmnt
;
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
);
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;
414 kstat_named_init(knp
, ksip
->name
, KSTAT_DATA_STRING
);
415 kstat_named_setstr(knp
, mntpt
);
419 for (; (np
= ksip
->name
) != NULL
; ++knp
, ++ksip
) {
420 kstat_named_init(knp
, np
, KSTAT_DATA_UINT64
);
428 hsfs_init_kstats(struct hsfs
*fsp
, int fsid
)
430 fsp
->hsfs_kstats
= hsfs_setup_named_kstats(fsp
, fsid
, "hsfs_read_stats",
431 hsfs_kstats
, hsfs_kstats_update
);
435 hsfs_fini_kstats(struct hsfs
*fsp
)
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
)));
445 fsp
->hsfs_kstats
= NULL
;