static int getforkparams(struct ofork *ofork, u_int16_t bitmap, char *buf, size_t *buflen) { struct path path; struct stat *st; struct adouble *adp; struct dir *dir; struct vol *vol; /* can only get the length of the opened fork */ if ( ( (bitmap & ((1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN))) && (ofork->of_flags & AFPFORK_RSRC)) || ( (bitmap & ((1<<FILPBIT_RFLEN) | (1<<FILPBIT_EXTRFLEN))) && (ofork->of_flags & AFPFORK_DATA))) { return( AFPERR_BITMAP ); } if ( ad_reso_fileno( ofork->of_ad ) == -1 ) { /* META ? */ adp = NULL; } else { adp = ofork->of_ad; } vol = ofork->of_vol; dir = dirlookup(vol, ofork->of_did); if (NULL == (path.u_name = mtoupath(vol, of_name(ofork), dir->d_did, utf8_encoding()))) { return( AFPERR_MISC ); } path.m_name = of_name(ofork); path.id = 0; st = &path.st; if ( bitmap & ( (1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN) | (1<<FILPBIT_FNUM) | (1 << FILPBIT_CDATE) | (1 << FILPBIT_MDATE) | (1 << FILPBIT_BDATE))) { if ( ad_data_fileno( ofork->of_ad ) <= 0 ) { /* 0 is for symlink */ if (movecwd(vol, dir) < 0) return( AFPERR_NOOBJ ); if ( lstat( path.u_name, st ) < 0 ) return( AFPERR_NOOBJ ); } else { if ( fstat( ad_data_fileno( ofork->of_ad ), st ) < 0 ) { return( AFPERR_BITMAP ); } } } return getmetadata(vol, bitmap, &path, dir, buf, buflen, adp ); }
/* use refcounts so that we don't have to re-establish fcntl locks. */ int ad_close( struct adouble *ad, int adflags) { int err = 0; if ((adflags & ADFLAGS_DF) && (ad_data_fileno(ad) >= 0 || ad_data_fileno(ad) == -2) /* -2 means symlink */ && --ad->ad_data_fork.adf_refcount == 0) { if (ad->ad_data_fork.adf_syml != NULL) { free(ad->ad_data_fork.adf_syml); ad->ad_data_fork.adf_syml = 0; } else { if ( close( ad_data_fileno(ad) ) < 0 ) err = -1; } ad_data_fileno(ad) = -1; adf_lock_free(&ad->ad_data_fork); } if (!( adflags & ADFLAGS_HF )) { return err; } /* meta /resource fork */ if ( ad_meta_fileno(ad) != -1 && !(--ad->ad_md->adf_refcount)) { if ( close( ad_meta_fileno(ad) ) < 0 ) { err = -1; } ad_meta_fileno(ad) = -1; adf_lock_free(ad->ad_md); } if (ad->ad_flags != AD_VERSION1_SFM) { return err; } if ((adflags & ADFLAGS_DIR)) { return err; } if ( ad_reso_fileno(ad) != -1 && !(--ad->ad_resource_fork.adf_refcount)) { if ( close( ad_reso_fileno(ad) ) < 0 ) { err = -1; } ad_reso_fileno(ad) = -1; adf_lock_free(&ad->ad_resource_fork); } return err; }
/*! * Return if a file is open by another process. * * Optimized for the common case: * - there's no locks held by another process (clients) * - or we already know the answer and don't need to test (attrbits) * * @param ad (rw) handle * @param attrbits (r) forks opened by us * @returns bitflags ATTRBIT_DOPEN | ATTRBIT_ROPEN if * other process has fork of file opened */ uint16_t ad_openforks(struct adouble *ad, uint16_t attrbits) { uint16_t ret = 0; struct ad_fd *adf; off_t off; off_t len; if (ad_data_fileno(ad) == -1) return 0; if (!(attrbits & (ATTRBIT_DOPEN | ATTRBIT_ROPEN))) { /* Test all 4 locks at once */ off = AD_FILELOCK_OPEN_WR; len = 4; if (testlock(&ad->ad_data_fork, off, len) == 0) return 0; } /* either there's a lock or we already know one fork is open */ if (!(attrbits & ATTRBIT_DOPEN)) { off = AD_FILELOCK_OPEN_WR; ret = testlock(&ad->ad_data_fork, off, 2) > 0 ? ATTRBIT_DOPEN : 0; } if (!(attrbits & ATTRBIT_ROPEN)) { off = AD_FILELOCK_RSRC_OPEN_WR; ret |= testlock(&ad->ad_data_fork, off, 2) > 0? ATTRBIT_ROPEN : 0; } return ret; }
/* --------------------- */ void ad_unlock(struct adouble *ad, const int fork, int unlckbrl) { LOG(log_debug, logtype_default, "ad_unlock(unlckbrl: %d): BEGIN", unlckbrl); if (ad_data_fileno(ad) != -1) { adf_unlock(ad, &ad->ad_data_fork, fork, unlckbrl); } if (ad_reso_fileno(ad) != -1) { adf_unlock(ad, &ad->ad_resource_fork, fork, unlckbrl); } LOG(log_debug, logtype_default, "ad_unlock: END"); }
off_t ad_size(const struct adouble *ad, const uint32_t eid) { if (eid == ADEID_DFORK) { struct stat st; if (ad->ad_data_fork.adf_syml) return strlen(ad->ad_data_fork.adf_syml); if (fstat(ad_data_fileno(ad), &st) < 0) return 0; return st.st_size; } #if 0 return ad_getentrylen(ad, eid); #endif return ad->ad_rlen; }
/* ------------------------------- */ 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 fork_setmode(const AFPObj *obj, struct adouble *adp, int eid, int access, int ofrefnum) { int ret; int readset; int writeset; int denyreadset; int denywriteset; if (! (access & (OPENACC_WR | OPENACC_RD | OPENACC_DWR | OPENACC_DRD))) { return ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_OPEN_NONE, 1, ofrefnum); } if ((access & (OPENACC_RD | OPENACC_DRD))) { if ((readset = ad_testlock(adp, eid, AD_FILELOCK_OPEN_RD)) <0) return readset; if ((denyreadset = ad_testlock(adp, eid, AD_FILELOCK_DENY_RD)) <0) return denyreadset; if ((access & OPENACC_RD) && denyreadset) { errno = EACCES; return -1; } if ((access & OPENACC_DRD) && readset) { errno = EACCES; return -1; } /* boolean logic is not enough, because getforkmode is not always telling the * true */ if ((access & OPENACC_RD)) { ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_OPEN_RD, 1, ofrefnum); if (ret) return ret; } if ((access & OPENACC_DRD)) { ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_DENY_RD, 1, ofrefnum); if (ret) return ret; } } /* ------------same for writing -------------- */ if ((access & (OPENACC_WR | OPENACC_DWR))) { if ((writeset = ad_testlock(adp, eid, AD_FILELOCK_OPEN_WR)) <0) return writeset; if ((denywriteset = ad_testlock(adp, eid, AD_FILELOCK_DENY_WR)) <0) return denywriteset; if ((access & OPENACC_WR) && denywriteset) { errno = EACCES; return -1; } if ((access & OPENACC_DWR) && writeset) { errno = EACCES; return -1; } if ((access & OPENACC_WR)) { ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_OPEN_WR, 1, ofrefnum); if (ret) return ret; } if ((access & OPENACC_DWR)) { ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_DENY_WR, 1, ofrefnum); if (ret) return ret; } } /* * Fix for Bug 560: put the Solaris share reservation after our locking stuff. * Note that this still leaves room for a race condition where between placing our own * locks above and putting the Solaris share move below another client puts a lock. * We then end up with set locks from above and return with an error code, the proper * fix requires a sane cleanup function for the error path in this function. */ #ifdef HAVE_FSHARE_T fshare_t shmd; if (obj->options.flags & OPTION_SHARE_RESERV) { shmd.f_access = (access & OPENACC_RD ? F_RDACC : 0) | (access & OPENACC_WR ? F_WRACC : 0); if (shmd.f_access == 0) /* we must give an access mode, otherwise fcntl will complain */ shmd.f_access = F_RDACC; shmd.f_deny = (access & OPENACC_DRD ? F_RDDNY : F_NODNY) | (access & OPENACC_DWR) ? F_WRDNY : 0; shmd.f_id = ofrefnum; int fd = (eid == ADEID_DFORK) ? ad_data_fileno(adp) : ad_reso_fileno(adp); if (fd != -1 && fd != AD_SYMLINK && fcntl(fd, F_SHARE, &shmd) != 0) { LOG(log_debug, logtype_afpd, "fork_setmode: fcntl: %s", strerror(errno)); errno = EACCES; return -1; } } #endif return 0; }
static int fork_setmode(const AFPObj *obj, struct adouble *adp, int eid, int access, int ofrefnum) { int ret; int readset; int writeset; int denyreadset; int denywriteset; #ifdef HAVE_FSHARE_T fshare_t shmd; if (obj->options.flags & OPTION_SHARE_RESERV) { shmd.f_access = (access & OPENACC_RD ? F_RDACC : 0) | (access & OPENACC_WR ? F_WRACC : 0); if (shmd.f_access == 0) /* we must give an access mode, otherwise fcntl will complain */ shmd.f_access = F_RDACC; shmd.f_deny = (access & OPENACC_DRD ? F_RDDNY : F_NODNY) | (access & OPENACC_DWR) ? F_WRDNY : 0; shmd.f_id = ofrefnum; int fd = (eid == ADEID_DFORK) ? ad_data_fileno(adp) : ad_reso_fileno(adp); if (fd != -1 && fd != AD_SYMLINK && fcntl(fd, F_SHARE, &shmd) != 0) { LOG(log_debug, logtype_afpd, "fork_setmode: fcntl: %s", strerror(errno)); errno = EACCES; return -1; } } #endif if (! (access & (OPENACC_WR | OPENACC_RD | OPENACC_DWR | OPENACC_DRD))) { return ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_OPEN_NONE, 1, ofrefnum); } if ((access & (OPENACC_RD | OPENACC_DRD))) { if ((readset = ad_testlock(adp, eid, AD_FILELOCK_OPEN_RD)) <0) return readset; if ((denyreadset = ad_testlock(adp, eid, AD_FILELOCK_DENY_RD)) <0) return denyreadset; if ((access & OPENACC_RD) && denyreadset) { errno = EACCES; return -1; } if ((access & OPENACC_DRD) && readset) { errno = EACCES; return -1; } /* boolean logic is not enough, because getforkmode is not always telling the * true */ if ((access & OPENACC_RD)) { ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_OPEN_RD, 1, ofrefnum); if (ret) return ret; } if ((access & OPENACC_DRD)) { ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_DENY_RD, 1, ofrefnum); if (ret) return ret; } } /* ------------same for writing -------------- */ if ((access & (OPENACC_WR | OPENACC_DWR))) { if ((writeset = ad_testlock(adp, eid, AD_FILELOCK_OPEN_WR)) <0) return writeset; if ((denywriteset = ad_testlock(adp, eid, AD_FILELOCK_DENY_WR)) <0) return denywriteset; if ((access & OPENACC_WR) && denywriteset) { errno = EACCES; return -1; } if ((access & OPENACC_DWR) && writeset) { errno = EACCES; return -1; } if ((access & OPENACC_WR)) { ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_OPEN_WR, 1, ofrefnum); if (ret) return ret; } if ((access & OPENACC_DWR)) { ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_DENY_WR, 1, ofrefnum); if (ret) return ret; } } return 0; }