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 }