1 /* $NetBSD: hfs_subr.c,v 1.6 2007/11/26 19:01:45 pooka Exp $ */ 2 3 /*- 4 * Copyright (c) 2005, 2007 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Yevgeny Binder and Dieter Baron. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: hfs_subr.c,v 1.6 2007/11/26 19:01:45 pooka Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/time.h> 38 #include <sys/kernel.h> 39 #include <sys/proc.h> 40 #include <sys/vnode.h> 41 #include <sys/malloc.h> 42 #include <sys/stat.h> 43 #include <sys/file.h> 44 #include <sys/filedesc.h> 45 #include <sys/mount.h> 46 #include <sys/disklabel.h> 47 #include <sys/conf.h> 48 #include <sys/kauth.h> 49 50 #include <fs/hfs/hfs.h> 51 52 /* 53 * Initialize the vnode associated with a new hfsnode. 54 */ 55 void 56 hfs_vinit(struct mount *mp, int (**specops)(void *), int (**fifoops)(void *), 57 struct vnode **vpp) 58 { 59 struct hfsnode *hp; 60 struct vnode *vp; 61 struct vnode *nvp; 62 63 vp = *vpp; 64 hp = VTOH(vp); 65 66 vp->v_type = hfs_catalog_keyed_record_vtype( 67 (hfs_catalog_keyed_record_t *)&hp->h_rec); 68 69 switch(vp->v_type) { 70 case VCHR: 71 case VBLK: 72 vp->v_op = specops; 73 if ((nvp = checkalias(vp, 74 HFS_CONVERT_RDEV(hp->h_rec.file.bsd.special.raw_device), 75 mp)) != NULL) { 76 /* 77 * Discard unneeded vnode, but save its inode. 78 */ 79 nvp->v_data = vp->v_data; 80 vp->v_data = NULL; 81 /* XXX spec_vnodeops has no locking, 82 do it explicitly */ 83 vp->v_vflag &= ~VV_LOCKSWORK; 84 VOP_UNLOCK(vp, 0); 85 vp->v_op = specops; 86 vrele(vp); 87 vgone(vp); 88 lockmgr(&nvp->v_lock, LK_EXCLUSIVE, 89 &nvp->v_interlock); 90 /* 91 * Reinitialize aliased inode. 92 */ 93 vp = nvp; 94 hp->h_vnode = vp; 95 } 96 break; 97 case VFIFO: 98 vp->v_op = fifoops; 99 break; 100 101 case VNON: 102 case VBAD: 103 case VSOCK: 104 case VDIR: 105 case VREG: 106 case VLNK: 107 break; 108 } 109 110 if (hp->h_rec.cnid == HFS_CNID_ROOT_FOLDER) 111 vp->v_vflag |= VV_ROOT; 112 113 *vpp = vp; 114 } 115 116 /* 117 * Callbacks for libhfs 118 */ 119 120 void 121 hfs_libcb_error( 122 const char* format, 123 const char* file, 124 int line, 125 va_list args) 126 { 127 #ifdef HFS_DEBUG 128 if (file != NULL) 129 printf("%s:%i: ", file, line); 130 else 131 printf("hfs: "); 132 #else 133 printf("hfs: "); 134 #endif 135 136 /* XXX Should we really display this if debugging is off? */ 137 vprintf(format, args); 138 printf("\n"); 139 } 140 141 /* XXX change malloc/realloc/free to use pools */ 142 143 void* 144 hfs_libcb_malloc(size_t size, hfs_callback_args* cbargs) 145 { 146 return malloc(size, /*M_HFSMNT*/ M_TEMP, M_WAITOK); 147 } 148 149 void* 150 hfs_libcb_realloc(void* ptr, size_t size, hfs_callback_args* cbargs) 151 { 152 return realloc(ptr, size, /*M_HFSMNT*/ M_TEMP, M_WAITOK); 153 } 154 155 void 156 hfs_libcb_free(void* ptr, hfs_callback_args* cbargs) 157 { 158 free(ptr, /*M_HFSMNT*/ M_TEMP); 159 } 160 161 /* 162 * hfs_libcb_opendev() 163 * 164 * hfslib uses this callback to open a volume's device node by name. However, 165 * by the time this is called here, the device node has already been opened by 166 * VFS. So we are passed the vnode to this volume's block device and use that 167 * instead of the device's name. 168 */ 169 int 170 hfs_libcb_opendev( 171 hfs_volume* vol, 172 const char* devname, 173 hfs_callback_args* cbargs) 174 { 175 hfs_libcb_data* cbdata = NULL; 176 hfs_libcb_argsopen* args; 177 struct partinfo dpart; 178 int result; 179 180 result = 0; 181 args = (hfs_libcb_argsopen*)(cbargs->openvol); 182 183 if (vol == NULL || devname == NULL) { 184 result = EINVAL; 185 goto error; 186 } 187 188 cbdata = malloc(sizeof(hfs_libcb_data), M_HFSMNT, M_WAITOK); 189 if (cbdata == NULL) { 190 result = ENOMEM; 191 goto error; 192 } 193 vol->cbdata = cbdata; 194 195 cbdata->devvp = NULL; 196 197 /* Open the device node. */ 198 if ((result = VOP_OPEN(args->devvp, vol->readonly? FREAD : FREAD|FWRITE, 199 FSCRED)) != 0) 200 goto error; 201 202 /* Flush out any old buffers remaining from a previous use. */ 203 vn_lock(args->devvp, LK_EXCLUSIVE | LK_RETRY); 204 result = vinvalbuf(args->devvp, V_SAVE, args->cred, args->l, 0, 0); 205 VOP_UNLOCK(args->devvp, 0); 206 if (result != 0) 207 goto error; 208 209 cbdata->devvp = args->devvp; 210 211 /* Determine the device's block size. Default to DEV_BSIZE if unavailable.*/ 212 if (VOP_IOCTL(args->devvp, DIOCGPART, &dpart, FREAD, args->cred) 213 != 0) 214 cbdata->devblksz = DEV_BSIZE; 215 else 216 cbdata->devblksz = dpart.disklab->d_secsize; 217 218 return 0; 219 220 error: 221 if (cbdata != NULL) { 222 if (cbdata->devvp != NULL) { 223 vn_lock(cbdata->devvp, LK_EXCLUSIVE | LK_RETRY); 224 (void)VOP_CLOSE(cbdata->devvp, vol->readonly ? FREAD : 225 FREAD | FWRITE, NOCRED); 226 VOP_UNLOCK(cbdata->devvp, 0); 227 } 228 free(cbdata, M_HFSMNT); 229 vol->cbdata = NULL; 230 } 231 232 return result; 233 } 234 235 void 236 hfs_libcb_closedev(hfs_volume* in_vol, hfs_callback_args* cbargs) 237 { 238 struct vnode *devvp; 239 240 if (in_vol == NULL) 241 return; 242 243 if (in_vol->cbdata != NULL) { 244 devvp = ((hfs_libcb_data*)in_vol->cbdata)->devvp; 245 if (devvp != NULL) { 246 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 247 (void)VOP_CLOSE(devvp, 248 in_vol->readonly ? FREAD : FREAD | FWRITE, NOCRED); 249 /* XXX do we need a VOP_UNLOCK() here? */ 250 } 251 252 free(in_vol->cbdata, M_HFSMNT); 253 in_vol->cbdata = NULL; 254 } 255 } 256 257 int 258 hfs_libcb_read( 259 hfs_volume* vol, 260 void* outbytes, 261 uint64_t length, 262 uint64_t offset, 263 hfs_callback_args* cbargs) 264 { 265 hfs_libcb_data *cbdata; 266 hfs_libcb_argsread* argsread; 267 kauth_cred_t cred; 268 uint64_t physoffset; /* physical offset from start of device(?) */ 269 270 if (vol == NULL || outbytes == NULL) 271 return -1; 272 273 cbdata = (hfs_libcb_data*)vol->cbdata; 274 275 if (cbargs != NULL 276 && (argsread = (hfs_libcb_argsread*)cbargs->read) != NULL 277 && argsread->cred != NULL) 278 cred = argsread->cred; 279 else 280 cred = NOCRED; 281 282 /* 283 * Since bread() only reads data in terms of integral blocks, it may have 284 * read some data before and/or after our desired offset & length. So when 285 * copying that data into the outgoing buffer, start at the actual desired 286 * offset and only copy the desired length. 287 */ 288 physoffset = offset + vol->offset; 289 290 return hfs_pread(cbdata->devvp, outbytes, cbdata->devblksz, physoffset, 291 length, cred); 292 } 293 294 /* 295 * So it turns out that bread() is pretty shoddy. It not only requires the size 296 * parameter to be an integral multiple of the device's block size, but also 297 * requires the block number to be on a boundary of that same block size -- and 298 * yet be given as an integral multiple of DEV_BSIZE! So after much toil and 299 * bloodshed, hfs_pread() was written as a convenience (and a model of how sane 300 * people take their bread()). Returns 0 on success. 301 */ 302 int 303 hfs_pread(struct vnode *vp, void *buf, size_t secsz, uint64_t off, 304 uint64_t len, kauth_cred_t cred) 305 { 306 struct buf *bp; 307 uint64_t curoff; /* relative to 'start' variable */ 308 uint64_t start; 309 int error; 310 311 if (vp == NULL || buf == NULL) 312 return EINVAL; 313 314 if (len == 0) 315 return 0; 316 317 curoff = 0; 318 error = 0; 319 320 /* align offset to highest preceding sector boundary */ 321 #define ABSZ(x, bsz) (((x)/(bsz))*(bsz)) 322 323 /* round size up to integral # of block sizes */ 324 #define RBSZ(x, bsz) (((x) + (bsz) - 1) & ~((bsz) - 1)) 325 326 start = ABSZ(off, secsz); 327 while (start + curoff < off + len) 328 { 329 bp = NULL; 330 331 /* XXX Does the algorithm always do what's intended here when 332 * XXX start != off? Need to test this. */ 333 334 error = bread(vp, (start + curoff) / DEV_BSIZE,/* no rounding involved*/ 335 RBSZ(min(len - curoff + (off - start), MAXBSIZE), secsz), cred, &bp); 336 337 if (error == 0) 338 memcpy((uint8_t*)buf + curoff, (uint8_t*)bp->b_data + 339 (off - start), min(len - curoff, MAXBSIZE - (off - start))); 340 341 if (bp != NULL) 342 brelse(bp, 0); 343 if (error != 0) 344 return error; 345 346 curoff += MAXBSIZE; 347 } 348 #undef ABSZ 349 #undef RBSZ 350 351 return 0; 352 } 353 354 /* XXX Provide a routine to take a catalog record and return its proper BSD file 355 * XXX or directory mode value */ 356 357 358 /* Convert from HFS+ time representation to UNIX time since epoch. */ 359 void 360 hfs_time_to_timespec(uint32_t hfstime, struct timespec *unixtime) 361 { 362 /* 363 * HFS+ time is calculated in seconds since midnight, Jan 1st, 1904. 364 * struct timespec counts from midnight, Jan 1st, 1970. Thus, there is 365 * precisely a 66 year difference between them, which is equal to 366 * 2,082,844,800 seconds. No, I didn't count them by hand. 367 */ 368 369 if (hfstime < 2082844800) 370 unixtime->tv_sec = 0; /* dates before 1970 are bs anyway, so use epoch*/ 371 else 372 unixtime->tv_sec = hfstime - 2082844800; 373 374 unixtime->tv_nsec = 0; /* we don't have nanosecond resolution */ 375 } 376 377 /* 378 * Endian conversion with automatic pointer incrementation. 379 */ 380 381 uint16_t be16tohp(void** inout_ptr) 382 { 383 uint16_t result; 384 uint16_t *ptr; 385 386 if(inout_ptr==NULL) 387 return 0; 388 389 ptr = *inout_ptr; 390 391 result = be16toh(*ptr); 392 393 ptr++; 394 *inout_ptr = ptr; 395 396 return result; 397 } 398 399 uint32_t be32tohp(void** inout_ptr) 400 { 401 uint32_t result; 402 uint32_t *ptr; 403 404 if(inout_ptr==NULL) 405 return 0; 406 407 ptr = *inout_ptr; 408 409 result = be32toh(*ptr); 410 411 ptr++; 412 *inout_ptr = ptr; 413 414 return result; 415 } 416 417 uint64_t be64tohp(void** inout_ptr) 418 { 419 uint64_t result; 420 uint64_t *ptr; 421 422 if(inout_ptr==NULL) 423 return 0; 424 425 ptr = *inout_ptr; 426 427 result = be64toh(*ptr); 428 429 ptr++; 430 *inout_ptr = ptr; 431 432 return result; 433 } 434 435 enum vtype 436 hfs_catalog_keyed_record_vtype(const hfs_catalog_keyed_record_t *rec) 437 { 438 if (rec->type == HFS_REC_FILE) { 439 uint32_t mode; 440 441 mode = ((const hfs_file_record_t *)rec)->bsd.file_mode; 442 if (mode != 0) 443 return IFTOVT(mode); 444 else 445 return VREG; 446 } 447 else 448 return VDIR; 449 } 450