]> arthur.barton.de Git - netatalk.git/blobdiff - etc/afpd/file.c
always test file open modes.
[netatalk.git] / etc / afpd / file.c
index acfbd5a7780ea41f4e1bf1eaea78c80cca6a2f0b..26015e3d2d44f473c31387e00f2cb3384507c6a3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: file.c,v 1.92.2.2.2.1 2003-09-09 16:42:20 didg Exp $
+ * $Id: file.c,v 1.92.2.2.2.15 2004-02-29 22:18:24 didg Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -11,9 +11,6 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
 
 /* STDC check */
 #if STDC_HEADERS
@@ -24,6 +21,7 @@
 #define strrchr index
 #endif /* HAVE_STRCHR */
 char *strchr (), *strrchr ();
+
 #ifndef HAVE_MEMCPY
 #define memcpy(d,s,n) bcopy ((s), (d), (n))
 #define memmove(d,s,n) bcopy ((s), (d), (n))
@@ -31,21 +29,14 @@ char *strchr (), *strrchr ();
 #endif /* STDC_HEADERS */
 
 #include <utime.h>
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif /* HAVE_FCNTL_H */
 #include <dirent.h>
-#include <sys/mman.h>
 #include <errno.h>
 
 #include <atalk/logger.h>
-#include <sys/types.h>
-#include <sys/time.h>
 #include <sys/param.h>
-#include <sys/stat.h>
 
-#include <netatalk/endian.h>
 #include <atalk/adouble.h>
+
 #include <atalk/afp.h>
 #include <atalk/util.h>
 #include <atalk/cnid.h>
@@ -101,14 +92,14 @@ void *get_finderinfo(const char *mpath, struct adouble *adp, void *data)
                && (em = getextmap( mpath ))
     ) {
         memcpy(data, em->em_type, sizeof( em->em_type ));
-        memcpy(data + 4, em->em_creator, sizeof(em->em_creator));
+        memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
     }
     return data;
 }
 
 /* ---------------------
 */
