- if (adflags & ADFLAGS_DF) {
- if (ad_dfileno(ad) == -1) {
- if (( ad->ad_df.adf_fd =
- open( path, oflags, ad_mode( path, mode ) )) < 0 ) {
- return( -1 );
- }
- ad->ad_df.adf_off = 0;
- ad->ad_df.adf_flags = oflags;
- }
- ad->ad_df.adf_refcount++;
- }
-
- if (adflags & ADFLAGS_HF) {
- if (ad_hfileno(ad) == -1) {
- ad_p = ad_path( path, adflags );
- admode = ad_mode( ad_p, mode ); /* FIXME? */
-
- hoflags = oflags & ~O_CREAT;
- if (( ad->ad_hf.adf_fd = open( ad_p, hoflags, admode )) < 0 ) {
- if ( errno == ENOENT && hoflags != oflags ) {
- /*
- * We're expecting to create a new adouble header file,
- * here.
- */
- errno = 0;
- if (( ad->ad_hf.adf_fd = open( ad_p, oflags,
- admode )) < 0 ) {
- /*
- * Probably .AppleDouble doesn't exist, try to
- * mkdir it.
- */
- if ((errno == ENOENT) &&
- ((adflags & ADFLAGS_NOADOUBLE) == 0)) {
- if (( slash = strrchr( ad_p, '/' )) == NULL ) {
- ad_close( ad, adflags );
- return( -1 );
- }
- *slash = '\0';
- errno = 0;
- if ( ad_mkdir( ad_p, 0777 ) < 0 ) {
- ad_close( ad, adflags );
- return( -1 );
- }
- *slash = '/';
- if (( ad->ad_hf.adf_fd =
- open( ad_p, oflags, ad_mode( ad_p, mode) )) < 0 ) {
- ad_close( ad, adflags );
- return( -1 );
- }
- } else {
- ad_close( ad, adflags );
- return( -1 );
- }
- }
- ad->ad_hf.adf_flags = oflags;
- } else {
- ad_close( ad, adflags );
- return( -1 );
- }
- } else if ((fstat(ad->ad_hf.adf_fd, &st) == 0) &&
- (st.st_size == 0)) {
- /* for 0 length files, treat them as new. */
- ad->ad_hf.adf_flags = oflags;
- } else {
- ad->ad_hf.adf_flags = hoflags;
- }
- ad->ad_hf.adf_off = 0;
-
- /*
- * This is a new adouble header file. Initialize the structure,
- * instead of reading it.
- */
- memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
- if ( ad->ad_hf.adf_flags & ( O_TRUNC | O_CREAT )) {
- struct timeval tv;
-
- ad->ad_magic = AD_MAGIC;
- ad->ad_version = AD_VERSION;
- memset(ad->ad_filler, 0, sizeof( ad->ad_filler ));
-
-#ifdef USE_MMAPPED_HEADERS
- /* truncate the header file and mmap it. */
- ftruncate(ad->ad_hf.adf_fd, AD_DATASZ);
- ad->ad_data = mmap(NULL, AD_DATASZ, PROT_READ | PROT_WRITE,
- MAP_SHARED, ad->ad_hf.adf_fd, 0);
- if (ad->ad_data == MAP_FAILED) {
- ad_close(ad, adflags);
- return -1;
- }
-#else /* USE_MMAPPED_HEADERS */
- memset(ad->ad_data, 0, sizeof(ad->ad_data));
-#endif /* USE_MMAPPED_HEADERS */
-
- eid = entry_order;
- while (eid->id) {
- ad->ad_eid[eid->id].ade_off = eid->offset;
- ad->ad_eid[eid->id].ade_len = eid->len;
- eid++;
- }
-
- /* put something sane in the directory finderinfo */
- if (adflags & ADFLAGS_DIR) {
- /* set default view */
- ashort = htons(FINDERINFO_CLOSEDVIEW);
- memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF,
- &ashort, sizeof(ashort));
- } else {
- /* set default creator/type fields */
- memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,
- "TEXT", 4);
- memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,
- "UNIX", 4);
- }
-
- /* make things invisible */
- if ((*path == '.') && strcmp(path, ".") && strcmp(path, "..")) {
- ashort = htons(ATTRBIT_INVISIBLE);
- ad_setattr(ad, ashort);
- ashort = htons(FINDERINFO_INVISIBLE);
- memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF,
- &ashort, sizeof(ashort));
- }
-
- if (gettimeofday(&tv, NULL) < 0) {
- ad_close(ad, adflags);
- return -1;
- }
-
- /* put something sane in the date fields */
- ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, tv.tv_sec);
- ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, tv.tv_sec);
- ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, tv.tv_sec);
- ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
-
- } else {
- /*
- * Read the adouble header in and parse it.
- */
- if ((ad_header_read( ad ) < 0)
+ if ((adflags & ADFLAGS_DF)) {
+ if (ad_data_fileno(ad) == -1) {
+ hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
+ admode = mode;
+ if ((oflags & O_CREAT)) {
+ st_invalid = ad_mode_st(path, &admode, &st_dir);
+ if ((ad->ad_options & ADVOL_UNIXPRIV)) {
+ admode = mode;
+ }
+ }
+ ad->ad_data_fork.adf_fd =open( path, hoflags, admode );
+ if (ad->ad_data_fork.adf_fd < 0 ) {
+ if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
+ hoflags = oflags;
+ ad->ad_data_fork.adf_fd = open( path, hoflags, admode );
+ }
+ }
+ if ( ad->ad_data_fork.adf_fd < 0)
+ return -1;
+
+ AD_SET(ad->ad_data_fork.adf_off);
+ ad->ad_data_fork.adf_flags = hoflags;
+ if (!st_invalid) {
+ /* just created, set owner if admin (root) */
+ ad_chown(path, &st_dir);
+ }
+ }
+ else {
+ /* the file is already open... but */
+ if ((oflags & ( O_RDWR | O_WRONLY)) && /* we want write access */
+ !(ad->ad_data_fork.adf_flags & ( O_RDWR | O_WRONLY))) /* and it was denied the first time */
+ {
+ errno = EACCES;
+ return -1;
+ }
+ /* FIXME
+ * for now ad_open is never called with O_TRUNC or O_EXCL if the file is
+ * already open. Should we check for it? ie
+ * O_EXCL --> error
+ * O_TRUNC --> truncate the fork.
+ * idem for ressource fork.
+ */
+ }
+ open_df = ADFLAGS_DF;
+ ad->ad_data_fork.adf_refcount++;
+ }
+
+ if (!(adflags & ADFLAGS_HF))
+ return 0;
+
+ /* ****************************************** */
+
+ if (ad_meta_fileno(ad) != -1) { /* the file is already open */
+ if ((oflags & ( O_RDWR | O_WRONLY)) &&
+ !(ad->ad_md->adf_flags & ( O_RDWR | O_WRONLY))) {
+ if (open_df) {
+ /* don't call with ADFLAGS_HF because we didn't open ressource fork */
+ ad_close( ad, open_df );
+ }
+ errno = EACCES;
+ return -1;
+ }
+ ad_refresh(ad);
+ ad->ad_md->adf_refcount++;
+ goto sfm;
+ }
+
+ ad_p = ad->ad_ops->ad_path( path, adflags );
+
+ hoflags = oflags & ~(O_CREAT | O_EXCL);
+ if (!(adflags & ADFLAGS_RDONLY)) {
+ hoflags = (hoflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
+ }
+ ad->ad_md->adf_fd = open( ad_p, hoflags, 0 );
+ if (ad->ad_md->adf_fd < 0 ) {
+ if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
+ hoflags = oflags & ~(O_CREAT | O_EXCL);
+ ad->ad_md->adf_fd = open( ad_p, hoflags, 0 );
+ }
+ }
+
+ if ( ad->ad_md->adf_fd < 0 ) {
+ if (errno == ENOENT && (oflags & O_CREAT) ) {
+ /*
+ * We're expecting to create a new adouble header file,
+ * here.
+ * if ((oflags & O_CREAT) ==> (oflags & O_RDWR)
+ */
+ admode = mode;
+ errno = 0;
+ st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
+ if ((ad->ad_options & ADVOL_UNIXPRIV)) {
+ admode = mode;
+ }
+ admode = ad_hf_mode(admode);
+ if ( errno == ENOENT && !(adflags & ADFLAGS_NOADOUBLE) && ad->ad_flags != AD_VERSION2_OSX) {
+ if (ad->ad_ops->ad_mkrf( ad_p) < 0) {
+ return ad_error(ad, adflags);
+ }
+ admode = mode;
+ st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
+ if ((ad->ad_options & ADVOL_UNIXPRIV)) {
+ admode = mode;
+ }
+ admode = ad_hf_mode(admode);
+ }
+ /* retry with O_CREAT */
+ ad->ad_md->adf_fd = open( ad_p, oflags,admode );
+ if ( ad->ad_md->adf_fd < 0 ) {
+ return ad_error(ad, adflags);
+ }
+ ad->ad_md->adf_flags = oflags;
+ /* just created, set owner if admin owner (root) */
+ if (!st_invalid) {
+ ad_chown(ad_p, &st_dir);
+ }
+ }
+ else {
+ return ad_error(ad, adflags);
+ }
+ } else {
+ ad->ad_md->adf_flags = hoflags;
+ if (fstat(ad->ad_md->adf_fd, &st_meta) == 0 && st_meta.st_size == 0) {
+ /* for 0 length files, treat them as new. */
+ ad->ad_md->adf_flags |= O_TRUNC;
+ }
+ else {
+ /* we have valid data in st_meta stat structure, reused it
+ in ad_header_read
+ */
+ pst = &st_meta;
+ }
+ }
+ AD_SET(ad->ad_md->adf_off);
+
+ memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
+ ad->ad_md->adf_refcount++;
+ if ((ad->ad_md->adf_flags & ( O_TRUNC | O_CREAT ))) {
+ /*
+ * This is a new adouble header file. Initialize the structure,
+ * instead of reading it.
+ */
+ if (new_rfork(path, ad, adflags) < 0) {
+ int err = errno;
+ /* the file is already deleted, perm, whatever, so return an error*/
+ ad_close(ad, adflags);
+ errno = err;
+ return -1;
+ }
+ ad_flush(ad);
+ } else {
+ /* Read the adouble header in and parse it.*/
+ if (ad->ad_ops->ad_header_read( ad , pst) < 0
+ || ad->ad_ops->ad_header_upgrade(ad, ad_p) < 0)
+ {
+ int err = errno;
+
+ ad_close( ad, adflags );
+ errno = err;
+ return -1;
+ }
+ }
+
+ /* ****************************************** */
+ /* open the resource fork if SFM */
+sfm:
+ if (ad->ad_flags != AD_VERSION1_SFM) {
+ return 0;
+ }
+
+ if ((adflags & ADFLAGS_DIR)) {
+ /* no resource fork for directories / volumes XXX it's false! */
+ return 0;
+ }
+
+ /* untrue yet but ad_close will decremente it*/
+ ad->ad_resource_fork.adf_refcount++;
+
+ if (ad_reso_fileno(ad) != -1) { /* the file is already open */
+ if ((oflags & ( O_RDWR | O_WRONLY)) &&
+ !(ad->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
+
+ ad_close( ad, open_df | ADFLAGS_HF);
+ errno = EACCES;
+ return -1;
+ }
+ return 0;
+ }
+
+ ad_p = ad->ad_ops->ad_path( path, ADFLAGS_RF );
+
+ hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
+ ad->ad_resource_fork.adf_fd = open( ad_p, hoflags, admode );
+ admode = mode;
+ st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
+
+ if ((ad->ad_options & ADVOL_UNIXPRIV)) {
+ admode = mode;
+ }
+
+ if (ad->ad_resource_fork.adf_fd < 0 ) {
+ if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
+ hoflags = oflags;
+ ad->ad_resource_fork.adf_fd =open( ad_p, hoflags, admode );
+ }
+ }
+
+ if ( ad->ad_resource_fork.adf_fd < 0) {
+ int err = errno;
+
+ ad_close( ad, adflags );
+ errno = err;
+ return -1;
+ }
+
+ AD_SET(ad->ad_resource_fork.adf_off);
+ ad->ad_resource_fork.adf_flags = hoflags;
+ if ((oflags & O_CREAT) && !st_invalid) {
+ /* just created, set owner if admin (root) */
+ ad_chown(ad_p, &st_dir);
+ }
+ else if (!fstat(ad->ad_resource_fork.adf_fd, &st_meta)) {
+ ad->ad_rlen = st_meta.st_size;
+ }
+ return 0 ;
+}
+
+/* -----------------------------------
+ * return only metadata but try very hard
+ */
+int ad_metadata(const char *name, int flags, struct adouble *adp)
+{
+ uid_t uid;
+ int ret, err;
+ int dir = flags & ADFLAGS_DIR;
+
+ /* Open with O_CREAT, thus enumarating a dir will create missing adouble files, see: */
+ /* http://marc.info/?l=netatalk-devel&m=124039156832408&w=2 */
+ if ((ret = ad_open(name, ADFLAGS_HF | dir, O_RDWR | O_CREAT, 0666, adp)) < 0 && errno == EACCES) {
+ uid = geteuid();
+ if (seteuid(0)) {
+ LOG(log_error, logtype_default, "ad_metadata(%s): seteuid failed %s", name, strerror(errno));
+ errno = EACCES;
+ return -1;
+ }
+ /* we are root open read only */
+ ret = ad_open(name, ADFLAGS_HF|ADFLAGS_RDONLY| dir, O_RDONLY, 0, adp);
+ err = errno;
+ if ( seteuid(uid) < 0) {
+ LOG(log_error, logtype_default, "ad_metadata: can't seteuid back");
+ exit(EXITERR_SYS);
+ }
+ errno = err;
+ }
+
+ if (!ret && (ADFLAGS_OPENFORKS & flags)) {
+ /*
+ we need to check if the file is open by another process.
+ it's slow so we only do it if we have to:
+ - it's requested.
+ - we don't already have the answer!
+ */
+ adp->ad_open_forks |= ad_openforks(adp, adp->ad_open_forks);
+ }
+ return ret;
+}
+
+/* ----------------------------------- */
+static int new_rfork(const char *path, struct adouble *ad, int adflags)
+{
+ const struct entry *eid;
+ u_int16_t ashort;
+ struct stat st;
+
+ ad->ad_magic = AD_MAGIC;
+ ad->ad_version = ad->ad_flags & 0x0f0000;
+ if (!ad->ad_version) {
+ ad->ad_version = AD_VERSION;
+ }
+
+ memset(ad->ad_filler, 0, sizeof( ad->ad_filler ));
+ memset(ad->ad_data, 0, sizeof(ad->ad_data));
+