]> arthur.barton.de Git - netatalk.git/blobdiff - libatalk/adouble/ad_open.c
Allow opening symlinks r/w, but don't actually allow writing. Fixes test426
[netatalk.git] / libatalk / adouble / ad_open.c
index 76f2442c72af72cb74aaf5f94a4960521526a900..bc3bc3b0a039d6b70709bd6e2541b6475da9631f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: ad_open.c,v 1.67 2010-01-06 12:59:10 franklahm Exp $
+ * $Id: ad_open.c,v 1.73 2010-03-30 12:55:26 franklahm Exp $
  *
  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
  * Copyright (c) 1990,1991 Regents of The University of Michigan.
@@ -1014,8 +1014,8 @@ int ad_stat(const char *path, struct stat *stbuf)
     if (!p) {
         return -1;
     }
-
-    return stat( p, stbuf );
+//FIXME!
+    return lstat( p, stbuf );
 }
 
 /* ----------------
@@ -1037,7 +1037,7 @@ static int ad_chown(const char *path, struct stat *stbuf)
     if (default_uid != (uid_t)-1) {
         /* we are root (admin) */
         id = (default_uid)?default_uid:stbuf->st_uid;
-        ret = chown( path, id, stbuf->st_gid );
+        ret = lchown( path, id, stbuf->st_gid );
     }
 #endif
     return ret;
@@ -1265,6 +1265,7 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble
         ad->ad_adflags = adflags;
         ad->ad_resource_fork.adf_refcount = 0;
         ad->ad_data_fork.adf_refcount = 0;
+        ad->ad_data_fork.adf_syml=0;
     }
     else {
         ad->ad_open_forks = ((ad->ad_data_fork.adf_refcount > 0) ? ATTRBIT_DOPEN : 0);
@@ -1283,14 +1284,30 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble
                     admode = mode;
                 }
             }
-            ad->ad_data_fork.adf_fd =open( path, hoflags, admode );
-            if (ad->ad_data_fork.adf_fd < 0 ) {
+                
+            ad->ad_data_fork.adf_fd =open( path, hoflags | O_NOFOLLOW, admode );
+            
+            if (ad->ad_data_fork.adf_fd == -1) {
                 if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
                     hoflags = oflags;
-                    ad->ad_data_fork.adf_fd = open( path, hoflags, admode );
+                    ad->ad_data_fork.adf_fd = open( path, hoflags | O_NOFOLLOW, admode );
+                }
+                if (ad->ad_data_fork.adf_fd == -1 && errno == ELOOP) {
+                    int lsz;
+
+                    ad->ad_data_fork.adf_syml = malloc(PATH_MAX+1);
+                    lsz = readlink(path, ad->ad_data_fork.adf_syml, PATH_MAX);
+                    if (lsz <= 0) {
+                        free(ad->ad_data_fork.adf_syml);
+                        return -1;
+                    }
+                    ad->ad_data_fork.adf_syml[lsz] = 0;
+                    ad->ad_data_fork.adf_syml = realloc(ad->ad_data_fork.adf_syml,lsz+1);
+                    ad->ad_data_fork.adf_fd = -2; /* -2 means its a symlink */
                 }
             }
-            if ( ad->ad_data_fork.adf_fd < 0)
+
+            if ( ad->ad_data_fork.adf_fd == -1 )
                 return -1;
 
             AD_SET(ad->ad_data_fork.adf_off);
@@ -1337,6 +1354,8 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble
             return -1;
         }
         ad_refresh(ad);
+        /* it's not new anymore */
+        ad->ad_md->adf_flags &= ~( O_TRUNC | O_CREAT );
         ad->ad_md->adf_refcount++;
         goto sfm;
     }
@@ -1349,11 +1368,11 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble
     if (!(adflags & ADFLAGS_RDONLY)) {
         hoflags = (hoflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
     }
-    ad->ad_md->adf_fd = open( ad_p, hoflags, 0 );
+    ad->ad_md->adf_fd = open( ad_p, hoflags | O_NOFOLLOW, 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 );
+            ad->ad_md->adf_fd = open( ad_p, hoflags | O_NOFOLLOW, 0 );
         }
     }
 