-char *set_name(const struct vol *vol, char *data, char *name, cnid_t id, u_int32_t utf8) 
+char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8) 
 {
     u_int32_t   aint;
     char        *tp = NULL;
@@ -123,7 +114,7 @@ char *set_name(const struct vol *vol, char *data, char *name, cnid_t id, u_int32
            
             /* global static variable... */
             tp = strdup(name);
-            if (!(u = mtoupath(vol, name, 1)) || !(m = utompath(vol, u, id, 0))) {
+            if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
                aint = 0;
             }
             else {
@@ -142,7 +133,7 @@ char *set_name(const struct vol *vol, char *data, char *name, cnid_t id, u_int32
         if (aint > 255)  /* FIXME safeguard, anyway if no ascii char it's game over*/
            aint = 255;
 
-        utf8 = htonl(vol->v_mac->kTextEncoding);         /* htonl(utf8) */
+        utf8 = vol->v_mac?htonl(vol->v_mac->kTextEncoding):0;         /* htonl(utf8) */
         memcpy(data, &utf8, sizeof(utf8));
         data += sizeof(utf8);
         
@@ -181,6 +172,7 @@ u_int32_t aint = 0;
 #if AD_VERSION > AD_VERSION1
 dev_t  dev;
 ino_t  ino;
+cnid_t a_did;
 char   stamp[ADEDLEN_PRIVSYN];
     /* look in AD v2 header 
      * note inode and device are opaques and not in network order
@@ -191,10 +183,17 @@ char   stamp[ADEDLEN_PRIVSYN];
             memcpy(&ino, ad_entry(adp, ADEID_PRIVINO), sizeof(ino_t));
             if (sizeof(stamp) == ad_getentrylen(adp,ADEID_PRIVSYN)) {
                 memcpy(stamp, ad_entry(adp, ADEID_PRIVSYN), sizeof(stamp));
-
-                if (dev == st->st_dev && ino == st->st_ino && !memcmp(vol->v_stamp, stamp, sizeof(stamp))) {
-                    memcpy(&aint, ad_entry(adp, ADEID_DID), sizeof(aint));
-                    return aint;
+                if (sizeof(cnid_t) == ad_getentrylen(adp, ADEID_DID)) {
+                    memcpy(&a_did, ad_entry(adp, ADEID_DID), sizeof(cnid_t));
+
+                    if (   ((vol->v_flags & AFPVOL_NODEV) || dev == st->st_dev)
+                           && ino == st->st_ino && a_did == did &&
+                           !memcmp(vol->v_stamp, stamp, sizeof(stamp)) &&
+                          (sizeof(cnid_t) == ad_getentrylen(adp, ADEID_PRIVID)) ) 
+                    { 
+                        memcpy(&aint, ad_entry(adp, ADEID_PRIVID), sizeof(aint));
+                        return aint;
+                    } 
                 }
             }
         }
@@ -202,34 +201,33 @@ char   stamp[ADEDLEN_PRIVSYN];
 #endif
     if (vol->v_cdb != NULL) {
            aint = cnid_add(vol->v_cdb, st, did, upath, len, aint);
-    /* Throw errors if cnid_add fails. */
-    if (aint == CNID_INVALID) {
-        switch (errno) {
-        case CNID_ERR_CLOSE: /* the db is closed */
-            break;
-        case CNID_ERR_PARAM:
-            LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
-            afp_errno = AFPERR_PARAM;
-            return CNID_INVALID;
-        case CNID_ERR_PATH:
-            afp_errno = AFPERR_PARAM;
-            return CNID_INVALID;
-        default:
-            afp_errno = AFPERR_MISC;
-            return CNID_INVALID;
+           /* Throw errors if cnid_add fails. */
+           if (aint == CNID_INVALID) {
+            switch (errno) {
+            case CNID_ERR_CLOSE: /* the db is closed */
+                break;
+            case CNID_ERR_PARAM:
+                LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
+                afp_errno = AFPERR_PARAM;
+                return CNID_INVALID;
+            case CNID_ERR_PATH:
+                afp_errno = AFPERR_PARAM;
+                return CNID_INVALID;
+            default:
+                afp_errno = AFPERR_MISC;
+                return CNID_INVALID;
+            }
         }
-    }
 #if AD_VERSION > AD_VERSION1
-            else if (adp && sizeof(dev_t) == ADEDLEN_PRIVDEV && sizeof(ino_t) == ADEDLEN_PRIVINO) {
-                /* update the ressource fork
-                 * for a folder adp is always null
-         */
-                 ad_setid(adp, st, aint, vol->v_stamp);
-                 ad_flush(adp, ADFLAGS_HF);
+        else if (adp && sizeof(dev_t) == ADEDLEN_PRIVDEV && sizeof(ino_t) == ADEDLEN_PRIVINO) {
+            /* update the ressource fork
+             * for a folder adp is always null
+             */
+            ad_setid(adp,(vol->v_flags & AFPVOL_NODEV)?0:st->st_dev, st->st_ino, aint, did, vol->v_stamp);
+            ad_flush(adp, ADFLAGS_HF);
         }
 #endif    
-        }
-
+    }
     return aint;
 }
              
@@ -486,12 +484,12 @@ int getmetadata(struct vol *vol,
     if ( l_nameoff ) {
         ashort = htons( data - buf );
         memcpy(l_nameoff, &ashort, sizeof( ashort ));
-        data = set_name(vol, data, path->m_name, id, 0);
+        data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
     }
     if ( utf_nameoff ) {
         ashort = htons( data - buf );
         memcpy(utf_nameoff, &ashort, sizeof( ashort ));
-        data = set_name(vol, data, path->m_name, id, utf8);
+        data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
     }
     *buflen = data - buf;
     return (AFP_OK);
@@ -539,8 +537,12 @@ int getfilparams(struct vol *vol,
            */
            if ((bitmap & (1 << FILPBIT_ATTR))) {
                 if (!(attrbits & ATTRBIT_ROPEN)) {
+                    attribs | = ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_OPEN_RD) ? ATTRBIT_ROPEN : 0;
+                    attribs | = ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_OPEN_WR) ? ATTRBIT_ROPEN : 0;
                 }
                 if (!(attrbits & ATTRBIT_DOPEN)) {
+                    attribs | = ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_OPEN_RD) ? ATTRBIT_DOPEN : 0;
+                    attribs | = ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_OPEN_WR) ? ATTRBIT_DOPEN : 0;
                 }
            }
        }
