1 /* $NetBSD: hfs_subr.c,v 1.9 2008/05/16 09:21:59 hannken 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.9 2008/05/16 09:21:59 hannken 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 #include <miscfs/specfs/specdev.h> 53 54 /* 55 * Initialize the vnode associated with a new hfsnode. 56 */ 57 void 58 hfs_vinit(struct mount *mp, int (**specops)(void *), int (**fifoops)(void *), 59 struct vnode **vpp) 60 { 61 struct hfsnode *hp; 62 struct vnode *vp; 63 64 vp = *vpp; 65 hp = VTOH(vp); 66 67 vp->v_type = hfs_catalog_keyed_record_vtype( 68 (hfs_catalog_keyed_record_t *)&hp->h_rec); 69 70 switch(vp->v_type) { 71 case VCHR: 72 case VBLK: 73 vp->v_op = specops; 74 spec_node_init(vp, 75 HFS_CONVERT_RDEV(hp->h_rec.file.bsd.special.raw_device)); 76 break; 77 case VFIFO: 78 vp->v_op = fifoops; 79 break; 80 81 case VNON: 82 case VBAD: 83 case VSOCK: 84 case VDIR: 85 case VREG: 86 case VLNK: 87 break; 88 } 89 90 if (hp->h_rec.cnid == HFS_CNID_ROOT_FOLDER) 91 vp->v_vflag |= VV_ROOT; 92 93 *vpp = vp; 94 } 95 96 /* 97 * Callbacks for libhfs 98 */ 99 100 void 101 hfs_libcb_error( 102 const char* format, 103 const char* file, 104 int line, 105 va_list args) 106 { 107 #ifdef HFS_DEBUG 108 if (file != NULL) 109 printf("%s:%i: ", file, line); 110 else 111 printf("hfs: "); 112 #else 113 printf("hfs: "); 114 #endif 115 116 /* XXX Should we really display this if debugging is off? */ 117 vprintf(format, args); 118 printf("\n"); 119 } 120 121 /* XXX change malloc/realloc/free to use pools */ 122 123 void* 124 hfs_libcb_malloc(size_t size, hfs_callback_args* cbargs) 125 { 126 return malloc(size, /*M_HFSMNT*/ M_TEMP, M_WAITOK); 127 } 128 129 void* 130 hfs_libcb_realloc(void* ptr, size_t size, hfs_callback_args* cbargs) 131 { 132 return realloc(ptr, size, /*M_HFSMNT*/ M_TEMP, M_WAITOK); 133 } 134 135 void 136 hfs_libcb_free(void* ptr, hfs_callback_args* cbargs) 137 { 138 free(ptr, /*M_HFSMNT*/ M_TEMP); 139 } 140 141 /* 142 * hfs_libcb_opendev() 143 * 144 * hfslib uses this callback to open a volume's device node by name. However, 145 * by the time this is called here, the device node has already been opened by 146 * VFS. So we are passed the vnode to this volume's block device and use that 147 * instead of the device's name. 148 */ 149 int 150 hfs_libcb_opendev( 151 hfs_volume* vol, 152 const char* devname, 153 hfs_callback_args* cbargs) 154 { 155 hfs_libcb_data* cbdata = NULL; 156 hfs_libcb_argsopen* args; 157 struct partinfo dpart; 158 int result; 159 160 result = 0; 161 args = (hfs_libcb_argsopen*)(cbargs->openvol); 162 163 if (vol == NULL || devname == NULL) { 164 result = EINVAL; 165 goto error; 166 } 167 168 cbdata = malloc(sizeof(hfs_libcb_data), M_HFSMNT, M_WAITOK); 169 if (cbdata == NULL) { 170 result = ENOMEM; 171 goto error; 172 } 173 vol->cbdata = cbdata; 174 175 cbdata->devvp = NULL; 176 177 /* Open the device node. */ 178 if ((result = VOP_OPEN(args->devvp, vol->readonly? FREAD : FREAD|FWRITE, 179 FSCRED)) != 0) 180 goto error; 181 182 /* Flush out any old buffers remaining from a previous use. */ 183 vn_lock(args->devvp, LK_EXCLUSIVE | LK_RETRY); 184 result = vinvalbuf(args->devvp, V_SAVE, args->cred, args->l, 0, 0); 185 VOP_UNLOCK(args->devvp, 0); 186 if (result != 0) 187 goto error; 188 189 cbdata->devvp = args->devvp; 190 191 /* Determine the device's block size. Default to DEV_BSIZE if unavailable.*/ 192 if (VOP_IOCTL(args->devvp, DIOCGPART, &dpart, FREAD, args->cred) 193 != 0) 194 cbdata->devblksz = DEV_BSIZE; 195 else 196 cbdata->devblksz = dpart.disklab->d_secsize; 197 198 return 0; 199 200 error: 201 if (cbdata != NULL) { 202 if (cbdata->devvp != NULL) { 203 vn_lock(cbdata->devvp, LK_EXCLUSIVE | LK_RETRY); 204 (void)VOP_CLOSE(cbdata->devvp, vol->readonly ? FREAD : 205 FREAD | FWRITE, NOCRED); 206 VOP_UNLOCK(cbdata->devvp, 0); 207 } 208 free(cbdata, M_HFSMNT); 209 vol->cbdata = NULL; 210 } 211 212 return result; 213 } 214 215 void 216 hfs_libcb_closedev(hfs_volume* in_vol, hfs_callback_args* cbargs) 217 { 218 struct vnode *devvp; 219 220 if (in_vol == NULL) 221 return; 222 223 if (in_vol->cbdata != NULL) { 224 devvp = ((hfs_libcb_data*)in_vol->cbdata)->devvp; 225 if (devvp != NULL) { 226 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 227 (void)VOP_CLOSE(devvp, 228 in_vol->readonly ? FREAD : FREAD | FWRITE, NOCRED); 229 /* XXX do we need a VOP_UNLOCK() here? */ 230 } 231 232 free(in_vol->cbdata, M_HFSMNT); 233 in_vol->cbdata = NULL; 234 } 235 } 236 237 int 238 hfs_libcb_read( 239 hfs_volume* vol, 240 void* outbytes, 241 uint64_t length, 242 uint64_t offset, 243 hfs_callback_args* cbargs) 244 { 245 hfs_libcb_data *cbdata; 246 hfs_libcb_argsread* argsread; 247 kauth_cred_t cred; 248 uint64_t physoffset; /* physical offset from start of device(?) */ 249 250 if (vol == NULL || outbytes == NULL) 251 return -1; 252 253 cbdata = (hfs_libcb_data*)vol->cbdata; 254 255 if (cbargs != NULL 256 && (argsread = (hfs_libcb_argsread*)cbargs->read) != NULL 257 && argsread->cred != NULL) 258 cred = argsread->cred; 259 else 260 cred = NOCRED; 261 262 /* 263 * Since bread() only reads data in terms of integral blocks, it may have 264 * read some data before and/or after our desired offset & length. So when 265 * copying that data into the outgoing buffer, start at the actual desired 266 * offset and only copy the desired length. 267 */ 268 physoffset = offset + vol->offset; 269 270 return hfs_pread(cbdata->devvp, outbytes, cbdata->devblksz, physoffset, 271 length, cred); 272 } 273 274 /* 275 * So it turns out that bread() is pretty shoddy. It not only requires the size 276 * parameter to be an integral multiple of the device's block size, but also 277 * requires the block number to be on a boundary of that same block size -- and 278 * yet be given as an integral multiple of DEV_BSIZE! So after much toil and 279 * bloodshed, hfs_pread() was written as a convenience (and a model of how sane 280 * people take their bread()). Returns 0 on success. 281 */ 282 int 283 hfs_pread(struct vnode *vp, void *buf, size_t secsz, uint64_t off, 284 uint64_t len, kauth_cred_t cred) 285 { 286 struct buf *bp; 287 uint64_t curoff; /* relative to 'start' variable */ 288 uint64_t start; 289 int error; 290 291 if (vp == NULL || buf == NULL) 292 return EINVAL; 293 294 if (len == 0) 295 return 0; 296 297 curoff = 0; 298 error = 0; 299 300 /* align offset to highest preceding sector boundary */ 301 #define ABSZ(x, bsz) (((x)/(bsz))*(bsz)) 302 303 /* round size up to integral # of block sizes */ 304 #define RBSZ(x, bsz) (((x) + (bsz) - 1) & ~((bsz) - 1)) 305 306 start = ABSZ(off, secsz); 307 while (start + curoff < off + len) 308 { 309 bp = NULL; 310 311 /* XXX Does the algorithm always do what's intended here when 312 * XXX start != off? Need to test this. */ 313 314 error = bread(vp, (start + curoff) / DEV_BSIZE,/* no rounding involved*/ 315 RBSZ(min(len - curoff + (off - start), MAXBSIZE), secsz), 316 cred, 0, &bp); 317 318 if (error == 0) 319 memcpy((uint8_t*)buf + curoff, (uint8_t*)bp->b_data + 320 (off - start), min(len - curoff, MAXBSIZE - (off - start))); 321 322 if (bp != NULL) 323 brelse(bp, 0); 324 if (error != 0) 325 return error; 326 327 curoff += MAXBSIZE; 328 } 329 #undef ABSZ 330 #undef RBSZ 331 332 return 0; 333 } 334 335 /* XXX Provide a routine to take a catalog record and return its proper BSD file 336 * XXX or directory mode value */ 337 338 339 /* Convert from HFS+ time representation to UNIX time since epoch. */ 340 void 341 hfs_time_to_timespec(uint32_t hfstime, struct timespec *unixtime) 342 { 343 /* 344 * HFS+ time is calculated in seconds since midnight, Jan 1st, 1904. 345 * struct timespec counts from midnight, Jan 1st, 1970. Thus, there is 346 * precisely a 66 year difference between them, which is equal to 347 * 2,082,844,800 seconds. No, I didn't count them by hand. 348 */ 349 350 if (hfstime < 2082844800) 351 unixtime->tv_sec = 0; /* dates before 1970 are bs anyway, so use epoch*/ 352 else 353 unixtime->tv_sec = hfstime - 2082844800; 354 355 unixtime->tv_nsec = 0; /* we don't have nanosecond resolution */ 356 } 357 358 /* 359 * Endian conversion with automatic pointer incrementation. 360 */ 361 362 uint16_t be16tohp(void** inout_ptr) 363 { 364 uint16_t result; 365 uint16_t *ptr; 366 367 if(inout_ptr==NULL) 368 return 0; 369 370 ptr = *inout_ptr; 371 372 result = be16toh(*ptr); 373 374 ptr++; 375 *inout_ptr = ptr; 376 377 return result; 378 } 379 380 uint32_t be32tohp(void** inout_ptr) 381 { 382 uint32_t result; 383 uint32_t *ptr; 384 385 if(inout_ptr==NULL) 386 return 0; 387 388 ptr = *inout_ptr; 389 390 result = be32toh(*ptr); 391 392 ptr++; 393 *inout_ptr = ptr; 394 395 return result; 396 } 397 398 uint64_t be64tohp(void** inout_ptr) 399 { 400 uint64_t result; 401 uint64_t *ptr; 402 403 if(inout_ptr==NULL) 404 return 0; 405 406 ptr = *inout_ptr; 407 408 result = be64toh(*ptr); 409 410 ptr++; 411 *inout_ptr = ptr; 412 413 return result; 414 } 415 416 enum vtype 417 hfs_catalog_keyed_record_vtype(const hfs_catalog_keyed_record_t *rec) 418 { 419 if (rec->type == HFS_REC_FILE) { 420 uint32_t mode; 421 422 mode = ((const hfs_file_record_t *)rec)->bsd.file_mode; 423 if (mode != 0) 424 return IFTOVT(mode); 425 else 426 return VREG; 427 } 428 else 429 return VDIR; 430 } 431