@@ -1512,24 +1531,29 @@ sfm:
  *
  * @param name  name of file/dir
  * @param flags ADFLAGS_DIR: name is a directory \n
- *              ADFLAGS_CREATE: force creation of header file, but only as use, not as root
+ *              ADFLAGS_CREATE: force creation of header file, but only as user, not as root\n
+ *              ADFLAGS_OPENFORKS: test if name is open by another afpd process
+ *
  * @param adp   pointer to struct adouble
  *
- * @note caller MUST pass ADFLAGS_DIR for directories
+ * @note caller MUST pass ADFLAGS_DIR for directories. Whether ADFLAGS_CREATE really creates
+ *       a adouble file depends on various other volume options, eg. ADVOL_CACHE
  */
 int ad_metadata(const char *name, int flags, struct adouble *adp)
 {
     uid_t uid;
     int   ret, err, dir;
-    int   create = 0;
+    int   create = O_RDONLY;
 
     dir = flags & ADFLAGS_DIR;
 
     /* Check if we shall call ad_open with O_CREAT */
-    if ( ! (adp->ad_options & ADVOL_NOADOUBLE) && (flags & ADFLAGS_CREATE) )
-        create = O_CREAT;
-
-    if ((ret = ad_open(name, ADFLAGS_HF | dir, O_RDWR | create, 0666, adp)) < 0 && errno == EACCES) {
+    if ( (adp->ad_options & ADVOL_CACHE)
+         && ! (adp->ad_options & ADVOL_NOADOUBLE)
+         && (flags & ADFLAGS_CREATE) ) {
+        create = O_CREAT | O_RDWR;
+    }
+    if ((ret = ad_open(name, ADFLAGS_HF | dir, create, 0666, adp)) < 0 && errno == EACCES) {
         uid = geteuid();
         if (seteuid(0)) {
             LOG(log_error, logtype_default, "ad_metadata(%s): seteuid failed %s", name, strerror(errno));
@@ -1558,6 +1582,41 @@ int ad_metadata(const char *name, int flags, struct adouble *adp)
     return ret;
 }
 
+/*
+ * @brief openat like wrapper for ad_metadata
+ */
+int ad_metadataat(int dirfd, const char *name, int flags, struct adouble *adp)
+{
+    int ret = 0;
+    int cwdfd = -1;
+
+    if (dirfd != -1) {
+        if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0)) {
+            ret = -1;
+            goto exit;
+        }
+    }
+
+    if (ad_metadata(name, flags, adp) < 0) {
+        ret = -1;
+        goto exit;
+    }
+
+    if (dirfd != -1) {
+        if (fchdir(cwdfd) != 0) {
+            LOG(log_error, logtype_afpd, "ad_openat: cant chdir back, exiting");
+            exit(EXITERR_SYS);
+        }
+    }
+
+exit:
+    if (cwdfd != -1)
+        close(cwdfd);
+
+    return ret;
+
+}
+
 /* ----------------------------------- */
 static int new_rfork(const char *path, struct adouble *ad, int adflags)
 {
@@ -1615,7 +1674,7 @@ static int new_rfork(const char *path, struct adouble *ad, int adflags)
         memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
     }
 
-    if (stat(path, &st) < 0) {
+    if (lstat(path, &st) < 0) {
         return -1;
     }
 
@@ -1637,3 +1696,39 @@ int ad_refresh(struct adouble *ad)
 
     return ad->ad_ops->ad_header_read(ad, NULL);
 }
+
+int ad_openat(int dirfd,  /* dir fd openat like */
+              const char *path,
+              int adflags,
+              int oflags,
+              int mode,
+              struct adouble  *ad)
+{
+    int ret = 0;
+    int cwdfd = -1;
+
+    if (dirfd != -1) {
+        if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0)) {
+            ret = -1;
+            goto exit;
+        }
+    }
+
+    if (ad_open(path, adflags, oflags, mode, ad) < 0) {
+        ret = -1;
+        goto exit;
+    }
+
+    if (dirfd != -1) {
+        if (fchdir(cwdfd) != 0) {
+            LOG(log_error, logtype_afpd, "ad_openat: cant chdir back, exiting");
+            exit(EXITERR_SYS);
+        }
+    }
+
+exit:
+    if (cwdfd != -1)
+        close(cwdfd);
+
+    return ret;
+}