@@ -863,6 +865,19 @@ int setfilparams(struct vol *vol,
             buf += 32;
             break;
 
+        case FILPBIT_UNIXPR :
+           /* Skip the UIG/GID, no way to set them from OSX clients */
+            buf += sizeof( aint );
+            buf += sizeof( aint );
+
+            change_mdate = 1;
+            change_parent_mdate = 1;
+            memcpy( &aint, buf, sizeof( aint ));
+            buf += sizeof( aint );
+            aint = ntohl (aint);
+
+            setfilemode(path, aint);
+            break;
             /* Client needs to set the ProDOS file info for this file.
                Use a defined string for TEXT to support crlf
                translations and convert all else into pXYY per Inside
@@ -888,19 +903,6 @@ int setfilparams(struct vol *vol,
                 break;
             }
             /* fallthrough */
-        case FILPBIT_UNIXPR :
-           /* Skip the UIG/GID, no way to set them from OSX clients */
-            buf += sizeof( aint );
-            buf += sizeof( aint );
-
-            change_mdate = 1;
-            change_parent_mdate = 1;
-            memcpy( &aint, buf, sizeof( aint ));
-            buf += sizeof( aint );
-            aint = ntohl (aint);
-
-            setfilemode(path, aint);
-            break;
         default :
             err = AFPERR_BITMAP;
             goto setfilparam_done;
@@ -1196,8 +1198,10 @@ int              ibuflen, *rbuflen;
     if (copy_path_name(newname, ibuf) < 0) {
         return( AFPERR_PARAM );
     }
-
-    if (NULL == (upath = mtoupath(vol, newname, utf8_encoding()))) {
+    /* newname is always only a filename so curdir *is* its
+     * parent folder
+    */
+    if (NULL == (upath = mtoupath(vol, newname, curdir->d_did, utf8_encoding()))) {
         return( AFPERR_PARAM );
     }
     if ( (err = copyfile(p, upath , newname, vol_noadouble(vol))) < 0 ) {
@@ -1261,30 +1265,40 @@ static int copy_fd(int dfd, int sfd)
     ssize_t cc;
     int     err = AFP_OK;
     char    filebuf[8192];
-
+    
 #ifdef SENDFILE_FLAVOR_LINUX
+    off_t   offset = 0;
+    size_t  size;
     struct stat         st;
+    #define BUF 128*1024*1024
 
     if (fstat(sfd, &st) == 0) {
-        if ((cc = sendfile(dfd, sfd, NULL, st.st_size)) < 0) {
-            switch (errno) {
-            case EINVAL:  /* there's no guarantee that all fs support sendfile */
-                break;
-            case EDQUOT:
-            case EFBIG:
-            case ENOSPC:
-                return AFPERR_DFULL;
-            case EROFS:
-                return AFPERR_VLOCK;
-            default:
-                return AFPERR_PARAM;
+        
+        while (1) {
+            if ( offset >= st.st_size) {
+               return AFP_OK;
+            }
+            size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
+            if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
+                switch (errno) {
+                case ENOSYS:
+                case EINVAL:  /* there's no guarantee that all fs support sendfile */
+                    goto no_sendfile;
+                case EDQUOT:
+                case EFBIG:
+                case ENOSPC:
+                    return AFPERR_DFULL;
+                case EROFS:
+                    return AFPERR_VLOCK;
+                default:
+                    return AFPERR_PARAM;
+                }
             }
-        }
-        else {
-           return AFP_OK;
         }
     }
-#endif /* SENDFILE_FLAVOR_LINUX */
+    no_sendfile:
+    lseek(sfd, offset, SEEK_SET);
+#endif 
 
     while (1) {
         if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
@@ -1311,6 +1325,7 @@ const int   noadouble;
     struct adouble     ads, add;
     int                        len, err = AFP_OK;
     int                 adflags;
+    struct stat         st;
     
 #ifdef DEBUG
     LOG(log_info, logtype_afpd, "begin copyfile:");
@@ -1356,6 +1371,29 @@ const int   noadouble;
        err = copy_fd(ad_dfileno(&add), ad_dfileno(&ads));
     }
 
+    /* Now, reopen destination file */
+    err = AFP_OK;
+    if (ad_close( &add, adflags ) <0) {
+       deletefile(NULL, dst, 0);
+        return AFPERR_PARAM;  /* FIXME */
+    } else {
+       ad_init(&add, 0);
+       if (ad_open(dst , adflags | noadouble, O_RDWR, 0666, &add) < 0) {
+           ad_close( &ads, adflags );
+           deletefile(NULL, dst, 0);
+           switch ( err ) {
+           case ENOENT :
+               return( AFPERR_NOOBJ );
+           case EACCES :
+               return( AFPERR_ACCESS );
+           case EROFS:
+               return AFPERR_VLOCK;
+           default :
+               return( AFPERR_PARAM );
+           }
+       }
+    }
+
     if (newname) {
         len = strlen( newname );
         ad_setentrylen( &add, ADEID_NAME, len );
@@ -1379,6 +1417,14 @@ const int   noadouble;
         }
     }
 
+    /* set dest modification date to src date */
+    if (!stat(src, &st)) {
+        struct utimbuf ut;
+
+       ut.actime = ut.modtime = st.st_mtime;
+       utime(dst, &ut);
+    }
+
 #ifdef DEBUG
     LOG(log_info, logtype_afpd, "end copyfile:");
 #endif /* DEBUG */
@@ -1567,6 +1613,55 @@ int              ibuflen, *rbuflen;
     return afp_errno;
 }
 
+static int
+reenumerate_id(const struct vol *vol, char *name, cnid_t did)
+{
+    DIR             *dp;
+    struct dirent   *de;
+    int             ret;
+    struct stat     st;
+    cnid_t         aint;
+    struct adouble  ad;
+       
+
+    if (vol->v_cdb == NULL) {
+       return -1;
+    }
+    if (NULL == ( dp = opendir( name)) ) {
+        return -1;
+    }
+    ret = 0;
+    for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
+        if (NULL == check_dirent(vol, de->d_name))
+            continue;
+
+        if ( stat(de->d_name, &st)<0 )
+            continue;
+       
+       /* update or add to cnid */
+        aint = cnid_add(vol->v_cdb, &st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
+
+#if AD_VERSION > AD_VERSION1
+        if (aint != CNID_INVALID && !S_ISDIR(st.st_mode)) {
+            ad_init(&ad, 0);  /* OK */
+            if ( ad_open( de->d_name, ADFLAGS_HF, O_RDWR, 0, &ad ) < 0 ) {
+                continue;
+            }
+            else {
+                ad_setid(&ad,(vol->v_flags & AFPVOL_NODEV)?0:st.st_dev, st.st_ino, aint, did, vol->v_stamp);
+                ad_flush(&ad, ADFLAGS_HF);
+                ad_close(&ad, ADFLAGS_HF);
+           }
+        }
+#endif /* AD_VERSION > AD_VERSION1 */
+
+        ret++;
+    }
+    closedir(dp);
+    return ret;
+}
+
+    
 /* ------------------------------
    resolve a file id */
 int afp_resolveid(obj, ibuf, ibuflen, rbuf, rbuflen )
@@ -1578,7 +1673,7 @@ int               ibuflen, *rbuflen;
     struct dir         *dir;
     char               *upath;
     struct path         path;
-    int                 err, buflen;
+    int                 err, buflen, retry=0;
     cnid_t             id, cnid;
     u_int16_t          vid, bitmap;
 
@@ -1607,6 +1702,7 @@ int               ibuflen, *rbuflen;
     ibuf += sizeof(id);
     cnid = id;
 
+retry:
     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
     }
@@ -1615,7 +1711,7 @@ int               ibuflen, *rbuflen;
         return AFPERR_NOID; /* idem AFPERR_PARAM */
     }
     path.u_name = upath;
-    if (movecwd(vol, dir) < 0 || of_stat(&path) < 0) {
+    if (movecwd(vol, dir) < 0) {
         switch (errno) {
         case EACCES:
         case EPERM:
@@ -1626,6 +1722,26 @@ int              ibuflen, *rbuflen;
             return AFPERR_PARAM;
         }
     }
+
+    if ( of_stat(&path) < 0 ) {
+       if ( errno == ENOENT && !retry) {
+           /* cnid db is out of sync, reenumerate the directory and updated ids */
+           reenumerate_id(vol, ".", id);
+           id = cnid;
+           retry = 1;
+           goto retry;
+        }
+        switch (errno) {
+        case EACCES:
+        case EPERM:
+            return AFPERR_ACCESS;
+        case ENOENT:
+            return AFPERR_NOID;
+        default:
+            return AFPERR_PARAM;
+        }
+    }
+
     /* directories are bad */
     if (S_ISDIR(path.st.st_mode))
         return AFPERR_BADTYPE;