1/* 2 * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 3 * Copyright (c) 1990,1991 Regents of The University of Michigan. 4 * All Rights Reserved. 5 * 6 * Permission to use, copy, modify, and distribute this software and 7 * its documentation for any purpose and without fee is hereby granted, 8 * provided that the above copyright notice appears in all copies and 9 * that both that copyright notice and this permission notice appear 10 * in supporting documentation, and that the name of The University 11 * of Michigan not be used in advertising or publicity pertaining to 12 * distribution of the software without specific, written prior 13 * permission. This software is supplied as is without expressed or 14 * implied warranties of any kind. 15 * 16 * Research Systems Unix Group 17 * The University of Michigan 18 * c/o Mike Clark 19 * 535 W. William Street 20 * Ann Arbor, Michigan 21 * +1-313-763-0525 22 * netatalk@itd.umich.edu 23 * 24 */ 25 26/*! 27 * @file 28 * Part of Netatalk's AppleDouble implementatation 29 * @note We don't use inlines because a good compiler should be 30 * able to optimize all the static funcs below. 31 * @sa include/atalk/adouble.h 32 */ 33 34#ifdef HAVE_CONFIG_H 35#include "config.h" 36#endif /* HAVE_CONFIG_H */ 37 38#include <errno.h> 39 40#include <atalk/adouble.h> 41#include <sys/param.h> 42#include <atalk/logger.h> 43 44#include <atalk/util.h> 45#include <string.h> 46 47#include "ad_private.h" 48#include <stdlib.h> 49 50#ifndef MAX 51#define MAX(a, b) ((a) < (b) ? (b) : (a)) 52#endif /* ! MAX */ 53 54/* 55 * AppleDouble entry default offsets. 56 * The layout looks like this: 57 * 58 * this is the v1 layout: 59 * 255 200 16 32 N 60 * | NAME | COMMENT | FILEI | FINDERI | RFORK | 61 * 62 * we need to change it to look like this: 63 * 64 * v2 layout: 65 * field length (in bytes) 66 * NAME 255 67 * COMMENT 200 68 * FILEDATESI 16 replaces FILEI 69 * FINDERI 32 70 * DID 4 new 71 * AFPFILEI 4 new 72 * SHORTNAME 12 8.3 new 73 * RFORK N 74 * 75 * so, all we need to do is replace FILEI with FILEDATESI, move RFORK, 76 * and add in the new fields. 77 * 78 * NOTE: the HFS module will need similar modifications to interact with 79 * afpd correctly. 80 */ 81 82#define ADEDOFF_MAGIC (0) 83#define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC) 84#define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION) 85#define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER) 86 87/* initial lengths of some of the fields */ 88#define ADEDLEN_INIT 0 89 90/* make sure we don't redefine ADEDOFF_FILEI */ 91#ifdef ADEDOFF_FILEI 92#undef ADEDOFF_FILEI 93#endif /* ADEDOFF_FILEI */ 94 95#define ADEDOFF_NAME_V1 (AD_HEADER_LEN + ADEID_NUM_V1*AD_ENTRY_LEN) 96#define ADEDOFF_COMMENT_V1 (ADEDOFF_NAME_V1 + ADEDLEN_NAME) 97#define ADEDOFF_FILEI (ADEDOFF_COMMENT_V1 + ADEDLEN_COMMENT) 98#define ADEDOFF_FINDERI_V1 (ADEDOFF_FILEI + ADEDLEN_FILEI) 99#define ADEDOFF_RFORK_V1 (ADEDOFF_FINDERI_V1 + ADEDLEN_FINDERI) 100 101/* i stick things in a slightly different order than their eid order in 102 * case i ever want to separate RootInfo behaviour from the rest of the 103 * stuff. */ 104#define ADEDOFF_NAME_V2 (AD_HEADER_LEN + ADEID_NUM_V2*AD_ENTRY_LEN) 105#define ADEDOFF_COMMENT_V2 (ADEDOFF_NAME_V2 + ADEDLEN_NAME) 106#define ADEDOFF_FILEDATESI (ADEDOFF_COMMENT_V2 + ADEDLEN_COMMENT) 107#define ADEDOFF_FINDERI_V2 (ADEDOFF_FILEDATESI + ADEDLEN_FILEDATESI) 108#define ADEDOFF_DID (ADEDOFF_FINDERI_V2 + ADEDLEN_FINDERI) 109#define ADEDOFF_AFPFILEI (ADEDOFF_DID + ADEDLEN_DID) 110#define ADEDOFF_SHORTNAME (ADEDOFF_AFPFILEI + ADEDLEN_AFPFILEI) 111#define ADEDOFF_PRODOSFILEI (ADEDOFF_SHORTNAME + ADEDLEN_SHORTNAME) 112#define ADEDOFF_PRIVDEV (ADEDOFF_PRODOSFILEI + ADEDLEN_PRODOSFILEI) 113#define ADEDOFF_PRIVINO (ADEDOFF_PRIVDEV + ADEDLEN_PRIVDEV) 114#define ADEDOFF_PRIVSYN (ADEDOFF_PRIVINO + ADEDLEN_PRIVINO) 115#define ADEDOFF_PRIVID (ADEDOFF_PRIVSYN + ADEDLEN_PRIVSYN) 116 117#define ADEDOFF_RFORK_V2 (ADEDOFF_PRIVID + ADEDLEN_PRIVID) 118 119#define ADEID_NUM_OSX 2 120#define ADEDOFF_FINDERI_OSX (AD_HEADER_LEN + ADEID_NUM_OSX*AD_ENTRY_LEN) 121#define ADEDOFF_RFORK_OSX (ADEDOFF_FINDERI_OSX + ADEDLEN_FINDERI) 122 123/* we keep local copies of a bunch of stuff so that we can initialize things 124 * correctly. */ 125 126/* this is to prevent changing timezones from causing problems with 127 localtime volumes. the screw-up is 30 years. we use a delta of 5 128 years. */ 129#define TIMEWARP_DELTA 157680000 130 131 132struct entry { 133 u_int32_t id, offset, len; 134}; 135 136static const struct entry entry_order1[ADEID_NUM_V1 +1] = { 137 {ADEID_NAME, ADEDOFF_NAME_V1, ADEDLEN_INIT}, /* 3 */ 138 {ADEID_COMMENT, ADEDOFF_COMMENT_V1, ADEDLEN_INIT}, /* 4 */ 139 {ADEID_FILEI, ADEDOFF_FILEI, ADEDLEN_FILEI}, /* 7 */ 140 {ADEID_FINDERI, ADEDOFF_FINDERI_V1, ADEDLEN_FINDERI}, /* 9 */ 141 {ADEID_RFORK, ADEDOFF_RFORK_V1, ADEDLEN_INIT}, /* 2 */ 142 {0, 0, 0} 143}; 144 145#if AD_VERSION == AD_VERSION1 146#define DISK_EID(ad, a) (a) 147 148#else /* AD_VERSION == AD_VERSION2 */ 149 150static u_int32_t get_eid(struct adouble *ad, u_int32_t eid) 151{ 152 if (eid <= 15) 153 return eid; 154 if (ad->ad_version == AD_VERSION1) 155 return 0; 156 if (eid == AD_DEV) 157 return ADEID_PRIVDEV; 158 if (eid == AD_INO) 159 return ADEID_PRIVINO; 160 if (eid == AD_SYN) 161 return ADEID_PRIVSYN; 162 if (eid == AD_ID) 163 return ADEID_PRIVID; 164 165 return 0; 166} 167 168#define DISK_EID(ad, a) get_eid(ad, a) 169 170static const struct entry entry_order2[ADEID_NUM_V2 +1] = { 171 {ADEID_NAME, ADEDOFF_NAME_V2, ADEDLEN_INIT}, 172 {ADEID_COMMENT, ADEDOFF_COMMENT_V2, ADEDLEN_INIT}, 173 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI, ADEDLEN_FILEDATESI}, 174 {ADEID_FINDERI, ADEDOFF_FINDERI_V2, ADEDLEN_FINDERI}, 175 {ADEID_DID, ADEDOFF_DID, ADEDLEN_DID}, 176 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI, ADEDLEN_AFPFILEI}, 177 {ADEID_SHORTNAME, ADEDOFF_SHORTNAME, ADEDLEN_INIT}, 178 {ADEID_PRODOSFILEI, ADEDOFF_PRODOSFILEI, ADEDLEN_PRODOSFILEI}, 179 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV, ADEDLEN_INIT}, 180 {ADEID_PRIVINO, ADEDOFF_PRIVINO, ADEDLEN_INIT}, 181 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN, ADEDLEN_INIT}, 182 {ADEID_PRIVID, ADEDOFF_PRIVID, ADEDLEN_INIT}, 183 {ADEID_RFORK, ADEDOFF_RFORK_V2, ADEDLEN_INIT}, 184 185 {0, 0, 0} 186}; 187 188/* OS X adouble finder info and resource fork only 189 */ 190static const struct entry entry_order_osx[ADEID_NUM_OSX +1] = { 191 {ADEID_FINDERI, ADEDOFF_FINDERI_OSX, ADEDLEN_FINDERI}, 192 {ADEID_RFORK, ADEDOFF_RFORK_OSX, ADEDLEN_INIT}, 193 194 {0, 0, 0} 195}; 196 197#define ADEID_NUM_SFM 3 198static const struct entry entry_order_sfm[ADEID_NUM_SFM +1] = { 199 {ADEID_FINDERI, 16, ADEDLEN_FINDERI}, /* 9 */ 200 {ADEID_SFMRESERVE2, 16+32, 6}, /* 21 */ 201 {ADEID_FILEI, 60, ADEDLEN_FILEI}, /* 7 */ 202 203 {0, 0, 0} 204}; 205 206#endif /* AD_VERSION == AD_VERSION2 */ 207 208#if AD_VERSION == AD_VERSION2 209 210/* update a version 2 adouble resource fork with our private entries */ 211static int ad_update(struct adouble *ad, const char *path) 212{ 213 struct stat st; 214 u_int16_t nentries = 0; 215 off_t off, shiftdata=0; 216 const struct entry *eid; 217 static off_t entry_len[ADEID_MAX]; 218 static char databuf[ADEID_MAX][256], *buf; 219 int fd; 220 int ret = -1; 221 222 /* check to see if we should convert this header. */ 223 if (!path || ad->ad_flags != AD_VERSION2) 224 return 0; 225 226 LOG(log_maxdebug, logtype_default, "ad_update: checking whether '%s' needs an upgrade.", path); 227 228 if (!(ad->ad_md->adf_flags & O_RDWR)) { 229 /* we were unable to open the file read write the last time */ 230 return 0; 231 } 232 233 if (ad->ad_eid[ADEID_RFORK].ade_off) { 234 shiftdata = ADEDOFF_RFORK_V2 -ad->ad_eid[ADEID_RFORK].ade_off; 235 } 236 237 memcpy(&nentries, ad->ad_data + ADEDOFF_NENTRIES, sizeof( nentries )); 238 nentries = ntohs( nentries ); 239 240 if ( shiftdata == 0 && nentries == ADEID_NUM_V2) 241 return 0; 242 243 memset(entry_len, 0, sizeof(entry_len)); 244 memset(databuf, 0, sizeof(databuf)); 245 246 /* bail if we can't get a lock */ 247 if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0, 0) < 0) 248 goto bail_err; 249 250 fd = ad->ad_md->adf_fd; 251 252 if (fstat(fd, &st)) { 253 goto bail_lock; 254 } 255 256 if (st.st_size > 0x7fffffff) { 257 LOG(log_debug, logtype_default, "ad_update: file '%s' too big for update.", path); 258 errno = EIO; 259 goto bail_lock; 260 } 261 262 off = ad->ad_eid[ADEID_RFORK].ade_off; 263 if (off > st.st_size) { 264 LOG(log_error, logtype_default, "ad_update: invalid resource fork offset. (off: %u)", off); 265 errno = EIO; 266 goto bail_lock; 267 } 268 269 if (ad->ad_eid[ADEID_RFORK].ade_len > st.st_size - off) { 270 LOG(log_error, logtype_default, "ad_update: invalid resource fork length. (rfork len: %u)", ad->ad_eid[ADEID_RFORK].ade_len); 271 errno = EIO; 272 goto bail_lock; 273 } 274 275 if ((void *) (buf = (char *) 276 mmap(NULL, st.st_size + shiftdata, 277 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == 278 MAP_FAILED) { 279 goto bail_lock; 280 } 281 282 /* last place for failure. */ 283 if (sys_ftruncate(fd, st.st_size + shiftdata) < 0) { 284 munmap(buf, st.st_size + shiftdata); 285 goto bail_lock; 286 } 287 288 /* move the RFORK. this assumes that the RFORK is at the end */ 289 if (off) { 290 memmove(buf + ADEDOFF_RFORK_V2, buf + off, ad->ad_eid[ADEID_RFORK].ade_len); 291 } 292 293 munmap(buf, st.st_size + shiftdata); 294 295 /* now, fix up our copy of the header */ 296 memset(ad->ad_filler, 0, sizeof(ad->ad_filler)); 297 298 /* save the header entries */ 299 eid = entry_order2; 300 while (eid->id) { 301 if( ad->ad_eid[eid->id].ade_off != 0) { 302 if ( eid->id > 2 && ad->ad_eid[eid->id].ade_len < 256) 303 memcpy( databuf[eid->id], ad->ad_data +ad->ad_eid[eid->id].ade_off, ad->ad_eid[eid->id].ade_len); 304 entry_len[eid->id] = ad->ad_eid[eid->id].ade_len; 305 } 306 eid++; 307 } 308 309 memset(ad->ad_data + AD_HEADER_LEN, 0, AD_DATASZ - AD_HEADER_LEN); 310 311 /* copy the saved entries to the new header */ 312 eid = entry_order2; 313 while (eid->id) { 314 if ( eid->id > 2 && entry_len[eid->id] > 0) { 315 memcpy(ad->ad_data+eid->offset, databuf[eid->id], entry_len[eid->id]); 316 } 317 ad->ad_eid[eid->id].ade_off = eid->offset; 318 ad->ad_eid[eid->id].ade_len = entry_len[eid->id]; 319 eid++; 320 } 321 322 /* rebuild the header and cleanup */ 323 LOG(log_debug, logtype_default, "updated AD2 header %s", path); 324 ad_flush(ad ); 325 ret = 0; 326 327bail_lock: 328 ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0, 0); 329bail_err: 330 return ret; 331} 332 333/* ------------------------------------------ 334 FIXME work only if < 2GB 335*/ 336static int ad_convert(struct adouble *ad, const char *path) 337{ 338 struct stat st; 339 u_int16_t attr; 340 char *buf; 341 int fd, off; 342 int ret = -1; 343 /* use resource fork offset from file */ 344 int shiftdata; 345 int toV2; 346 int toV1; 347 348 if (!path) { 349 return 0; 350 } 351 352 if (!(ad->ad_md->adf_flags & ( O_RDWR))) { 353 /* we were unable to open the file read write the last time */ 354 return 0; 355 } 356 357 /* check to see if we should convert this header. */ 358 toV2 = ad->ad_version == AD_VERSION1 && ad->ad_flags == AD_VERSION2; 359 toV1 = ad->ad_version == AD_VERSION2 && ad->ad_flags == AD_VERSION1; 360 361 if (!toV2 && !toV1) 362 return 0; 363 364 /* convert from v1 to v2. what does this mean? 365 * 1) change FILEI into FILEDATESI 366 * 2) create space for SHORTNAME, AFPFILEI, DID, and PRODOSI 367 * 3) move FILEI attributes into AFPFILEI 368 * 4) initialize ACCESS field of FILEDATESI. 369 * 5) move the resource fork 370 */ 371 372 /* bail if we can't get a lock */ 373 if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0, 0) < 0) 374 goto bail_err; 375 376 /* we reuse fd from the resource fork */ 377 fd = ad->ad_md->adf_fd; 378 379 if (ad->ad_eid[ADEID_RFORK].ade_off) { 380 shiftdata = ADEDOFF_RFORK_V2 -ad->ad_eid[ADEID_RFORK].ade_off; 381 } 382 else { 383 shiftdata = ADEDOFF_RFORK_V2 -ADEDOFF_RFORK_V1; /* 136 */ 384 } 385 386 if (fstat(fd, &st)) { 387 goto bail_lock; 388 } 389 390 if (st.st_size > 0x7fffffff -shiftdata) { 391 LOG(log_debug, logtype_default, "ad_v1tov2: file too big."); 392 errno = EIO; 393 goto bail_lock; 394 } 395 396 off = ad->ad_eid[ADEID_RFORK].ade_off; 397 398 if (off > st.st_size) { 399 LOG(log_error, logtype_default, "ad_v1tov2: invalid resource fork offset. (off: %u)", off); 400 errno = EIO; 401 goto bail_lock; 402 } 403 404 if (ad->ad_eid[ADEID_RFORK].ade_len > st.st_size - off) { 405 LOG(log_error, logtype_default, "ad_v1tov2: invalid resource fork length. (rfork len: %u)", ad->ad_eid[ADEID_RFORK].ade_len); 406 errno = EIO; 407 goto bail_lock; 408 } 409 410 if ((void *) (buf = (char *) 411 mmap(NULL, st.st_size + shiftdata, 412 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == 413 MAP_FAILED) { 414 goto bail_lock; 415 } 416 417 /* last place for failure. */ 418 419 if (sys_ftruncate(fd, st.st_size + shiftdata) < 0) { 420 goto bail_lock; 421 } 422 423 /* move the RFORK. this assumes that the RFORK is at the end */ 424 if (off) { 425 memmove(buf + ADEDOFF_RFORK_V2, buf + off, ad->ad_eid[ADEID_RFORK].ade_len); 426 } 427 428 munmap(buf, st.st_size + shiftdata); 429 430 /* now, fix up our copy of the header */ 431 memset(ad->ad_filler, 0, sizeof(ad->ad_filler)); 432 433 /* replace FILEI with FILEDATESI */ 434 ad_getattr(ad, &attr); 435 ad->ad_eid[ADEID_FILEDATESI].ade_off = ADEDOFF_FILEDATESI; 436 ad->ad_eid[ADEID_FILEDATESI].ade_len = ADEDLEN_FILEDATESI; 437 ad->ad_eid[ADEID_FILEI].ade_off = 0; 438 ad->ad_eid[ADEID_FILEI].ade_len = 0; 439 440 /* add in the new entries */ 441 ad->ad_eid[ADEID_DID].ade_off = ADEDOFF_DID; 442 ad->ad_eid[ADEID_DID].ade_len = ADEDLEN_DID; 443 ad->ad_eid[ADEID_AFPFILEI].ade_off = ADEDOFF_AFPFILEI; 444 ad->ad_eid[ADEID_AFPFILEI].ade_len = ADEDLEN_AFPFILEI; 445 ad->ad_eid[ADEID_SHORTNAME].ade_off = ADEDOFF_SHORTNAME; 446 ad->ad_eid[ADEID_SHORTNAME].ade_len = ADEDLEN_INIT; 447 ad->ad_eid[ADEID_PRODOSFILEI].ade_off = ADEDOFF_PRODOSFILEI; 448 ad->ad_eid[ADEID_PRODOSFILEI].ade_len = ADEDLEN_PRODOSFILEI; 449 450 ad->ad_eid[ADEID_PRIVDEV].ade_off = ADEDOFF_PRIVDEV; 451 ad->ad_eid[ADEID_PRIVDEV].ade_len = ADEDLEN_INIT; 452 ad->ad_eid[ADEID_PRIVINO].ade_off = ADEDOFF_PRIVINO; 453 ad->ad_eid[ADEID_PRIVINO].ade_len = ADEDLEN_INIT; 454 ad->ad_eid[ADEID_PRIVSYN].ade_off = ADEDOFF_PRIVSYN; 455 ad->ad_eid[ADEID_PRIVSYN].ade_len = ADEDLEN_INIT; 456 ad->ad_eid[ADEID_PRIVID].ade_off = ADEDOFF_PRIVID; 457 ad->ad_eid[ADEID_PRIVID].ade_len = ADEDLEN_INIT; 458 459 /* shift the old entries (NAME, COMMENT, FINDERI, RFORK) */ 460 ad->ad_eid[ADEID_NAME].ade_off = ADEDOFF_NAME_V2; 461 ad->ad_eid[ADEID_COMMENT].ade_off = ADEDOFF_COMMENT_V2; 462 ad->ad_eid[ADEID_FINDERI].ade_off = ADEDOFF_FINDERI_V2; 463 ad->ad_eid[ADEID_RFORK].ade_off = ADEDOFF_RFORK_V2; 464 465 /* switch to dest version */ 466 ad->ad_version = (toV2)?AD_VERSION2:AD_VERSION1; 467 468 /* move our data buffer to make space for the new entries. */ 469 memmove(ad->ad_data + ADEDOFF_NAME_V2, ad->ad_data + ADEDOFF_NAME_V1, 470 ADEDOFF_RFORK_V1 - ADEDOFF_NAME_V1); 471 472 /* now, fill in the space with appropriate stuff. we're 473 operating as a v2 file now. */ 474 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime); 475 memset(ad_entry(ad, ADEID_DID), 0, ADEDLEN_DID); 476 memset(ad_entry(ad, ADEID_AFPFILEI), 0, ADEDLEN_AFPFILEI); 477 ad_setattr(ad, attr); 478 memset(ad_entry(ad, ADEID_SHORTNAME), 0, ADEDLEN_SHORTNAME); 479 memset(ad_entry(ad, ADEID_PRODOSFILEI), 0, ADEDLEN_PRODOSFILEI); 480 481 /* rebuild the header and cleanup */ 482 ad_flush(ad ); 483 ret = 0; 484 485bail_lock: 486 ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0, 0); 487bail_err: 488 return ret; 489} 490#endif /* AD_VERSION == AD_VERSION2 */ 491 492/* ------------------------------------- 493 read in the entries 494*/ 495static void parse_entries(struct adouble *ad, char *buf, 496 u_int16_t nentries) 497{ 498 u_int32_t eid, len, off; 499 int warning = 0; 500 501 /* now, read in the entry bits */ 502 for (; nentries > 0; nentries-- ) { 503 memcpy(&eid, buf, sizeof( eid )); 504 eid = DISK_EID(ad, ntohl( eid )); 505 buf += sizeof( eid ); 506 memcpy(&off, buf, sizeof( off )); 507 off = ntohl( off ); 508 buf += sizeof( off ); 509 memcpy(&len, buf, sizeof( len )); 510 len = ntohl( len ); 511 buf += sizeof( len ); 512 513 if (eid && eid < ADEID_MAX && off < sizeof(ad->ad_data) && 514 (off +len <= sizeof(ad->ad_data) || eid == ADEID_RFORK)) { 515 ad->ad_eid[ eid ].ade_off = off; 516 ad->ad_eid[ eid ].ade_len = len; 517 } else if (!warning) { 518 warning = 1; 519 LOG(log_debug, logtype_default, "ad_refresh: nentries %hd eid %d", 520 nentries, eid ); 521 } 522 } 523} 524 525 526/* this reads enough of the header so that we can figure out all of 527 * the entry lengths and offsets. once that's done, we just read/mmap 528 * the rest of the header in. 529 * 530 * NOTE: we're assuming that the resource fork is kept at the end of 531 * the file. also, mmapping won't work for the hfs fs until it 532 * understands how to mmap header files. */ 533static int ad_header_read(struct adouble *ad, struct stat *hst) 534{ 535 char *buf = ad->ad_data; 536 u_int16_t nentries; 537 int len; 538 ssize_t header_len; 539 static int warning = 0; 540 struct stat st; 541 542 /* read the header */ 543 if ((header_len = adf_pread( ad->ad_md, buf, sizeof(ad->ad_data), 0)) < 0) { 544 return -1; 545 } 546 if (header_len < AD_HEADER_LEN) { 547 errno = EIO; 548 return -1; 549 } 550 551 memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic )); 552 memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, sizeof( ad->ad_version )); 553 554 /* tag broken v1 headers. just assume they're all right. 555 * we detect two cases: null magic/version 556 * byte swapped magic/version 557 * XXX: in the future, you'll need the v1compat flag. 558 * (ad->ad_flags & ADFLAGS_V1COMPAT) */ 559 if (!ad->ad_magic && !ad->ad_version) { 560 if (!warning) { 561 LOG(log_debug, logtype_default, "notice: fixing up null v1 magic/version."); 562 warning++; 563 } 564 ad->ad_magic = AD_MAGIC; 565 ad->ad_version = AD_VERSION1; 566 567 } else if (ad->ad_magic == AD_MAGIC && ad->ad_version == AD_VERSION1) { 568 if (!warning) { 569 LOG(log_debug, logtype_default, "notice: fixing up byte-swapped v1 magic/version."); 570 warning++; 571 } 572 573 } else { 574 ad->ad_magic = ntohl( ad->ad_magic ); 575 ad->ad_version = ntohl( ad->ad_version ); 576 } 577 578 if ((ad->ad_magic != AD_MAGIC) || ((ad->ad_version != AD_VERSION1) 579#if AD_VERSION == AD_VERSION2 580 && (ad->ad_version != AD_VERSION2) 581#endif /* AD_VERSION == AD_VERSION2 */ 582 )) { 583 LOG(log_debug, logtype_default, "ad_open: can't parse AppleDouble header."); 584 errno = EIO; 585 return -1; 586 } 587 588 memcpy(ad->ad_filler, buf + ADEDOFF_FILLER, sizeof( ad->ad_filler )); 589 memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries )); 590 nentries = ntohs( nentries ); 591 592 /* read in all the entry headers. if we have more than the 593 * maximum, just hope that the rfork is specified early on. */ 594 len = nentries*AD_ENTRY_LEN; 595 596 if (len + AD_HEADER_LEN > sizeof(ad->ad_data)) 597 len = sizeof(ad->ad_data) - AD_HEADER_LEN; 598 599 buf += AD_HEADER_LEN; 600 if (len > header_len - AD_HEADER_LEN) { 601 LOG(log_debug, logtype_default, "ad_header_read: can't read entry info."); 602 errno = EIO; 603 return -1; 604 } 605 606 /* figure out all of the entry offsets and lengths. if we aren't 607 * able to read a resource fork entry, bail. */ 608 nentries = len / AD_ENTRY_LEN; 609 parse_entries(ad, buf, nentries); 610 if (!ad_getentryoff(ad, ADEID_RFORK) 611 || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data)) 612 ) { 613 LOG(log_debug, logtype_default, "ad_header_read: problem with rfork entry offset."); 614 errno = EIO; 615 return -1; 616 } 617 618 if (ad_getentryoff(ad, ADEID_RFORK) > header_len) { 619 LOG(log_debug, logtype_default, "ad_header_read: can't read in entries."); 620 errno = EIO; 621 return -1; 622 } 623 624 if (hst == NULL) { 625 hst = &st; 626 if (fstat(ad->ad_md->adf_fd, &st) < 0) { 627 return 1; /* fail silently */ 628 } 629 } 630 ad->ad_rlen = hst->st_size - ad_getentryoff(ad, ADEID_RFORK); 631 632 /* fix up broken dates */ 633 if (ad->ad_version == AD_VERSION1) { 634 u_int32_t aint; 635 636 /* check to see if the ad date is wrong. just see if we have 637 * a modification date in the future. */ 638 if (((ad_getdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, &aint)) == 0) && 639 (aint > TIMEWARP_DELTA + hst->st_mtime)) { 640 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, aint - AD_DATE_DELTA); 641 ad_getdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, &aint); 642 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, aint - AD_DATE_DELTA); 643 ad_getdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, &aint); 644 ad_setdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, aint - AD_DATE_DELTA); 645 } 646 } 647 648 return 0; 649} 650 651/* --------------------------- 652 SFM structure 653*/ 654#if 0 655typedef struct { 656 byte afpi_Signature[4]; /* Must be 0x00504641 */ 657 byte afpi_Version[4]; /* Must be 0x00010000 */ 658 byte afpi_Reserved1[4]; 659 byte afpi_BackupTime[4]; /* Backup time for the file/dir */ 660 byte finderinfo[32]; /* Finder info */ 661 byte afpi_ProDosInfo[6]; /* ProDos Info */ 662 byte afpi_Reserved2[6]; 663} sfm_info; 664#endif 665 666static int ad_header_sfm_read(struct adouble *ad, struct stat *hst) 667{ 668 char *buf = ad->ad_data; 669 const struct entry *eid; 670 ssize_t header_len; 671 struct stat st; 672 673 /* read the header */ 674 if ((header_len = adf_pread( ad->ad_md, buf, sizeof(ad->ad_data), 0)) < 0) { 675 return -1; 676 } 677 if (header_len != AD_SFM_LEN) { 678 errno = EIO; 679 return -1; 680 } 681 682 memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic )); 683 memcpy(&ad->ad_version, buf + 4, sizeof( ad->ad_version )); 684 685 /* FIXME in the great Microsoft tradition they aren't in network order */ 686#if 0 687 if (ad->ad_magic == SFM_MAGIC && ad->ad_version == AD_VERSION1) { 688 static int warning = 0; 689 if (!warning) { 690 LOG(log_debug, logtype_default, "notice: fixing up byte-swapped v1 magic/version."); 691 warning++; 692 } 693 694 } else { 695 ad->ad_magic = ntohl( ad->ad_magic ); 696 ad->ad_version = ntohl( ad->ad_version ); 697 } 698#endif 699 if ((ad->ad_magic != SFM_MAGIC) || ((ad->ad_version != AD_VERSION1) )) { 700 errno = EIO; 701 LOG(log_debug, logtype_default, "ad_header_sfm_read: can't parse AppleDouble header."); 702 return -1; 703 } 704 705 /* reinit adouble table */ 706 eid = entry_order_sfm; 707 while (eid->id) { 708 ad->ad_eid[eid->id].ade_off = eid->offset; 709 ad->ad_eid[eid->id].ade_len = eid->len; 710 eid++; 711 } 712 713 /* steal some prodos for attribute */ 714 { 715 716 u_int16_t attribute; 717 memcpy(&attribute, buf + 48 +4, sizeof(attribute)); 718 ad_setattr(ad, attribute ); 719 } 720 721 if (ad->ad_resource_fork.adf_fd != -1) { 722 /* we have a resource fork use it rather than the metadata */ 723 if (fstat(ad->ad_resource_fork.adf_fd, &st) < 0) { 724 /* XXX set to zero ? 725 ad->ad_rlen = 0; 726 */ 727 return 1; 728 } 729 ad->ad_rlen = st.st_size; 730 hst = &st; 731 } 732 else if (hst == NULL) { 733 hst = &st; 734 if (fstat(ad->ad_md->adf_fd, &st) < 0) { 735 return 1; /* fail silently */ 736 } 737 } 738 739 /* fix up broken dates */ 740 if (ad->ad_version == AD_VERSION1) { 741 u_int32_t aint; 742 743 /* check to see if the ad date is wrong. just see if we have 744 * a modification date in the future. */ 745 if (((ad_getdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, &aint)) == 0) && 746 (aint > TIMEWARP_DELTA + hst->st_mtime)) { 747 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, aint - AD_DATE_DELTA); 748 ad_getdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, &aint); 749 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, aint - AD_DATE_DELTA); 750 ad_getdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, &aint); 751 ad_setdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, aint - AD_DATE_DELTA); 752 } 753 } 754 755 return 0; 756} 757 758/* --------------------------------------- 759 * Put the .AppleDouble where it needs to be: 760 * 761 * / a/.AppleDouble/b 762 * a/b 763 * \ b/.AppleDouble/.Parent 764 * 765 * FIXME: should do something for pathname > MAXPATHLEN 766 */ 767char * 768ad_path( const char *path, int adflags) 769{ 770 static char pathbuf[ MAXPATHLEN + 1]; 771 const char *slash; 772 size_t l ; 773 774 if ( adflags & ADFLAGS_DIR ) { 775 l = strlcpy( pathbuf, path, sizeof(pathbuf)); 776 777 if ( l && l < MAXPATHLEN) { 778 pathbuf[l++] = '/'; 779 } 780 strlcpy(pathbuf +l, ".AppleDouble/.Parent", sizeof(pathbuf) -l); 781 } else { 782 if (NULL != ( slash = strrchr( path, '/' )) ) { 783 slash++; 784 l = slash - path; 785 /* XXX we must return NULL here and test in the caller */ 786 if (l > MAXPATHLEN) 787 l = MAXPATHLEN; 788 memcpy( pathbuf, path, l); 789 } else { 790 l = 0; 791 slash = path; 792 } 793 l += strlcpy( pathbuf +l, ".AppleDouble/", sizeof(pathbuf) -l); 794 strlcpy( pathbuf + l, slash, sizeof(pathbuf) -l); 795 } 796 797 return( pathbuf ); 798} 799 800/* -------------------- */ 801static int ad_mkrf(char *path) 802{ 803 char *slash; 804 /* 805 * Probably .AppleDouble doesn't exist, try to mkdir it. 806 */ 807 if (NULL == ( slash = strrchr( path, '/' )) ) { 808 return -1; 809 } 810 *slash = '\0'; 811 errno = 0; 812 if ( ad_mkdir( path, 0777 ) < 0 ) { 813 return -1; 814 } 815 *slash = '/'; 816 return 0; 817} 818 819/* --------------------------------------- 820 * Put the resource fork where it needs to be: 821 * ._name 822 */ 823char * 824ad_path_osx(const char *path, int adflags _U_) 825{ 826 static char pathbuf[ MAXPATHLEN + 1]; 827 char c, *slash, buf[MAXPATHLEN + 1]; 828 829 if (!strcmp(path,".")) { 830 /* fixme */ 831 getcwd(buf, MAXPATHLEN); 832 } 833 else { 834 strlcpy(buf, path, MAXPATHLEN +1); 835 } 836 if (NULL != ( slash = strrchr( buf, '/' )) ) { 837 c = *++slash; 838 *slash = '\0'; 839 strlcpy( pathbuf, buf, MAXPATHLEN +1); 840 *slash = c; 841 } else { 842 pathbuf[ 0 ] = '\0'; 843 slash = buf; 844 } 845 strlcat( pathbuf, "._", MAXPATHLEN +1); 846 strlcat( pathbuf, slash, MAXPATHLEN +1); 847 return pathbuf; 848} 849/* -------------------- */ 850static int ad_mkrf_osx(char *path _U_) 851{ 852 return 0; 853} 854 855/* --------------------------------------- 856 * Put the .AppleDouble where it needs to be: 857 * 858 * / a/.AppleDouble/b/AFP_AfpInfo 859 * a/b 860 * \ b/.AppleDouble/.Parent/AFP_AfpInfo 861 * 862 */ 863char * 864ad_path_sfm( const char *path, int adflags) 865{ 866 static char pathbuf[ MAXPATHLEN + 1]; 867 char c, *slash, buf[MAXPATHLEN + 1]; 868 size_t l; 869 870 l = strlcpy(buf, path, MAXPATHLEN +1); 871 if ( adflags & ADFLAGS_DIR ) { 872 strcpy( pathbuf, buf); 873 if ( *buf != '\0' && l < MAXPATHLEN) { 874 pathbuf[l++] = '/'; 875 pathbuf[l] = 0; 876 } 877 slash = ".Parent"; 878 } else { 879 if (NULL != ( slash = strrchr( buf, '/' )) ) { 880 c = *++slash; 881 *slash = '\0'; 882 strcpy( pathbuf, buf); 883 *slash = c; 884 } else { 885 pathbuf[ 0 ] = '\0'; 886 slash = buf; 887 } 888 } 889 strlcat( pathbuf, ".AppleDouble/", MAXPATHLEN +1); 890 strlcat( pathbuf, slash, MAXPATHLEN +1); 891 892 if ((adflags == ADFLAGS_RF)) { 893 strlcat( pathbuf, "/AFP_Resource", MAXPATHLEN +1); 894 } 895 else { 896 strlcat( pathbuf, "/AFP_AfpInfo", MAXPATHLEN +1); 897 } 898 return( pathbuf ); 899} 900 901/* -------------------- */ 902static int ad_mkrf_sfm(char *path) 903{ 904 char *slash; 905 /* 906 * Probably .AppleDouble doesn't exist, try to mkdir it. 907 */ 908 if (NULL == ( slash = strrchr( path, '/' )) ) { 909 return -1; 910 } 911 *slash = 0; 912 errno = 0; 913 if ( ad_mkdir( path, 0777 ) < 0 ) { 914 if ( errno == ENOENT ) { 915 char *slash1; 916 917 if (NULL == ( slash1 = strrchr( path, '/' )) ) 918 return -1; 919 errno = 0; 920 *slash1 = 0; 921 if ( ad_mkdir( path, 0777 ) < 0 ) 922 return -1; 923 *slash1 = '/'; 924 if ( ad_mkdir( path, 0777 ) < 0 ) 925 return -1; 926 } 927 else 928 return -1; 929 } 930 *slash = '/'; 931 return 0; 932} 933 934/* ------------------------- 935 * Support inherited protection modes for AppleDouble files. The supplied 936 * mode is ANDed with the parent directory's mask value in lieu of "umask", 937 * and that value is returned. 938 */ 939 940#define DEFMASK 07700 /* be conservative */ 941 942char 943*ad_dir(const char *path) 944{ 945 static char modebuf[ MAXPATHLEN + 1]; 946 char *slash; 947 /* 948 * For a path with directories in it, remove the final component 949 * (path or subdirectory name) to get the name we want to stat. 950 * For a path which is just a filename, use "." instead. 951 */ 952 slash = strrchr( path, '/' ); 953 if (slash) { 954 size_t len; 955 956 len = slash - path; 957 if (len >= MAXPATHLEN) { 958 errno = ENAMETOOLONG; 959 return NULL; /* can't do it */ 960 } 961 memcpy( modebuf, path, len ); 962 modebuf[len] = '\0'; 963 /* is last char a '/' ? */ 964 if (slash[1] == 0) { 965 slash = modebuf+ len; 966 /* remove them */ 967 while (modebuf < slash && slash[-1] == '/') { 968 --slash; 969 } 970 if (modebuf == slash) { 971 goto use_cur; 972 } 973 *slash = '\0'; 974 while (modebuf < slash && *slash != '/') { 975 --slash; 976 } 977 if (modebuf == slash) { 978 goto use_cur; 979 } 980 *slash = '\0'; /* remove pathname component */ 981 } 982 return modebuf; 983 } 984use_cur: 985 modebuf[0] = '.'; /* use current directory */ 986 modebuf[1] = '\0'; 987 return modebuf; 988} 989 990/* ---------------- */ 991static uid_t default_uid = -1; 992 993int ad_setfuid(const uid_t id) 994{ 995 default_uid = id; 996 return 0; 997} 998 999/* ---------------- */ 1000uid_t ad_getfuid(void) 1001{ 1002 return default_uid; 1003} 1004 1005/* ---------------- 1006 return inode of path parent directory 1007*/ 1008int ad_stat(const char *path, struct stat *stbuf) 1009{ 1010 char *p; 1011 1012 p = ad_dir(path); 1013 if (!p) { 1014 return -1; 1015 } 1016 1017 return stat(p, stbuf); 1018} 1019 1020/* ---------------- 1021 if we are root change path user/ group 1022 It can be a native function for BSD cf. FAQ.Q10 1023 path: pathname to chown 1024 stbuf: parent directory inode 1025 1026 use fstat and fchown or lchown with linux? 1027*/ 1028#define EMULATE_SUIDDIR 1029 1030static int ad_chown(const char *path, struct stat *stbuf) 1031{ 1032 int ret = 0; 1033#ifdef EMULATE_SUIDDIR 1034 uid_t id; 1035 1036 if (default_uid != (uid_t)-1) { 1037 /* we are root (admin) */ 1038 id = (default_uid)?default_uid:stbuf->st_uid; 1039 ret = chown(path, id, stbuf->st_gid); 1040 } 1041#endif 1042 return ret; 1043} 1044 1045/* ---------------- 1046 return access right and inode of path parent directory 1047*/ 1048static int ad_mode_st(const char *path, int *mode, struct stat *stbuf) 1049{ 1050 if (*mode == 0) { 1051 return -1; 1052 } 1053 if (ad_stat(path, stbuf) != 0) { 1054 *mode &= DEFMASK; 1055 return -1; 1056 } 1057 *mode &= stbuf->st_mode; 1058 return 0; 1059} 1060 1061/* ---------------- 1062 return access right of path parent directory 1063*/ 1064int 1065ad_mode( const char *path, int mode) 1066{ 1067 struct stat stbuf; 1068 ad_mode_st(path, &mode, &stbuf); 1069 return mode; 1070} 1071 1072/* 1073 * Use mkdir() with mode bits taken from ad_mode(). 1074 */ 1075int 1076ad_mkdir( const char *path, int mode) 1077{ 1078 int ret; 1079 int st_invalid; 1080 struct stat stbuf; 1081 1082 LOG(log_debug, logtype_default, "ad_mkdir(\"%s\", %04o) {cwd: \"%s\"}", 1083 path, mode, getcwdpath()); 1084 1085 st_invalid = ad_mode_st(path, &mode, &stbuf); 1086 ret = mkdir( path, mode ); 1087 if (ret || st_invalid) 1088 return ret; 1089 ad_chown(path, &stbuf); 1090 1091 return ret; 1092} 1093 1094/* ----------------- */ 1095static int ad_error(struct adouble *ad, int adflags) 1096{ 1097 int err = errno; 1098 if ((adflags & ADFLAGS_NOHF)) { 1099 /* FIXME double check : set header offset ?*/ 1100 return 0; 1101 } 1102 if ((adflags & ADFLAGS_DF)) { 1103 ad_close( ad, ADFLAGS_DF ); 1104 err = errno; 1105 } 1106 return -1 ; 1107} 1108 1109static int new_rfork(const char *path, struct adouble *ad, int adflags); 1110 1111#ifdef HAVE_PREAD 1112#define AD_SET(a) 1113#else 1114#define AD_SET(a) a = 0 1115#endif 1116 1117/* --------------------------- */ 1118static int ad_check_size(struct adouble *ad _U_, struct stat *st) 1119{ 1120 if (st->st_size > 0 && st->st_size < AD_DATASZ1) 1121 return 1; 1122 return 0; 1123} 1124 1125/* --------------------------- */ 1126static int ad_check_size_sfm(struct adouble *ad _U_, struct stat *st) 1127{ 1128 if (st->st_size > 0 && st->st_size < AD_SFM_LEN) 1129 return 1; 1130 return 0; 1131} 1132 1133/* --------------------------- */ 1134static int ad_header_upgrade(struct adouble *ad, char *name) 1135{ 1136#if AD_VERSION == AD_VERSION2 1137 int ret; 1138 if ( (ret = ad_convert(ad, name)) < 0 || (ret = ad_update(ad, name) < 0)) { 1139 return ret; 1140 } 1141#endif 1142 return 0; 1143} 1144 1145/* --------------------------- */ 1146static int ad_header_upgrade_none(struct adouble *ad _U_, char *name _U_) 1147{ 1148 return 0; 1149} 1150 1151/* --------------------------- */ 1152static struct adouble_fops ad_osx = { 1153 &ad_path_osx, 1154 &ad_mkrf_osx, 1155 &ad_rebuild_adouble_header, 1156 &ad_check_size, 1157 1158 &ad_header_read, 1159 &ad_header_upgrade, 1160}; 1161 1162static struct adouble_fops ad_sfm = { 1163 &ad_path_sfm, 1164 &ad_mkrf_sfm, 1165 &ad_rebuild_sfm_header, 1166 &ad_check_size_sfm, 1167 1168 &ad_header_sfm_read, 1169 &ad_header_upgrade_none, 1170}; 1171 1172static struct adouble_fops ad_adouble = { 1173 &ad_path, 1174 &ad_mkrf, 1175 &ad_rebuild_adouble_header, 1176 &ad_check_size, 1177 1178 &ad_header_read, 1179 &ad_header_upgrade, 1180}; 1181 1182 1183void ad_init(struct adouble *ad, int flags, int options) 1184{ 1185 ad->ad_inited = 0; 1186 ad->ad_flags = flags; 1187 if (flags == AD_VERSION2_OSX) { 1188 ad->ad_ops = &ad_osx; 1189 ad->ad_md = &ad->ad_resource_fork; 1190 } 1191 else if (flags == AD_VERSION1_SFM) { 1192 ad->ad_ops = &ad_sfm; 1193 ad->ad_md = &ad->ad_metadata_fork; 1194 } 1195 else { 1196 ad->ad_ops = &ad_adouble; 1197 ad->ad_md = &ad->ad_resource_fork; 1198 } 1199 ad->ad_options = options; 1200 1201 ad_data_fileno(ad) = -1; 1202 ad_reso_fileno(ad) = -1; 1203 ad_meta_fileno(ad) = -1; 1204 /* following can be read even if there's no 1205 * meda data. 1206 */ 1207 memset(ad->ad_eid, 0, sizeof( ad->ad_eid )); 1208 ad->ad_rlen = 0; 1209} 1210 1211/*! 1212 * Open data-, metadata(header)- or ressource fork 1213 * 1214 * You must call ad_init() before ad_open, usually you'll just call it like this: \n 1215 * @code 1216 * struct adoube ad; 1217 * ad_init(&ad, vol->v_adouble, vol->v_ad_options); 1218 * @endcode 1219 * 1220 * @param path Path to file or directory 1221 * 1222 * @param adflags ADFLAGS_DF: open data file/fork\n 1223 * ADFLAGS_HF: open header (metadata) file\n 1224 * ADFLAGS_RF: open ressource fork *** FIXME: not used ?! *** \n 1225 * ADFLAGS_CREATE: indicate creation\n 1226 * ADFLAGS_NOHF: it's not an error if header file couldn't be created\n 1227 * ADFLAGS_DIR: if path is a directory you MUST or ADFLAGS_DIR to adflags\n 1228 * ADFLAGS_NOADOUBLE: dont create adouble files if not necessary\n 1229 * ADFLAGS_RDONLY: open read only\n 1230 * ADFLAGS_OPENFORKS: check for open forks from other processes\n 1231 * ADFLAGS_MD: alias for ADFLAGS_HF\n 1232 * ADFLAGS_V1COMPAT: obsolete 1233 * 1234 * @param oflags flags passed through to open syscall: \n 1235 * O_RDONLY: *** FIXME *** \n 1236 * O_RDWR: *** FIXME *** \n 1237 * O_CREAT: create fork\n 1238 * O_EXCL: fail if exists with O_CREAT 1239 * 1240 * @param mode passed to open with O_CREAT 1241 * 1242 * @param ad pointer to struct adouble 1243 * 1244 * @returns 0 on success 1245 * 1246 * @note It's not possible to open the header file O_RDONLY -- the read 1247 * will fail and return an error. this refcounts things now.\n 1248 * metadata(ressource)-fork only gets created with O_CREAT. 1249 */ 1250int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble *ad) 1251{ 1252 struct stat st_dir; 1253 struct stat st_meta; 1254 struct stat *pst = NULL; 1255 char *ad_p; 1256 int hoflags, admode; 1257 int st_invalid = -1; 1258 int open_df = 0; 1259 1260 if (ad->ad_inited != AD_INITED) { 1261 ad->ad_inited = AD_INITED; 1262 ad->ad_refcount = 1; 1263 ad->ad_open_forks = 0; 1264 ad->ad_adflags = adflags; 1265 ad->ad_resource_fork.adf_refcount = 0; 1266 ad->ad_data_fork.adf_refcount = 0; 1267 ad->ad_data_fork.adf_syml=0; 1268 } 1269 else { 1270 ad->ad_open_forks = ((ad->ad_data_fork.adf_refcount > 0) ? ATTRBIT_DOPEN : 0); 1271 /* XXX not true if we have a meta data fork ? */ 1272 if ((ad->ad_resource_fork.adf_refcount > ad->ad_data_fork.adf_refcount)) 1273 ad->ad_open_forks |= ATTRBIT_ROPEN; 1274 } 1275 1276 if ((adflags & ADFLAGS_DF)) { 1277 if (ad_data_fileno(ad) == -1) { 1278 hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR; 1279 admode = mode; 1280 if ((oflags & O_CREAT)) { 1281 st_invalid = ad_mode_st(path, &admode, &st_dir); 1282 if ((ad->ad_options & ADVOL_UNIXPRIV)) { 1283 admode = mode; 1284 } 1285 } 1286 1287 ad->ad_data_fork.adf_fd = open(path, hoflags | ad_get_syml_opt(ad), admode); 1288 1289 if (ad->ad_data_fork.adf_fd == -1) { 1290 if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) { 1291 hoflags = oflags; 1292 ad->ad_data_fork.adf_fd = open( path, hoflags | ad_get_syml_opt(ad), admode ); 1293 } 1294 if (ad->ad_data_fork.adf_fd == -1 && errno == OPEN_NOFOLLOW_ERRNO) { 1295 int lsz; 1296 1297 ad->ad_data_fork.adf_syml = malloc(MAXPATHLEN+1); 1298 lsz = readlink(path, ad->ad_data_fork.adf_syml, MAXPATHLEN); 1299 if (lsz <= 0) { 1300 free(ad->ad_data_fork.adf_syml); 1301 return -1; 1302 } 1303 ad->ad_data_fork.adf_syml[lsz] = 0; 1304 ad->ad_data_fork.adf_fd = -2; /* -2 means its a symlink */ 1305 } 1306 } 1307 1308 if ( ad->ad_data_fork.adf_fd == -1 ) 1309 return -1; 1310 1311 AD_SET(ad->ad_data_fork.adf_off); 1312 ad->ad_data_fork.adf_flags = hoflags; 1313 if (!st_invalid) { 1314 /* just created, set owner if admin (root) */ 1315 ad_chown(path, &st_dir); 1316 } 1317 adf_lock_init(&ad->ad_data_fork); 1318 } 1319 else { 1320 /* the file is already open... but */ 1321 if ((oflags & ( O_RDWR | O_WRONLY)) && /* we want write access */ 1322 !(ad->ad_data_fork.adf_flags & ( O_RDWR | O_WRONLY))) /* and it was denied the first time */ 1323 { 1324 errno = EACCES; 1325 return -1; 1326 } 1327 /* FIXME 1328 * for now ad_open is never called with O_TRUNC or O_EXCL if the file is 1329 * already open. Should we check for it? ie 1330 * O_EXCL --> error 1331 * O_TRUNC --> truncate the fork. 1332 * idem for ressource fork. 1333 */ 1334 } 1335 open_df = ADFLAGS_DF; 1336 ad->ad_data_fork.adf_refcount++; 1337 } 1338 1339 adf_lock_init(&ad->ad_data_fork); 1340 if (!(adflags & ADFLAGS_HF)) 1341 return 0; 1342 1343 if(strcmp(path,".")==0) 1344 { 1345 char dir[1024]; 1346 getcwd(dir,sizeof(dir)); 1347 if(strstr(dir,"Private Directory Data")) 1348 return 0; 1349 } 1350 if(strstr(path,"Private Directory Data")) 1351 return 0; 1352 1353 /* ****************************************** */ 1354 1355 if (ad_meta_fileno(ad) != -1) { /* the file is already open */ 1356 if ((oflags & ( O_RDWR | O_WRONLY)) && 1357 !(ad->ad_md->adf_flags & ( O_RDWR | O_WRONLY))) { 1358 if (open_df) { 1359 /* don't call with ADFLAGS_HF because we didn't open ressource fork */ 1360 ad_close( ad, open_df ); 1361 } 1362 errno = EACCES; 1363 return -1; 1364 } 1365 ad_refresh(ad); 1366 /* it's not new anymore */ 1367 ad->ad_md->adf_flags &= ~( O_TRUNC | O_CREAT ); 1368 ad->ad_md->adf_refcount++; 1369 goto sfm; 1370 } 1371 1372 memset(ad->ad_eid, 0, sizeof( ad->ad_eid )); 1373 ad->ad_rlen = 0; 1374 ad_p = ad->ad_ops->ad_path( path, adflags ); 1375 hoflags = oflags & ~(O_CREAT | O_EXCL); 1376 if (!(adflags & ADFLAGS_RDONLY)) { 1377 hoflags = (hoflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR; 1378 } 1379 ad->ad_md->adf_fd = open(ad_p, hoflags | ad_get_syml_opt(ad), 0); 1380 if (ad->ad_md->adf_fd < 0 ) { 1381 if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) { 1382 hoflags = oflags & ~(O_CREAT | O_EXCL); 1383 ad->ad_md->adf_fd = open(ad_p, hoflags | ad_get_syml_opt(ad), 0); 1384 } 1385 } 1386 1387 if ( ad->ad_md->adf_fd < 0 ) { 1388 if (errno == ENOENT && (oflags & O_CREAT) ) { 1389 /* 1390 * We're expecting to create a new adouble header file, 1391 * here. 1392 * if ((oflags & O_CREAT) ==> (oflags & O_RDWR) 1393 */ 1394 LOG(log_debug, logtype_default, "ad_open(\"%s\"): {cwd: \"%s\"} creating adouble file", 1395 ad_p, getcwdpath()); 1396 admode = mode; 1397 errno = 0; 1398 st_invalid = ad_mode_st(ad_p, &admode, &st_dir); 1399 if ((ad->ad_options & ADVOL_UNIXPRIV)) { 1400 admode = mode; 1401 } 1402 admode = ad_hf_mode(admode); 1403 if ((errno == ENOENT) && (ad->ad_flags != AD_VERSION2_OSX)) { 1404 if (ad->ad_ops->ad_mkrf(ad_p) < 0) { 1405 return ad_error(ad, adflags); 1406 } 1407 admode = mode; 1408 st_invalid = ad_mode_st(ad_p, &admode, &st_dir); 1409 if ((ad->ad_options & ADVOL_UNIXPRIV)) { 1410 admode = mode; 1411 } 1412 admode = ad_hf_mode(admode); 1413 } 1414 /* retry with O_CREAT */ 1415 ad->ad_md->adf_fd = open( ad_p, oflags,admode ); 1416 if ( ad->ad_md->adf_fd < 0 ) { 1417 return ad_error(ad, adflags); 1418 } 1419 ad->ad_md->adf_flags = oflags; 1420 /* just created, set owner if admin owner (root) */ 1421 if (!st_invalid) { 1422 ad_chown(ad_p, &st_dir); 1423 } 1424 } 1425 else { 1426 return ad_error(ad, adflags); 1427 } 1428 } else { 1429 ad->ad_md->adf_flags = hoflags; 1430 if (fstat(ad->ad_md->adf_fd, &st_meta) == 0 && st_meta.st_size == 0) { 1431 /* for 0 length files, treat them as new. */ 1432 ad->ad_md->adf_flags |= O_TRUNC; 1433 } 1434 else { 1435 /* we have valid data in st_meta stat structure, reused it 1436 in ad_header_read 1437 */ 1438 pst = &st_meta; 1439 } 1440 } 1441 AD_SET(ad->ad_md->adf_off); 1442 1443 ad->ad_md->adf_refcount = 1; 1444 adf_lock_init(ad->ad_md); 1445 if ((ad->ad_md->adf_flags & ( O_TRUNC | O_CREAT ))) { 1446 /* 1447 * This is a new adouble header file. Initialize the structure, 1448 * instead of reading it. 1449 */ 1450 if (new_rfork(path, ad, adflags) < 0) { 1451 int err = errno; 1452 /* the file is already deleted, perm, whatever, so return an error*/ 1453 ad_close(ad, adflags); 1454 errno = err; 1455 return -1; 1456 } 1457 ad_flush(ad); 1458 } else { 1459 /* Read the adouble header in and parse it.*/ 1460 if (ad->ad_ops->ad_header_read( ad , pst) < 0 1461 || ad->ad_ops->ad_header_upgrade(ad, ad_p) < 0) 1462 { 1463 int err = errno; 1464 1465 ad_close( ad, adflags ); 1466 errno = err; 1467 return -1; 1468 } 1469 } 1470 1471 /* ****************************************** */ 1472 /* open the resource fork if SFM */ 1473sfm: 1474 if (ad->ad_flags != AD_VERSION1_SFM) { 1475 return 0; 1476 } 1477 1478 if ((adflags & ADFLAGS_DIR)) { 1479 /* no resource fork for directories / volumes XXX it's false! */ 1480 return 0; 1481 } 1482 1483 /* untrue yet but ad_close will decremente it*/ 1484 ad->ad_resource_fork.adf_refcount++; 1485 1486 if (ad_reso_fileno(ad) != -1) { /* the file is already open */ 1487 if ((oflags & ( O_RDWR | O_WRONLY)) && 1488 !(ad->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) { 1489 1490 ad_close( ad, open_df | ADFLAGS_HF); 1491 errno = EACCES; 1492 return -1; 1493 } 1494 return 0; 1495 } 1496 1497 ad_p = ad->ad_ops->ad_path( path, ADFLAGS_RF ); 1498 1499 admode = mode; 1500 st_invalid = ad_mode_st(ad_p, &admode, &st_dir); 1501 1502 if ((ad->ad_options & ADVOL_UNIXPRIV)) { 1503 admode = mode; 1504 } 1505 1506 hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR; 1507 ad->ad_resource_fork.adf_fd = open( ad_p, hoflags, admode ); 1508 1509 if (ad->ad_resource_fork.adf_fd < 0 ) { 1510 if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) { 1511 hoflags = oflags; 1512 ad->ad_resource_fork.adf_fd =open( ad_p, hoflags, admode ); 1513 } 1514 } 1515 1516 if ( ad->ad_resource_fork.adf_fd < 0) { 1517 int err = errno; 1518 1519 ad_close( ad, adflags ); 1520 errno = err; 1521 return -1; 1522 } 1523 adf_lock_init(&ad->ad_resource_fork); 1524 AD_SET(ad->ad_resource_fork.adf_off); 1525 ad->ad_resource_fork.adf_flags = hoflags; 1526 if ((oflags & O_CREAT) && !st_invalid) { 1527 /* just created, set owner if admin (root) */ 1528 ad_chown(ad_p, &st_dir); 1529 } 1530 else if (!fstat(ad->ad_resource_fork.adf_fd, &st_meta)) { 1531 ad->ad_rlen = st_meta.st_size; 1532 } 1533 return 0 ; 1534} 1535 1536/*! 1537 * @brief open metadata, possibly as root 1538 * 1539 * Return only metadata but try very hard ie at first try as user, then try as root. 1540 * 1541 * @param name name of file/dir 1542 * @param flags ADFLAGS_DIR: name is a directory \n 1543 * ADFLAGS_CREATE: force creation of header file, but only as user, not as root\n 1544 * ADFLAGS_OPENFORKS: test if name is open by another afpd process 1545 * 1546 * @param adp pointer to struct adouble 1547 * 1548 * @note caller MUST pass ADFLAGS_DIR for directories. Whether ADFLAGS_CREATE really creates 1549 * a adouble file depends on various other volume options, eg. ADVOL_CACHE 1550 */ 1551int ad_metadata(const char *name, int flags, struct adouble *adp) 1552{ 1553 uid_t uid; 1554 int ret, err, dir; 1555 int create = O_RDONLY; 1556 1557 dir = flags & ADFLAGS_DIR; 1558 1559 /* Check if we shall call ad_open with O_CREAT */ 1560 if ( (adp->ad_options & ADVOL_CACHE) 1561 && ! (adp->ad_options & ADVOL_NOADOUBLE) 1562 && (flags & ADFLAGS_CREATE) ) { 1563 create = O_CREAT | O_RDWR; 1564 } 1565 if ((ret = ad_open(name, ADFLAGS_HF | dir, create, 0666, adp)) < 0 && errno == EACCES) { 1566 uid = geteuid(); 1567 if (seteuid(0)) { 1568 LOG(log_error, logtype_default, "ad_metadata(%s): seteuid failed %s", name, strerror(errno)); 1569 errno = EACCES; 1570 return -1; 1571 } 1572 /* we are root open read only */ 1573 ret = ad_open(name, ADFLAGS_HF|ADFLAGS_RDONLY| dir, O_RDONLY, 0, adp); 1574 err = errno; 1575 if ( seteuid(uid) < 0) { 1576 LOG(log_error, logtype_default, "ad_metadata: can't seteuid back"); 1577 exit(EXITERR_SYS); 1578 } 1579 errno = err; 1580 } 1581 1582 if (!ret && (ADFLAGS_OPENFORKS & flags)) { 1583 /* 1584 we need to check if the file is open by another process. 1585 it's slow so we only do it if we have to: 1586 - it's requested. 1587 - we don't already have the answer! 1588 */ 1589 adp->ad_open_forks |= ad_openforks(adp, adp->ad_open_forks); 1590 } 1591 return ret; 1592} 1593 1594/* 1595 * @brief openat like wrapper for ad_metadata 1596 */ 1597int ad_metadataat(int dirfd, const char *name, int flags, struct adouble *adp) 1598{ 1599 int ret = 0; 1600 int cwdfd = -1; 1601 1602 if (dirfd != -1) { 1603 if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0)) { 1604 ret = -1; 1605 goto exit; 1606 } 1607 } 1608 1609 if (ad_metadata(name, flags, adp) < 0) { 1610 ret = -1; 1611 goto exit; 1612 } 1613 1614 if (dirfd != -1) { 1615 if (fchdir(cwdfd) != 0) { 1616 LOG(log_error, logtype_afpd, "ad_openat: cant chdir back, exiting"); 1617 exit(EXITERR_SYS); 1618 } 1619 } 1620 1621exit: 1622 if (cwdfd != -1) 1623 close(cwdfd); 1624 1625 return ret; 1626 1627} 1628 1629/* ----------------------------------- */ 1630static int new_rfork(const char *path, struct adouble *ad, int adflags) 1631{ 1632 const struct entry *eid; 1633 u_int16_t ashort; 1634 struct stat st; 1635 1636 ad->ad_magic = AD_MAGIC; 1637 ad->ad_version = ad->ad_flags & 0x0f0000; 1638 if (!ad->ad_version) { 1639 ad->ad_version = AD_VERSION; 1640 } 1641 1642 memset(ad->ad_filler, 0, sizeof( ad->ad_filler )); 1643 memset(ad->ad_data, 0, sizeof(ad->ad_data)); 1644 1645#if AD_VERSION == AD_VERSION2 1646 if (ad->ad_flags == AD_VERSION2) 1647 eid = entry_order2; 1648 else if (ad->ad_flags == AD_VERSION2_OSX) 1649 eid = entry_order_osx; 1650 else if (ad->ad_flags == AD_VERSION1_SFM) { 1651 ad->ad_magic = SFM_MAGIC; 1652 eid = entry_order_sfm; 1653 } 1654 else 1655#endif 1656 eid = entry_order1; 1657 1658 while (eid->id) { 1659 ad->ad_eid[eid->id].ade_off = eid->offset; 1660 ad->ad_eid[eid->id].ade_len = eid->len; 1661 eid++; 1662 } 1663 1664 /* make things invisible */ 1665 if ((ad->ad_options & ADVOL_INVDOTS) && !(adflags & ADFLAGS_CREATE) && 1666 (*path == '.') && strcmp(path, ".") && strcmp(path, "..")) 1667 { 1668 ashort = htons(ATTRBIT_INVISIBLE); 1669 ad_setattr(ad, ashort); 1670 ashort = htons(FINDERINFO_INVISIBLE); 1671 memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort)); 1672 } 1673 1674 if (ostat(path, &st, ad_get_syml_opt(ad)) < 0) { 1675 return -1; 1676 } 1677 1678 /* put something sane in the date fields */ 1679 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime); 1680 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime); 1681 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime); 1682 ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START); 1683 return 0; 1684} 1685 1686/* to do this with mmap, we need the hfs fs to understand how to mmap 1687 header files. */ 1688int ad_refresh(struct adouble *ad) 1689{ 1690 1691 if (ad_meta_fileno(ad) < 0) 1692 return -1; 1693 1694 return ad->ad_ops->ad_header_read(ad, NULL); 1695} 1696 1697int ad_openat(int dirfd, /* dir fd openat like */ 1698 const char *path, 1699 int adflags, 1700 int oflags, 1701 int mode, 1702 struct adouble *ad) 1703{ 1704 int ret = 0; 1705 int cwdfd = -1; 1706 1707 if (dirfd != -1) { 1708 if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0)) { 1709 ret = -1; 1710 goto exit; 1711 } 1712 } 1713 1714 if (ad_open(path, adflags, oflags, mode, ad) < 0) { 1715 ret = -1; 1716 goto exit; 1717 } 1718 1719 if (dirfd != -1) { 1720 if (fchdir(cwdfd) != 0) { 1721 LOG(log_error, logtype_afpd, "ad_openat: cant chdir back, exiting"); 1722 exit(EXITERR_SYS); 1723 } 1724 } 1725 1726exit: 1727 if (cwdfd != -1) 1728 close(cwdfd); 1729 1730 return ret; 1731} 1732