]> arthur.barton.de Git - netatalk.git/blobdiff - etc/afpd/file.c
Configurable symlink behaviour
[netatalk.git] / etc / afpd / file.c
index 93b1a169612ab5c4f25e6fd69fc4fb01c0e8baa2..9f1ff318b1b8729928ae06f3fa0f32f7305f38ed 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: file.c,v 1.141 2010/03/12 15:16:49 franklahm Exp $
- *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
  */
@@ -39,14 +37,16 @@ char *strchr (), *strrchr ();
 #include <atalk/util.h>
 #include <atalk/cnid.h>
 #include <atalk/unix.h>
+#include <atalk/globals.h>
+#include <atalk/fce_api.h>
 
 #include "directory.h"
+#include "dircache.h"
 #include "desktop.h"
 #include "volume.h"
 #include "fork.h"
 #include "file.h"
 #include "filedir.h"
-#include "globals.h"
 #include "unix.h"
 
 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
@@ -115,7 +115,7 @@ void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *a
         }
     }
 
-    if (islink){
+    if (islink && !vol_syml_opt(vol)) {
         u_int16_t linkflag;
         memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
         linkflag |= htons(FINDERINFO_ISALIAS);
@@ -166,8 +166,8 @@ char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t
     else {
         u_int16_t temp;
 
-        if (aint > 255)  /* FIXME safeguard, anyway if no ascii char it's game over*/
-           aint = 255;
+        if (aint > UTF8FILELEN_EARLY)  /* FIXME safeguard, anyway if no ascii char it's game over*/
+           aint = UTF8FILELEN_EARLY;
 
         utf8 = vol->v_kTextEncoding;
         memcpy(data, &utf8, sizeof(utf8));
@@ -201,19 +201,39 @@ char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t
                                  (1 << FILPBIT_FNUM) |\
                                  (1 << FILPBIT_UNIXPR)))
 
-/* -------------------------- */
-u_int32_t get_id(const struct vol *vol, struct adouble *adp,  const struct stat *st,
-                 const cnid_t did, char *upath, const int len) 
+/*!
+ * @brief Get CNID for did/upath args both from database and adouble file
+ *
+ * 1. Get the objects CNID as stored in its adouble file
+ * 2. Get the objects CNID from the database
+ * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
+ * 4. In case 2 and 3 differ, store 3 in the adouble file
+ *
+ * @param vol    (rw) volume
+ * @param adp    (rw) adouble struct of object upath, might be NULL
+ * @param st     (r) stat of upath, must NOT be NULL
+ * @param did    (r) parent CNID of upath
+ * @param upath  (r) name of object
+ * @param len    (r) strlen of upath
+ */
+uint32_t get_id(struct vol *vol,
+                struct adouble *adp, 
+                const struct stat *st,
+                const cnid_t did,
+                const char *upath,
+                const int len) 
 {
+    static int first = 1;       /* mark if this func is called the first time */
     u_int32_t adcnid;
     u_int32_t dbcnid = CNID_INVALID;
 
+restart:
     if (vol->v_cdb != NULL) {
         /* prime aint with what we think is the cnid, set did to zero for
            catching moved files */
-        adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp);
+        adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
 
-           dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid);
+           dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
            /* Throw errors if cnid_add fails. */
            if (dbcnid == CNID_INVALID) {
             switch (errno) {
@@ -222,23 +242,57 @@ u_int32_t get_id(const struct vol *vol, struct adouble *adp,  const struct stat
             case CNID_ERR_PARAM:
                 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
                 afp_errno = AFPERR_PARAM;
-                return CNID_INVALID;
+                goto exit;
             case CNID_ERR_PATH:
                 afp_errno = AFPERR_PARAM;
-                return CNID_INVALID;
+                goto exit;
             default:
+                /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
+                /* we have to do it here for "dbd" because it uses "lazy opening" */
+                /* In order to not end in a loop somehow with goto restart below  */
+                /*  */
+                if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
+                    cnid_close(vol->v_cdb);
+                    free(vol->v_cnidscheme);
+                    vol->v_cnidscheme = strdup("tdb");
+
+                    int flags = CNID_FLAG_MEMORY;
+                    if ((vol->v_flags & AFPVOL_NODEV)) {
+                        flags |= CNID_FLAG_NODEV;
+                    }
+                    LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
+                        vol->v_path);
+                    vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
+                    if (vol->v_cdb) {
+                        vol->v_flags &= ~AFPVOL_CACHE;
+                        if (!(vol->v_flags & AFPVOL_TM)) {
+                            vol->v_flags |= AFPVOL_RO;
+                            setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
+                                       "Check server messages for details. Switching to read-only mode.");
+                            kill(getpid(), SIGUSR2);
+                        }
+                        goto restart; /* now try again with the temp CNID db */
+                    } else {
+                        setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
+                                   "Check server messages for details, can't recover from this state!");
+                    }
+                }
                 afp_errno = AFPERR_MISC;
