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