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, size_t size, 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 402 size /= sizeof (hsfs_ksindex_t); 403 ksp = kstat_create("hsfs_fs", fsid, name, "hsfs", 404 KSTAT_TYPE_NAMED, size-1, KSTAT_FLAG_VIRTUAL); 405 if (ksp == NULL) 406 return (NULL); 407 408 ksp->ks_data = kmem_alloc(sizeof (kstat_named_t) * size, KM_SLEEP); 409 ksp->ks_private = fsp; 410 ksp->ks_update = update; 411 ksp->ks_data_size += strlen(mntpt) + 1; 412 knp = ksp->ks_data; 413 kstat_named_init(knp, ksip->name, KSTAT_DATA_STRING); 414 kstat_named_setstr(knp, mntpt); 415 knp++; 416 ksip++; 417 418 for (; (np = ksip->name) != NULL; ++knp, ++ksip) { 419 kstat_named_init(knp, np, KSTAT_DATA_UINT64); 420 } 421 kstat_install(ksp); 422 423 return (ksp); 424 } 425 426 void 427 hsfs_init_kstats(struct hsfs *fsp, int fsid) 428 { 429 fsp->hsfs_kstats = hsfs_setup_named_kstats(fsp, fsid, "hsfs_read_stats", 430 hsfs_kstats, sizeof (hsfs_kstats), hsfs_kstats_update); 431 } 432 433 void 434 hsfs_fini_kstats(struct hsfs *fsp) 435 { 436 void *data; 437 438 if (fsp->hsfs_kstats != NULL) { 439 data = fsp->hsfs_kstats->ks_data; 440 kstat_delete(fsp->hsfs_kstats); 441 kmem_free(data, (sizeof (hsfs_kstats)) / \ 442 (sizeof (hsfs_ksindex_t))); 443 } 444 fsp->hsfs_kstats = NULL; 445 } 446