-                return CNID_INVALID;
+                goto exit;
             }
         }
-        else if (adp && (adcnid != dbcnid)) {
+        else if (adp && (adcnid != dbcnid)) { /* 4 */
             /* Update the ressource fork. For a folder adp is always null */
-            LOG(log_debug, logtype_afpd, "get_id: calling ad_setid. adcnid: %u, dbcnid: %u", htonl(adcnid), htonl(dbcnid));
+            LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
+                getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
             if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
                 ad_flush(adp);
             }
         }
     }
+
+exit:
+    first = 0;
     return dbcnid;
 }
              
@@ -259,21 +313,61 @@ int getmetadata(struct vol *vol,
     struct stat         *st;
     struct maccess     ma;
 
+    LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
 
     upath = path->u_name;
     st = &path->st;
-
     data = buf;
 
     if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
          || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
          || (bitmap & (1 << FILPBIT_FNUM))) {
-        if (!path->id)
-            id = get_id(vol, adp, st, dir->d_did, upath, strlen(upath));
-        else 
+        if (!path->id) {
+            bstring fullpath;
+            struct dir *cachedfile;
+            int len = strlen(upath);
+            if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
+                id = cachedfile->d_did;
+            else {
+                id = get_id(vol, adp, st, dir->d_did, upath, len);
+
+                /* Add it to the cache */
+                LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
+                    ntohl(dir->d_did), upath, ntohl(id));
+
+                /* Get macname from unixname first */
+                if (path->m_name == NULL) {
+                    if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
+                        LOG(log_error, logtype_afpd, "getmetadata: utompath error");
+                        return AFPERR_MISC;
+                    }
+                }
+                
+                /* Build fullpath */
+                if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
+                    || (bconchar(fullpath, '/') != BSTR_OK)
+                    || (bcatcstr(fullpath, upath)) != BSTR_OK) {
+                    LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
+                    return AFPERR_MISC;
+                }
+
+                if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
+                    LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
+                    return AFPERR_MISC;
+                }
+
+                if ((dircache_add(vol, cachedfile)) != 0) {
+                    LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
+                    return AFPERR_MISC;
+                }
+            }
+        } else {
             id = path->id;
+        }
+
         if (id == CNID_INVALID)
             return afp_errno;
+
         if (!path->m_name) {
             path->m_name = utompath(vol, upath, id, utf8_encoding());
         }
