/* rebuild the adouble header * XXX should be in a separate file ? */ int ad_rebuild_adouble_header(struct adouble *ad) { u_int32_t eid; u_int32_t temp; u_int16_t nent; char *buf, *nentp; /* * Rebuild any header information that might have changed. */ buf = ad->ad_data; temp = htonl( ad->ad_magic ); memcpy(buf, &temp, sizeof( temp )); buf += sizeof( temp ); temp = htonl( ad->ad_version ); memcpy(buf, &temp, sizeof( temp )); buf += sizeof( temp ); memcpy(buf, ad->ad_filler, sizeof( ad->ad_filler )); buf += sizeof( ad->ad_filler ); nentp = buf; buf += sizeof( nent ); for ( eid = 0, nent = 0; eid < ADEID_MAX; eid++ ) { if ( ad->ad_eid[ eid ].ade_off == 0 ) { continue; } temp = htonl( EID_DISK(eid) ); memcpy(buf, &temp, sizeof( temp )); buf += sizeof( temp ); temp = htonl( ad->ad_eid[ eid ].ade_off ); memcpy(buf, &temp, sizeof( temp )); buf += sizeof( temp ); temp = htonl( ad->ad_eid[ eid ].ade_len ); memcpy(buf, &temp, sizeof( temp )); buf += sizeof( temp ); nent++; } nent = htons( nent ); memcpy(nentp, &nent, sizeof( nent )); return ad_getentryoff(ad, ADEID_RFORK); }
/* ------------------------------- */ int ad_readfile_init(const struct adouble *ad, const int eid, off_t *off, const int end) { int fd; if (end) *off = ad_size(ad, eid) - *off; if (eid == ADEID_DFORK) { fd = ad_data_fileno(ad); } else { *off += ad_getentryoff(ad, eid); fd = ad_reso_fileno(ad); } return fd; }
static int ad_conv_v22ea_hf(const char *path, const struct stat *sp, const struct vol *vol) { EC_INIT; struct adouble adv2; struct adouble adea; const char *adpath; int adflags; uint32_t ctime, mtime, afpinfo = 0; char *emptyad; LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): BEGIN", fullpathname(path)); ad_init(&adea, vol); ad_init_old(&adv2, AD_VERSION2, adea.ad_options); adflags = S_ISDIR(sp->st_mode) ? ADFLAGS_DIR : 0; /* Open and lock adouble:v2 file */ EC_ZERO( ad_open(&adv2, path, adflags | ADFLAGS_HF | ADFLAGS_RDWR) ); EC_NEG1_LOG( ad_tmplock(&adv2, ADEID_RFORK, ADLOCK_WR | ADLOCK_FILELOCK, 0, 0, 0) ); EC_NEG1_LOG( adv2.ad_ops->ad_header_read(path, &adv2, sp) ); /* Check if it's a non-empty header */ if (S_ISREG(sp->st_mode)) emptyad = &emptyfilad[0]; else emptyad = &emptydirad[0]; if (ad_getentrylen(&adv2, ADEID_COMMENT) != 0) goto copy; if (ad_getentryoff(&adv2, ADEID_FINDERI) && (ad_getentrylen(&adv2, ADEID_FINDERI) == ADEDLEN_FINDERI) && (memcmp(ad_entry(&adv2, ADEID_FINDERI), emptyad, ADEDLEN_FINDERI) != 0)) goto copy; if (ad_getentryoff(&adv2, ADEID_FILEDATESI)) { EC_ZERO_LOG( ad_getdate(&adv2, AD_DATE_CREATE | AD_DATE_UNIX, &ctime) ); EC_ZERO_LOG( ad_getdate(&adv2, AD_DATE_MODIFY | AD_DATE_UNIX, &mtime) ); if ((ctime != mtime) || (mtime != sp->st_mtime)) goto copy; } if (ad_getentryoff(&adv2, ADEID_AFPFILEI)) { if (memcmp(ad_entry(&adv2, ADEID_AFPFILEI), &afpinfo, ADEDLEN_AFPFILEI) != 0) goto copy; } LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): default adouble", fullpathname(path), ret); goto EC_CLEANUP; copy: /* Create a adouble:ea meta EA */ LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): copying adouble", fullpathname(path), ret); EC_ZERO_LOGSTR( ad_open(&adea, path, adflags | ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE), "ad_conv_v22ea_hf(\"%s\"): error creating metadata EA: %s", fullpathname(path), strerror(errno)); EC_ZERO_LOG( ad_copy_header(&adea, &adv2) ); ad_flush(&adea); EC_CLEANUP: EC_ZERO_LOG( ad_close(&adv2, ADFLAGS_HF | ADFLAGS_SETSHRMD) ); EC_ZERO_LOG( ad_close(&adea, ADFLAGS_HF | ADFLAGS_SETSHRMD) ); LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): END: %d", fullpathname(path), ret); EC_EXIT; }
int ad_tmplock(struct adouble *ad, uint32_t eid, int locktype, off_t off, off_t len, int fork) { struct flock lock; struct ad_fd *adf; int err; int type; LOG(log_debug, logtype_default, "ad_tmplock(%s, %s, off: %jd (%s), len: %jd): BEGIN", eid == ADEID_DFORK ? "data" : "reso", locktypetostr(locktype), (intmax_t)off, shmdstrfromoff(off), (intmax_t)len); lock.l_start = off; type = locktype; if (eid == ADEID_DFORK) { adf = &ad->ad_data_fork; } else { adf = &ad->ad_resource_fork; if (adf->adf_fd == -1) { /* there's no resource fork. return success */ err = 0; goto exit; } /* if ADLOCK_FILELOCK we want a lock from offset 0 * it's used when deleting a file: * in open we put read locks on meta datas * in delete a write locks on the whole file * so if the file is open by somebody else it fails */ if (!(type & ADLOCK_FILELOCK)) lock.l_start += ad_getentryoff(ad, eid); } if (!(adf->adf_flags & O_RDWR) && (type & ADLOCK_WR)) { type = (type & ~ADLOCK_WR) | ADLOCK_RD; } lock.l_type = XLATE_FCNTL_LOCK(type & ADLOCK_MASK); lock.l_whence = SEEK_SET; lock.l_len = len; /* see if it's locked by another fork. */ if (fork && adf_findxlock(adf, fork, ADLOCK_WR | ((type & ADLOCK_WR) ? ADLOCK_RD : 0), lock.l_start, lock.l_len) > -1) { errno = EACCES; err = -1; goto exit; } /* okay, we might have ranges byte-locked. we need to make sure that * we restore the appropriate ranges once we're done. so, we check * for overlap on an unlock and relock. * XXX: in the future, all the byte locks will be sorted and contiguous. * we just want to upgrade all the locks and then downgrade them * here. */ err = set_lock(adf->adf_fd, F_SETLK, &lock); if (!err && (lock.l_type == F_UNLCK)) adf_relockrange(adf, adf->adf_fd, lock.l_start, len); exit: LOG(log_debug, logtype_default, "ad_tmplock: END: %d", err); return err; }
int ad_lock(struct adouble *ad, uint32_t eid, int locktype, off_t off, off_t len, int fork) { struct flock lock; struct ad_fd *adf; adf_lock_t *adflock; int oldlock; int i; int type; int ret = 0, fcntl_lock_err = 0; LOG(log_debug, logtype_default, "ad_lock(%s, %s, off: %jd (%s), len: %jd): BEGIN", eid == ADEID_DFORK ? "data" : "reso", locktypetostr(locktype), (intmax_t)off, shmdstrfromoff(off), (intmax_t)len); if ((locktype & ADLOCK_FILELOCK) && (len != 1)) AFP_PANIC("lock API error"); type = locktype; if (eid == ADEID_DFORK) { adf = &ad->ad_data_fork; lock.l_start = off; } else { /* rfork */ if (type & ADLOCK_FILELOCK) { adf = &ad->ad_data_fork; lock.l_start = rf2off(off); } else { adf = ad->ad_rfp; lock.l_start = off + ad_getentryoff(ad, ADEID_RFORK); } } /* NOTE: we can't write lock a read-only file. on those, we just * make sure that we have a read lock set. that way, we at least prevent * someone else from really setting a deny read/write on the file. */ if (!(adf->adf_flags & O_RDWR) && (type & ADLOCK_WR)) { type = (type & ~ADLOCK_WR) | ADLOCK_RD; } lock.l_type = XLATE_FCNTL_LOCK(type & ADLOCK_MASK); lock.l_whence = SEEK_SET; lock.l_len = len; /* byte_lock(len=-1) lock whole file */ if (len == BYTELOCK_MAX) { lock.l_len -= lock.l_start; /* otherwise EOVERFLOW error */ } /* see if it's locked by another fork. * NOTE: this guarantees that any existing locks must be at most * read locks. we use ADLOCK_WR/RD because F_RD/WRLCK aren't * guaranteed to be ORable. */ if (adf_findxlock(adf, fork, ADLOCK_WR | ((type & ADLOCK_WR) ? ADLOCK_RD : 0), lock.l_start, lock.l_len) > -1) { errno = EACCES; ret = -1; goto exit; } /* look for any existing lock that we may have */ i = adf_findlock(adf, fork, ADLOCK_RD | ADLOCK_WR, lock.l_start, lock.l_len); adflock = (i < 0) ? NULL : adf->adf_lock + i; /* here's what we check for: 1) we're trying to re-lock a lock, but we didn't specify an update. 2) we're trying to free only part of a lock. 3) we're trying to free a non-existent lock. */ if ( (!adflock && (lock.l_type == F_UNLCK)) || (adflock && !(type & ADLOCK_UPGRADE) && ((lock.l_type != F_UNLCK) || (adflock->lock.l_start != lock.l_start) || (adflock->lock.l_len != lock.l_len) )) ) { errno = EINVAL; ret = -1; goto exit; } /* now, update our list of locks */ /* clear the lock */ if (lock.l_type == F_UNLCK) { adf_freelock(adf, i); goto exit; } /* attempt to lock the file. */ if (set_lock(adf->adf_fd, F_SETLK, &lock) < 0) { ret = -1; goto exit; } /* we upgraded this lock. */ if (adflock && (type & ADLOCK_UPGRADE)) { memcpy(&adflock->lock, &lock, sizeof(lock)); goto exit; } /* it wasn't an upgrade */ oldlock = -1; if (lock.l_type == F_RDLCK) { oldlock = adf_findxlock(adf, fork, ADLOCK_RD, lock.l_start, lock.l_len); } /* no more space. this will also happen if lockmax == lockcount == 0 */ if (adf->adf_lockmax == adf->adf_lockcount) { adf_lock_t *tmp = (adf_lock_t *) realloc(adf->adf_lock, sizeof(adf_lock_t)* (adf->adf_lockmax + ARRAY_BLOCK_SIZE)); if (!tmp) { ret = fcntl_lock_err = -1; goto exit; } adf->adf_lock = tmp; adf->adf_lockmax += ARRAY_BLOCK_SIZE; } adflock = adf->adf_lock + adf->adf_lockcount; /* fill in fields */ memcpy(&adflock->lock, &lock, sizeof(lock)); adflock->user = fork; if (oldlock > -1) { adflock->refcount = (adf->adf_lock + oldlock)->refcount; } else if ((adflock->refcount = calloc(1, sizeof(int))) == NULL) { ret = fcntl_lock_err = 1; goto exit; } (*adflock->refcount)++; adf->adf_lockcount++; exit: if (ret != 0) { if (fcntl_lock_err != 0) { lock.l_type = F_UNLCK; set_lock(adf->adf_fd, F_SETLK, &lock); } } LOG(log_debug, logtype_default, "ad_lock: END: %d", ret); return ret; }