@@ -296,9 +390,9 @@ int getmetadata(struct vol *vol,
             /* FIXME do we want a visual clue if the file is read only
              */
             struct maccess     ma;
-            accessmode( ".", &ma, dir , NULL);
+            accessmode(vol, ".", &ma, dir , NULL);
             if ((ma.ma_user & AR_UWRITE)) {
-               accessmode( upath, &ma, dir , st);
+               accessmode(vol, upath, &ma, dir , st);
                if (!(ma.ma_user & AR_UWRITE)) {
                        ashort |= htons(ATTRBIT_NOWRITE);
                 }
@@ -465,7 +559,7 @@ int getmetadata(struct vol *vol,
             break;
         case FILPBIT_UNIXPR :
             /* accessmode may change st_mode with ACLs */
-            accessmode( upath, &ma, dir , st);
+            accessmode(vol, upath, &ma, dir , st);
 
             aint = htonl(st->st_uid);
             memcpy( data, &aint, sizeof( aint ));
@@ -527,6 +621,8 @@ int getfilparams(struct vol *vol,
     int                 opened = 0;
     int rc;    
 
+    LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
+
     opened = PARAM_NEED_ADP(bitmap);
     adp = NULL;
 
@@ -605,7 +701,7 @@ int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_,
     upath = s_path->u_name;
     
     /* if upath is deleted we already in trouble anyway */
-    if ((of = of_findname(s_path))) {
+    if ((of = of_findname(vol, s_path))) {
         adp = of->of_ad;
     } else {
         ad_init(&ad, vol->v_adouble, vol->v_ad_options);
@@ -639,6 +735,7 @@ int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_,
             return( AFPERR_ACCESS );
         case EDQUOT:
         case ENOSPC :
+           LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
             return( AFPERR_DFULL );
         default :
             return( AFPERR_PARAM );
@@ -660,11 +757,25 @@ int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_,
 
     path = s_path->m_name;
     ad_setname(adp, path);
+
+    struct stat st;
+    if (lstat(upath, &st) != 0) {
+        LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
+            upath, strerror(errno));
+        ad_close( adp, ADFLAGS_DF|ADFLAGS_HF);
+        return AFPERR_MISC;
+    }
+
+    (void)get_id(vol, adp, &st, dir->d_did, upath, strlen(upath));
+
     ad_flush( adp);
+
+    fce_register_new_file(s_path);
+
     ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
 
 createfile_done:
-    curdir->offcnt++;
+    curdir->d_offcnt++;
 
 #ifdef DROPKLUDGE
     if (vol->v_flags & AFPVOL_DROPBOX) {
@@ -760,6 +871,9 @@ int setfilparams(struct vol *vol,
     u_int16_t           bitmap = f_bitmap;
     u_int32_t           cdate,bdate;
     u_char              finder_buf[32];
+    int fp;
+    ssize_t len;
+    char symbuf[MAXPATHLEN+1];
 
 #ifdef DEBUG
     LOG(log_debug9, logtype_afpd, "begin setfilparams:");
@@ -801,29 +915,32 @@ int setfilparams(struct vol *vol,
             break;
         case FILPBIT_FINFO :
             change_mdate = 1;
-            memcpy(finder_buf, buf, 32 );
-            if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
-            // SLFINFO
-                int fp;
-                ssize_t len;
-                int erc=1;
-                char buf[PATH_MAX+1];
-                if ((fp=open(path->u_name,O_RDONLY))>=0){
-                    if ((len=read(fp,buf,PATH_MAX+1))){
-                        if (unlink(path->u_name)==0){
-                            buf[len]=0;
-                            erc = symlink(buf, path->u_name);
-                            if (!erc)
-                                of_stat(path);
-                        }
-                    }
-                    close(fp);
+            if (memcmp(buf,"slnkrhap",8) == 0
+                && !(S_ISLNK(path->st.st_mode))
+                && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
+                /* request to turn this into a symlink */
+                if ((fp = open(path->u_name, O_RDONLY)) == -1) {
+                    err = AFPERR_MISC;
+                    goto setfilparam_done;
+                }
+                len = read(fp, symbuf, MAXPATHLEN);
+                close(fp);
+                if (!(len > 0)) {
+                    err = AFPERR_MISC;
+                    goto setfilparam_done;
                 }
-                if (erc!=0){
-                    err=AFPERR_BITMAP;
+                if (unlink(path->u_name) != 0) {
+                    err = AFPERR_MISC;
                     goto setfilparam_done;
                 }
+                symbuf[len] = 0;
+                if (symlink(symbuf, path->u_name) != 0) {
+                    err = AFPERR_MISC;
+                    goto setfilparam_done;
+                }
+                of_stat(vol, path);
             }
+            memcpy(finder_buf, buf, 32 );
             buf += 32;
             break;
         case FILPBIT_UNIXPR :
@@ -1011,6 +1128,9 @@ int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *n
 {
     int                rc;
 
+    LOG(log_debug, logtype_afpd,
+        "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
+
     if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
         switch ( errno ) {
         case ENOENT :
@@ -1258,7 +1378,7 @@ int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, si
         retvalue = err;
         goto copy_exit;
     }
-    curdir->offcnt++;
+    curdir->d_offcnt++;
 
 #ifdef DROPKLUDGE
     if (vol->v_flags & AFPVOL_DROPBOX) {
@@ -1438,7 +1558,8 @@ int copyfile(const struct vol *s_vol,
     if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
         /* copy the data fork */
         if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
-            err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
+            if (ad_meta_fileno(adp) != -1)
+                err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
         }
     }
 
@@ -1479,6 +1600,7 @@ done:
     case EDQUOT:
     case EFBIG:
     case ENOSPC:
+       LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
         return AFPERR_DFULL;
     case ENOENT:
         return AFPERR_NOOBJ;
@@ -1691,7 +1813,7 @@ static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
     cnid_t        did  = param->did;
     cnid_t       aint;
     
-    if ( lstat(de->d_name, &path.st)<0 )
+    if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
         return 0;
     
     /* update or add to cnid */
@@ -1736,13 +1858,13 @@ reenumerate_id(struct vol *vol, char *name, struct dir *dir)
     }
     
     /* FIXME use of_statdir ? */
-    if (lstat(name, &st)) {
+    if (ostat(name, &st, vol_syml_opt(vol))) {
        return -1;
     }
 
     if (dirreenumerate(dir, &st)) {
         /* we already did it once and the dir haven't been modified */
-       return dir->offcnt;
+       return dir->d_offcnt;
     }
     
     data.vol = vol;
@@ -1815,7 +1937,7 @@ retry:
 
     memset(&path, 0, sizeof(path));
     path.u_name = upath;
-    if ( of_stat(&path) < 0 ) {
+    if (of_stat(vol, &path) < 0 ) {
 #ifdef ESTALE
         /* with nfs and our working directory is deleted */
        if (errno == ESTALE) {
@@ -1910,7 +2032,7 @@ int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_
     }
 
     err = AFP_OK;
-    if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
+    if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
         switch (errno) {
         case EACCES:
         case EPERM:
@@ -1946,7 +2068,7 @@ delete:
 }
 
 /* ------------------------------ */
-static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
+static struct adouble *find_adouble(const struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
 {
     int             ret;
 
@@ -1973,7 +2095,7 @@ static struct adouble *find_adouble(struct path *path, struct ofork **of, struct
         return NULL;
     }
     
-    if ((*of = of_findname(path))) {
+    if ((*of = of_findname(vol, path))) {
         /* reuse struct adouble so it won't break locks */
         adp = (*of)->of_ad;
     }
@@ -2064,7 +2186,7 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
     }
     
     ad_init(&ads, vol->v_adouble, vol->v_ad_options);
-    if (!(adsp = find_adouble( path, &s_of, &ads))) {
+    if (!(adsp = find_adouble(vol, path, &s_of, &ads))) {
         return afp_errno;
     }
 
@@ -2097,7 +2219,7 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
     }
 
     ad_init(&add, vol->v_adouble, vol->v_ad_options);
-    if (!(addp = find_adouble( path, &d_of, &add))) {
+    if (!(addp = find_adouble(vol, path, &d_of, &add))) {
         err = afp_errno;
         goto err_exchangefile;
     }
@@ -2150,16 +2272,15 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
     /* id's need switching. src -> dest and dest -> src. 
      * we need to re-stat() if it was a cross device copy.
     */
-    if (sid) {
-       cnid_delete(vol->v_cdb, sid);
-    }
-    if (did) {
-       cnid_delete(vol->v_cdb, did);
-    }
-    if ((did && ( (crossdev && lstat( upath, &srcst) < 0) || 
+    if (sid)
+        cnid_delete(vol->v_cdb, sid);
+    if (did)
+        cnid_delete(vol->v_cdb, did);
+
+    if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) || 
                 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
        ||
-       (sid && ( (crossdev && lstat(p, &destst) < 0) ||
+        (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
                 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
     ) {
         switch (errno) {
@@ -2249,5 +2370,11 @@ err_exchangefile:
        ad_close(addp, ADFLAGS_HF);
     }
 
+    struct dir *cached;
+    if ((cached = dircache_search_by_did(vol, sid)) != NULL)
+        (void)dir_remove(vol, cached);
+    if ((cached = dircache_search_by_did(vol, did)) != NULL)
+        (void)dir_remove(vol, cached);
+
     return err;
 }