]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/file.c
Configurable symlink behaviour
[netatalk.git] / etc / afpd / file.c
1 /*
2  * Copyright (c) 1990,1993 Regents of The University of Michigan.
3  * All Rights Reserved.  See COPYRIGHT.
4  */
5
6 #ifdef HAVE_CONFIG_H
7 #include "config.h"
8 #endif /* HAVE_CONFIG_H */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12
13 /* STDC check */
14 #if STDC_HEADERS
15 #include <string.h>
16 #else /* STDC_HEADERS */
17 #ifndef HAVE_STRCHR
18 #define strchr index
19 #define strrchr index
20 #endif /* HAVE_STRCHR */
21 char *strchr (), *strrchr ();
22
23 #ifndef HAVE_MEMCPY
24 #define memcpy(d,s,n) bcopy ((s), (d), (n))
25 #define memmove(d,s,n) bcopy ((s), (d), (n))
26 #endif /* ! HAVE_MEMCPY */
27 #endif /* STDC_HEADERS */
28
29 #include <utime.h>
30 #include <errno.h>
31 #include <sys/param.h>
32
33 #include <atalk/adouble.h>
34 #include <atalk/vfs.h>
35 #include <atalk/logger.h>
36 #include <atalk/afp.h>
37 #include <atalk/util.h>
38 #include <atalk/cnid.h>
39 #include <atalk/unix.h>
40 #include <atalk/globals.h>
41 #include <atalk/fce_api.h>
42
43 #include "directory.h"
44 #include "dircache.h"
45 #include "desktop.h"
46 #include "volume.h"
47 #include "fork.h"
48 #include "file.h"
49 #include "filedir.h"
50 #include "unix.h"
51
52 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
53  * field         bytes        subfield    bytes
54  * 
55  * files:
56  * ioFlFndrInfo  16      ->       type    4  type field
57  *                             creator    4  creator field
58  *                               flags    2  finder flags:
59  *                                           alias, bundle, etc.
60  *                            location    4  location in window
61  *                              folder    2  window that contains file
62  * 
63  * ioFlXFndrInfo 16      ->     iconID    2  icon id
64  *                              unused    6  reserved 
65  *                              script    1  script system
66  *                              xflags    1  reserved
67  *                           commentID    2  comment id
68  *                           putawayID    4  home directory id
69  */
70
71 const u_char ufinderi[ADEDLEN_FINDERI] = {
72                               0, 0, 0, 0, 0, 0, 0, 0,
73                               1, 0, 0, 0, 0, 0, 0, 0,
74                               0, 0, 0, 0, 0, 0, 0, 0,
75                               0, 0, 0, 0, 0, 0, 0, 0
76                           };
77
78 static const u_char old_ufinderi[] = {
79                               'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
80                           };
81
82 /* ---------------------- 
83 */
84 static int default_type(void *finder) 
85 {
86     if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
87         return 1;
88     return 0;
89 }
90
91 /* FIXME path : unix or mac name ? (for now it's unix name ) */
92 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
93 {
94     struct extmap       *em;
95     void                *ad_finder = NULL;
96     int                 chk_ext = 0;
97     
98     if (adp)
99         ad_finder = ad_entry(adp, ADEID_FINDERI);
100
101     if (ad_finder) {
102         memcpy(data, ad_finder, ADEDLEN_FINDERI);
103         /* default type ? */
104         if (default_type(ad_finder)) 
105             chk_ext = 1;
106     }
107     else {
108         memcpy(data, ufinderi, ADEDLEN_FINDERI);
109         chk_ext = 1;
110         if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
111             u_int16_t ashort;
112             
113             ashort = htons(FINDERINFO_INVISIBLE);
114             memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
115         }
116     }
117
118     if (islink && !vol_syml_opt(vol)) {
119         u_int16_t linkflag;
120         memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
121         linkflag |= htons(FINDERINFO_ISALIAS);
122         memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
123         memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4); 
124         memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4); 
125         chk_ext = 0;
126     }
127
128     /** Only enter if no appledouble information and no finder information found. */
129     if (chk_ext && (em = getextmap( upath ))) {
130         memcpy(data, em->em_type, sizeof( em->em_type ));
131         memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
132     }
133     return data;
134 }
135
136 /* ---------------------
137 */
138 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8) 
139 {
140     u_int32_t   aint;
141     char        *tp = NULL;
142     char        *src = name;
143     aint = strlen( name );
144
145     if (!utf8) {
146         /* want mac name */
147         if (utf8_encoding()) {
148             /* but name is an utf8 mac name */
149             char *u, *m;
150            
151             /* global static variable... */
152             tp = strdup(name);
153             if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
154                aint = 0;
155             }
156             else {
157                 aint = strlen(m);
158                 src = m;
159             }
160             
161         }
162         if (aint > MACFILELEN)
163             aint = MACFILELEN;
164         *data++ = aint;
165     }
166     else {
167         u_int16_t temp;
168
169         if (aint > UTF8FILELEN_EARLY)  /* FIXME safeguard, anyway if no ascii char it's game over*/
170            aint = UTF8FILELEN_EARLY;
171
172         utf8 = vol->v_kTextEncoding;
173         memcpy(data, &utf8, sizeof(utf8));
174         data += sizeof(utf8);
175         
176         temp = htons(aint);
177         memcpy(data, &temp, sizeof(temp));
178         data += sizeof(temp);
179     }
180
181     memcpy( data, src, aint );
182     data += aint;
183     if (tp) {
184         strcpy(name, tp);
185         free(tp);
186     }
187     return data;
188 }
189
190 /*
191  * FIXME: PDINFO is UTF8 and doesn't need adp
192 */
193 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR)  |\
194                                   (1 << FILPBIT_CDATE) |\
195                                   (1 << FILPBIT_MDATE) |\
196                                   (1 << FILPBIT_BDATE) |\
197                                   (1 << FILPBIT_FINFO) |\
198                                   (1 << FILPBIT_RFLEN) |\
199                                   (1 << FILPBIT_EXTRFLEN) |\
200                                   (1 << FILPBIT_PDINFO) |\
201                                   (1 << FILPBIT_FNUM) |\
202                                   (1 << FILPBIT_UNIXPR)))
203
204 /*!
205  * @brief Get CNID for did/upath args both from database and adouble file
206  *
207  * 1. Get the objects CNID as stored in its adouble file
208  * 2. Get the objects CNID from the database
209  * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
210  * 4. In case 2 and 3 differ, store 3 in the adouble file
211  *
212  * @param vol    (rw) volume
213  * @param adp    (rw) adouble struct of object upath, might be NULL
214  * @param st     (r) stat of upath, must NOT be NULL
215  * @param did    (r) parent CNID of upath
216  * @param upath  (r) name of object
217  * @param len    (r) strlen of upath
218  */
219 uint32_t get_id(struct vol *vol,
220                 struct adouble *adp, 
221                 const struct stat *st,
222                 const cnid_t did,
223                 const char *upath,
224                 const int len) 
225 {
226     static int first = 1;       /* mark if this func is called the first time */
227     u_int32_t adcnid;
228     u_int32_t dbcnid = CNID_INVALID;
229
230 restart:
231     if (vol->v_cdb != NULL) {
232         /* prime aint with what we think is the cnid, set did to zero for
233            catching moved files */
234         adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
235
236             dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
237             /* Throw errors if cnid_add fails. */
238             if (dbcnid == CNID_INVALID) {
239             switch (errno) {
240             case CNID_ERR_CLOSE: /* the db is closed */
241                 break;
242             case CNID_ERR_PARAM:
243                 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
244                 afp_errno = AFPERR_PARAM;
245                 goto exit;
246             case CNID_ERR_PATH:
247                 afp_errno = AFPERR_PARAM;
248                 goto exit;
249             default:
250                 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
251                 /* we have to do it here for "dbd" because it uses "lazy opening" */
252                 /* In order to not end in a loop somehow with goto restart below  */
253                 /*  */
254                 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
255                     cnid_close(vol->v_cdb);
256                     free(vol->v_cnidscheme);
257                     vol->v_cnidscheme = strdup("tdb");
258
259                     int flags = CNID_FLAG_MEMORY;
260                     if ((vol->v_flags & AFPVOL_NODEV)) {
261                         flags |= CNID_FLAG_NODEV;
262                     }
263                     LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
264                         vol->v_path);
265                     vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
266                     if (vol->v_cdb) {
267                         vol->v_flags &= ~AFPVOL_CACHE;
268                         if (!(vol->v_flags & AFPVOL_TM)) {
269                             vol->v_flags |= AFPVOL_RO;
270                             setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
271                                        "Check server messages for details. Switching to read-only mode.");
272                             kill(getpid(), SIGUSR2);
273                         }
274                         goto restart; /* now try again with the temp CNID db */
275                     } else {
276                         setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
277                                    "Check server messages for details, can't recover from this state!");
278                     }
279                 }
280                 afp_errno = AFPERR_MISC;
281                 goto exit;
282             }
283         }
284         else if (adp && (adcnid != dbcnid)) { /* 4 */
285             /* Update the ressource fork. For a folder adp is always null */
286             LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
287                 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
288             if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
289                 ad_flush(adp);
290             }
291         }
292     }
293
294 exit:
295     first = 0;
296     return dbcnid;
297 }
298              
299 /* -------------------------- */
300 int getmetadata(struct vol *vol,
301                  u_int16_t bitmap,
302                  struct path *path, struct dir *dir, 
303                  char *buf, size_t *buflen, struct adouble *adp)
304 {
305     char                *data, *l_nameoff = NULL, *upath;
306     char                *utf_nameoff = NULL;
307     int                 bit = 0;
308     u_int32_t           aint;
309     cnid_t              id = 0;
310     u_int16_t           ashort;
311     u_char              achar, fdType[4];
312     u_int32_t           utf8 = 0;
313     struct stat         *st;
314     struct maccess      ma;
315
316     LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
317
318     upath = path->u_name;
319     st = &path->st;
320     data = buf;
321
322     if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
323          || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
324          || (bitmap & (1 << FILPBIT_FNUM))) {
325         if (!path->id) {
326             bstring fullpath;
327             struct dir *cachedfile;
328             int len = strlen(upath);
329             if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
330                 id = cachedfile->d_did;
331             else {
332                 id = get_id(vol, adp, st, dir->d_did, upath, len);
333
334                 /* Add it to the cache */
335                 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
336                     ntohl(dir->d_did), upath, ntohl(id));
337
338                 /* Get macname from unixname first */
339                 if (path->m_name == NULL) {
340                     if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
341                         LOG(log_error, logtype_afpd, "getmetadata: utompath error");
342                         return AFPERR_MISC;
343                     }
344                 }
345                 
346                 /* Build fullpath */
347                 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
348                     || (bconchar(fullpath, '/') != BSTR_OK)
349                     || (bcatcstr(fullpath, upath)) != BSTR_OK) {
350                     LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
351                     return AFPERR_MISC;
352                 }
353
354                 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
355                     LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
356                     return AFPERR_MISC;
357                 }
358
359                 if ((dircache_add(vol, cachedfile)) != 0) {
360                     LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
361                     return AFPERR_MISC;
362                 }
363             }
364         } else {
365             id = path->id;
366         }
367
368         if (id == CNID_INVALID)
369             return afp_errno;
370
371         if (!path->m_name) {
372             path->m_name = utompath(vol, upath, id, utf8_encoding());
373         }
374     }
375     while ( bitmap != 0 ) {
376         while (( bitmap & 1 ) == 0 ) {
377             bitmap = bitmap>>1;
378             bit++;
379         }
380
381         switch ( bit ) {
382         case FILPBIT_ATTR :
383             if ( adp ) {
384                 ad_getattr(adp, &ashort);
385             } else if (vol_inv_dots(vol) && *upath == '.') {
386                 ashort = htons(ATTRBIT_INVISIBLE);
387             } else
388                 ashort = 0;
389 #if 0
390             /* FIXME do we want a visual clue if the file is read only
391              */
392             struct maccess      ma;
393             accessmode(vol, ".", &ma, dir , NULL);
394             if ((ma.ma_user & AR_UWRITE)) {
395                 accessmode(vol, upath, &ma, dir , st);
396                 if (!(ma.ma_user & AR_UWRITE)) {
397                         ashort |= htons(ATTRBIT_NOWRITE);
398                 }
399             }
400 #endif
401             memcpy(data, &ashort, sizeof( ashort ));
402             data += sizeof( ashort );
403             LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
404                 path->u_name, ntohs(ashort));
405             break;
406
407         case FILPBIT_PDID :
408             memcpy(data, &dir->d_did, sizeof( u_int32_t ));
409             data += sizeof( u_int32_t );
410             LOG(log_debug, logtype_afpd, "metadata('%s'):     Parent DID: %u",
411                 path->u_name, ntohl(dir->d_did));
412             break;
413
414         case FILPBIT_CDATE :
415             if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
416                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
417             memcpy(data, &aint, sizeof( aint ));
418             data += sizeof( aint );
419             break;
420
421         case FILPBIT_MDATE :
422             if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
423                 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
424                    aint = AD_DATE_FROM_UNIX(st->st_mtime);
425                 }
426             } else {
427                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
428             }
429             memcpy(data, &aint, sizeof( int ));
430             data += sizeof( int );
431             break;
432
433         case FILPBIT_BDATE :
434             if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
435                 aint = AD_DATE_START;
436             memcpy(data, &aint, sizeof( int ));
437             data += sizeof( int );
438             break;
439
440         case FILPBIT_FINFO :
441                 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
442             data += ADEDLEN_FINDERI;
443             break;
444
445         case FILPBIT_LNAME :
446             l_nameoff = data;
447             data += sizeof( u_int16_t );
448             break;
449
450         case FILPBIT_SNAME :
451             memset(data, 0, sizeof(u_int16_t));
452             data += sizeof( u_int16_t );
453             break;
454
455         case FILPBIT_FNUM :
456             memcpy(data, &id, sizeof( id ));
457             data += sizeof( id );
458             LOG(log_debug, logtype_afpd, "metadata('%s'):           CNID: %u",
459                 path->u_name, ntohl(id));
460             break;
461
462         case FILPBIT_DFLEN :
463             if  (st->st_size > 0xffffffff)
464                aint = 0xffffffff;
465             else
466                aint = htonl( st->st_size );
467             memcpy(data, &aint, sizeof( aint ));
468             data += sizeof( aint );
469             break;
470
471         case FILPBIT_RFLEN :
472             if ( adp ) {
473                 if (adp->ad_rlen > 0xffffffff)
474                     aint = 0xffffffff;
475                 else
476                     aint = htonl( adp->ad_rlen);
477             } else {
478                 aint = 0;
479             }
480             memcpy(data, &aint, sizeof( aint ));
481             data += sizeof( aint );
482             break;
483
484             /* Current client needs ProDOS info block for this file.
485                Use simple heuristic and let the Mac "type" string tell
486                us what the PD file code should be.  Everything gets a
487                subtype of 0x0000 unless the original value was hashed
488                to "pXYZ" when we created it.  See IA, Ver 2.
489                <shirsch@adelphia.net> */
490         case FILPBIT_PDINFO :
491             if (afp_version >= 30) { /* UTF8 name */
492                 utf8 = kTextEncodingUTF8;
493                 utf_nameoff = data;
494                 data += sizeof( u_int16_t );
495                 aint = 0;
496                 memcpy(data, &aint, sizeof( aint ));
497                 data += sizeof( aint );
498             }
499             else {
500                 if ( adp ) {
501                     memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
502
503                     if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
504                         achar = '\x04';
505                         ashort = 0x0000;
506                     }
507                     else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
508                         achar = '\xff';
509                         ashort = 0x0000;
510                     }
511                     else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
512                         achar = '\xb3';
513                         ashort = 0x0000;
514                     }
515                     else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
516                         achar = '\x00';
517                         ashort = 0x0000;
518                     }
519                     else if ( fdType[0] == 'p' ) {
520                         achar = fdType[1];
521                         ashort = (fdType[2] * 256) + fdType[3];
522                     }
523                     else {
524                         achar = '\x00';
525                         ashort = 0x0000;
526                     }
527                 }
528                 else {
529                     achar = '\x00';
530                     ashort = 0x0000;
531                 }
532
533                 *data++ = achar;
534                 *data++ = 0;
535                 memcpy(data, &ashort, sizeof( ashort ));
536                 data += sizeof( ashort );
537                 memset(data, 0, sizeof( ashort ));
538                 data += sizeof( ashort );
539             }
540             break;
541         case FILPBIT_EXTDFLEN:
542             aint = htonl(st->st_size >> 32);
543             memcpy(data, &aint, sizeof( aint ));
544             data += sizeof( aint );
545             aint = htonl(st->st_size);
546             memcpy(data, &aint, sizeof( aint ));
547             data += sizeof( aint );
548             break;
549         case FILPBIT_EXTRFLEN:
550             aint = 0;
551             if (adp) 
552                 aint = htonl(adp->ad_rlen >> 32);
553             memcpy(data, &aint, sizeof( aint ));
554             data += sizeof( aint );
555             if (adp) 
556                 aint = htonl(adp->ad_rlen);
557             memcpy(data, &aint, sizeof( aint ));
558             data += sizeof( aint );
559             break;
560         case FILPBIT_UNIXPR :
561             /* accessmode may change st_mode with ACLs */
562             accessmode(vol, upath, &ma, dir , st);
563
564             aint = htonl(st->st_uid);
565             memcpy( data, &aint, sizeof( aint ));
566             data += sizeof( aint );
567             aint = htonl(st->st_gid);
568             memcpy( data, &aint, sizeof( aint ));
569             data += sizeof( aint );
570
571             /* FIXME: ugly hack
572                type == slnk indicates an OSX style symlink, 
573                we have to add S_IFLNK to the mode, otherwise
574                10.3 clients freak out. */
575
576             aint = st->st_mode;
577             if (adp) {
578                 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
579                 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
580                     aint |= S_IFLNK;
581                 }
582             }
583             aint = htonl(aint);
584
585             memcpy( data, &aint, sizeof( aint ));
586             data += sizeof( aint );
587
588             *data++ = ma.ma_user;
589             *data++ = ma.ma_world;
590             *data++ = ma.ma_group;
591             *data++ = ma.ma_owner;
592             break;
593             
594         default :
595             return( AFPERR_BITMAP );
596         }
597         bitmap = bitmap>>1;
598         bit++;
599     }
600     if ( l_nameoff ) {
601         ashort = htons( data - buf );
602         memcpy(l_nameoff, &ashort, sizeof( ashort ));
603         data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
604     }
605     if ( utf_nameoff ) {
606         ashort = htons( data - buf );
607         memcpy(utf_nameoff, &ashort, sizeof( ashort ));
608         data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
609     }
610     *buflen = data - buf;
611     return (AFP_OK);
612 }
613                 
614 /* ----------------------- */
615 int getfilparams(struct vol *vol,
616                  u_int16_t bitmap,
617                  struct path *path, struct dir *dir, 
618                  char *buf, size_t *buflen )
619 {
620     struct adouble      ad, *adp;
621     int                 opened = 0;
622     int rc;    
623
624     LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
625
626     opened = PARAM_NEED_ADP(bitmap);
627     adp = NULL;
628
629     if (opened) {
630         char *upath;
631         int  flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
632
633         adp = of_ad(vol, path, &ad);
634         upath = path->u_name;
635
636         if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) {
637             switch (errno) {
638             case EACCES:
639                 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
640                 upath, strerror(errno));
641                 return AFPERR_ACCESS;
642             case EIO:
643                 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
644                 /* fall through */
645             case ENOENT:
646             default:
647                 adp = NULL;
648                 break;
649             }
650         }
651     }
652     rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
653     if ( adp ) {
654         ad_close_metadata( adp);
655     }
656
657     return( rc );
658 }
659
660 /* ----------------------------- */
661 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
662 {
663     struct adouble      ad, *adp;
664     struct vol          *vol;
665     struct dir          *dir;
666     struct ofork        *of = NULL;
667     char                *path, *upath;
668     int                 creatf, did, openf, retvalue = AFP_OK;
669     u_int16_t           vid;
670     struct path         *s_path;
671     
672     *rbuflen = 0;
673     ibuf++;
674     creatf = (unsigned char) *ibuf++;
675
676     memcpy(&vid, ibuf, sizeof( vid ));
677     ibuf += sizeof( vid );
678
679     if (NULL == ( vol = getvolbyvid( vid )) ) {
680         return( AFPERR_PARAM );
681     }
682
683     if (vol->v_flags & AFPVOL_RO)
684         return AFPERR_VLOCK;
685
686     memcpy(&did, ibuf, sizeof( did));
687     ibuf += sizeof( did );
688
689     if (NULL == ( dir = dirlookup( vol, did )) ) {
690         return afp_errno;
691     }
692
693     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
694         return get_afp_errno(AFPERR_PARAM);
695     }
696
697     if ( *s_path->m_name == '\0' ) {
698         return( AFPERR_BADTYPE );
699     }
700
701     upath = s_path->u_name;
702     
703     /* if upath is deleted we already in trouble anyway */
704     if ((of = of_findname(vol, s_path))) {
705         adp = of->of_ad;
706     } else {
707         ad_init(&ad, vol->v_adouble, vol->v_ad_options);
708         adp = &ad;
709     }
710     if ( creatf) {
711         /* on a hard create, fail if file exists and is open */
712         if (of)
713             return AFPERR_BUSY;
714         openf = O_RDWR|O_CREAT|O_TRUNC;
715     } else {
716         /* on a soft create, if the file is open then ad_open won't fail
717            because open syscall is not called
718         */
719         if (of) {
720                 return AFPERR_EXIST;
721         }
722         openf = O_RDWR|O_CREAT|O_EXCL;
723     }
724
725     if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
726                   openf, 0666, adp) < 0 ) {
727         switch ( errno ) {
728         case EROFS:
729             return AFPERR_VLOCK;
730         case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
731             return ( AFPERR_NOOBJ );
732         case EEXIST :
733             return( AFPERR_EXIST );
734         case EACCES :
735             return( AFPERR_ACCESS );
736         case EDQUOT:
737         case ENOSPC :
738             LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
739             return( AFPERR_DFULL );
740         default :
741             return( AFPERR_PARAM );
742         }
743     }
744     if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
745          /* on noadouble volumes, just creating the data fork is ok */
746          if (vol_noadouble(vol)) {
747              ad_close( adp, ADFLAGS_DF );
748              goto createfile_done;
749          }
750          /* FIXME with hard create on an existing file, we already
751           * corrupted the data file.
752           */
753          netatalk_unlink( upath );
754          ad_close( adp, ADFLAGS_DF );
755          return AFPERR_ACCESS;
756     }
757
758     path = s_path->m_name;
759     ad_setname(adp, path);
760
761     struct stat st;
762     if (lstat(upath, &st) != 0) {
763         LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
764             upath, strerror(errno));
765         ad_close( adp, ADFLAGS_DF|ADFLAGS_HF);
766         return AFPERR_MISC;
767     }
768
769     (void)get_id(vol, adp, &st, dir->d_did, upath, strlen(upath));
770
771     ad_flush( adp);
772
773     fce_register_new_file(s_path);
774
775     ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
776
777 createfile_done:
778     curdir->d_offcnt++;
779
780 #ifdef DROPKLUDGE
781     if (vol->v_flags & AFPVOL_DROPBOX) {
782         retvalue = matchfile2dirperms(upath, vol, did);
783     }
784 #endif /* DROPKLUDGE */
785
786     setvoltime(obj, vol );
787
788     return (retvalue);
789 }
790
791 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
792 {
793     struct vol  *vol;
794     struct dir  *dir;
795     struct path *s_path;
796     int         did, rc;
797     u_int16_t   vid, bitmap;
798
799     *rbuflen = 0;
800     ibuf += 2;
801
802     memcpy(&vid, ibuf, sizeof( vid ));
803     ibuf += sizeof( vid );
804     if (NULL == ( vol = getvolbyvid( vid )) ) {
805         return( AFPERR_PARAM );
806     }
807
808     if (vol->v_flags & AFPVOL_RO)
809         return AFPERR_VLOCK;
810
811     memcpy(&did, ibuf, sizeof( did ));
812     ibuf += sizeof( did );
813     if (NULL == ( dir = dirlookup( vol, did )) ) {
814         return afp_errno; /* was AFPERR_NOOBJ */
815     }
816
817     memcpy(&bitmap, ibuf, sizeof( bitmap ));
818     bitmap = ntohs( bitmap );
819     ibuf += sizeof( bitmap );
820
821     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
822         return get_afp_errno(AFPERR_PARAM);
823     }
824
825     if (path_isadir(s_path)) {
826         return( AFPERR_BADTYPE ); /* it's a directory */
827     }
828
829     if ( s_path->st_errno != 0 ) {
830         return( AFPERR_NOOBJ );
831     }
832
833     if ((u_long)ibuf & 1 ) {
834         ibuf++;
835     }
836
837     if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
838         setvoltime(obj, vol );
839     }
840
841     return( rc );
842 }
843
844 /*
845  * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic  
846  * 
847 */
848 extern struct path Cur_Path;
849
850 int setfilparams(struct vol *vol,
851                  struct path *path, u_int16_t f_bitmap, char *buf )
852 {
853     struct adouble      ad, *adp;
854     struct extmap       *em;
855     int                 bit, isad = 1, err = AFP_OK;
856     char                *upath;
857     u_char              achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
858     u_int16_t           ashort, bshort, oshort;
859     u_int32_t           aint;
860     u_int32_t           upriv;
861     u_int16_t           upriv_bit = 0;
862     
863     struct utimbuf      ut;
864
865     int                 change_mdate = 0;
866     int                 change_parent_mdate = 0;
867     int                 newdate = 0;
868     struct timeval      tv;
869     uid_t               f_uid;
870     gid_t               f_gid;
871     u_int16_t           bitmap = f_bitmap;
872     u_int32_t           cdate,bdate;
873     u_char              finder_buf[32];
874     int fp;
875     ssize_t len;
876     char symbuf[MAXPATHLEN+1];
877
878 #ifdef DEBUG
879     LOG(log_debug9, logtype_afpd, "begin setfilparams:");
880 #endif /* DEBUG */
881
882     adp = of_ad(vol, path, &ad);
883     upath = path->u_name;
884
885     if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
886         return AFPERR_ACCESS;
887     }
888
889     /* with unix priv maybe we have to change adouble file priv first */
890     bit = 0;
891     while ( bitmap != 0 ) {
892         while (( bitmap & 1 ) == 0 ) {
893             bitmap = bitmap>>1;
894             bit++;
895         }
896         switch(  bit ) {
897         case FILPBIT_ATTR :
898             change_mdate = 1;
899             memcpy(&ashort, buf, sizeof( ashort ));
900             buf += sizeof( ashort );
901             break;
902         case FILPBIT_CDATE :
903             change_mdate = 1;
904             memcpy(&cdate, buf, sizeof(cdate));
905             buf += sizeof( cdate );
906             break;
907         case FILPBIT_MDATE :
908             memcpy(&newdate, buf, sizeof( newdate ));
909             buf += sizeof( newdate );
910             break;
911         case FILPBIT_BDATE :
912             change_mdate = 1;
913             memcpy(&bdate, buf, sizeof( bdate));
914             buf += sizeof( bdate );
915             break;
916         case FILPBIT_FINFO :
917             change_mdate = 1;
918             if (memcmp(buf,"slnkrhap",8) == 0
919                 && !(S_ISLNK(path->st.st_mode))
920                 && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
921                 /* request to turn this into a symlink */
922                 if ((fp = open(path->u_name, O_RDONLY)) == -1) {
923                     err = AFPERR_MISC;
924                     goto setfilparam_done;
925                 }
926                 len = read(fp, symbuf, MAXPATHLEN);
927                 close(fp);
928                 if (!(len > 0)) {
929                     err = AFPERR_MISC;
930                     goto setfilparam_done;
931                 }
932                 if (unlink(path->u_name) != 0) {
933                     err = AFPERR_MISC;
934                     goto setfilparam_done;
935                 }
936                 symbuf[len] = 0;
937                 if (symlink(symbuf, path->u_name) != 0) {
938                     err = AFPERR_MISC;
939                     goto setfilparam_done;
940                 }
941                 of_stat(vol, path);
942             }
943             memcpy(finder_buf, buf, 32 );
944             buf += 32;
945             break;
946         case FILPBIT_UNIXPR :
947             if (!vol_unix_priv(vol)) {
948                 /* this volume doesn't use unix priv */
949                 err = AFPERR_BITMAP;
950                 bitmap = 0;
951                 break;
952             }
953             change_mdate = 1;
954             change_parent_mdate = 1;
955
956             memcpy( &aint, buf, sizeof( aint ));
957             f_uid = ntohl (aint);
958             buf += sizeof( aint );
959             memcpy( &aint, buf, sizeof( aint ));
960             f_gid = ntohl (aint);
961             buf += sizeof( aint );
962             setfilowner(vol, f_uid, f_gid, path);
963
964             memcpy( &upriv, buf, sizeof( upriv ));
965             buf += sizeof( upriv );
966             upriv = ntohl (upriv);
967             if ((upriv & S_IWUSR)) {
968                 setfilunixmode(vol, path, upriv);
969             }
970             else {
971                 /* do it later */
972                 upriv_bit = 1;
973             }
974             break;
975         case FILPBIT_PDINFO :
976             if (afp_version < 30) { /* else it's UTF8 name */
977                 achar = *buf;
978                 buf += 2;
979                 /* Keep special case to support crlf translations */
980                 if ((unsigned int) achar == 0x04) {
981                     fdType = (u_char *)"TEXT";
982                     buf += 2;
983                 } else {
984                     xyy[0] = ( u_char ) 'p';
985                     xyy[1] = achar;
986                     xyy[3] = *buf++;
987                     xyy[2] = *buf++;
988                     fdType = xyy;
989                 }
990                 break;
991             }
992             /* fallthrough */
993         default :
994             err = AFPERR_BITMAP;
995             /* break while loop */
996             bitmap = 0;
997             break;
998         }
999
1000         bitmap = bitmap>>1;
1001         bit++;
1002     }
1003
1004     /* second try with adouble open 
1005     */
1006     if ( ad_open_metadata( upath, 0, O_CREAT, adp) < 0) {
1007         LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
1008         /*
1009          * For some things, we don't need an adouble header:
1010          * - change of modification date
1011          * - UNIX privs (Bug-ID #2863424)
1012          */
1013         if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
1014             LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
1015             return AFPERR_ACCESS;
1016         }
1017         LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
1018         isad = 0;
1019     } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
1020         ad_setname(adp, path->m_name);
1021     }
1022     
1023     bit = 0;
1024     bitmap = f_bitmap;
1025     while ( bitmap != 0 ) {
1026         while (( bitmap & 1 ) == 0 ) {
1027             bitmap = bitmap>>1;
1028             bit++;
1029         }
1030
1031         switch(  bit ) {
1032         case FILPBIT_ATTR :
1033             ad_getattr(adp, &bshort);
1034             oshort = bshort;
1035             if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1036                 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1037             } else {
1038                 bshort &= ~ashort;
1039             }
1040             if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1041                 change_parent_mdate = 1;
1042             ad_setattr(adp, bshort);
1043             break;
1044         case FILPBIT_CDATE :
1045             ad_setdate(adp, AD_DATE_CREATE, cdate);
1046             break;
1047         case FILPBIT_MDATE :
1048             break;
1049         case FILPBIT_BDATE :
1050             ad_setdate(adp, AD_DATE_BACKUP, bdate);
1051             break;
1052         case FILPBIT_FINFO :
1053             if (default_type( ad_entry( adp, ADEID_FINDERI ))
1054                     && ( 
1055                      ((em = getextmap( path->m_name )) &&
1056                       !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1057                       !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1058                      || ((em = getdefextmap()) &&
1059                       !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1060                       !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1061             )) {
1062                 memcpy(finder_buf, ufinderi, 8 );
1063             }
1064             memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1065             break;
1066         case FILPBIT_UNIXPR :
1067             if (upriv_bit) {
1068                 setfilunixmode(vol, path, upriv);
1069             }
1070             break;
1071         case FILPBIT_PDINFO :
1072             if (afp_version < 30) { /* else it's UTF8 name */
1073                 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1074                 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1075                 break;
1076             }
1077             /* fallthrough */
1078         default :
1079             err = AFPERR_BITMAP;
1080             goto setfilparam_done;
1081         }
1082         bitmap = bitmap>>1;
1083         bit++;
1084     }
1085
1086 setfilparam_done:
1087     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1088        newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1089     }
1090     if (newdate) {
1091        if (isad)
1092           ad_setdate(adp, AD_DATE_MODIFY, newdate);
1093        ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1094        utime(upath, &ut);
1095     }
1096
1097     if (isad) {
1098         ad_flush( adp);
1099         ad_close_metadata( adp);
1100
1101     }
1102
1103     if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1104         newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1105         bitmap = 1<<FILPBIT_MDATE;
1106         setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1107     }
1108
1109 #ifdef DEBUG
1110     LOG(log_debug9, logtype_afpd, "end setfilparams:");
1111 #endif /* DEBUG */
1112     return err;
1113 }
1114
1115 /*
1116  * renamefile and copyfile take the old and new unix pathnames
1117  * and the new mac name.
1118  *
1119  * sdir_fd     source dir fd to which src path is relative (for openat et al semantics)
1120  *             passing -1 means this is not used, src path is a full path
1121  * src         the source path 
1122  * dst         the dest filename in current dir
1123  * newname     the dest mac name
1124  * adp         adouble struct of src file, if open, or & zeroed one
1125  *
1126  */
1127 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1128 {
1129     int         rc;
1130
1131     LOG(log_debug, logtype_afpd,
1132         "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1133
1134     if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1135         switch ( errno ) {
1136         case ENOENT :
1137             return( AFPERR_NOOBJ );
1138         case EPERM:
1139         case EACCES :
1140             return( AFPERR_ACCESS );
1141         case EROFS:
1142             return AFPERR_VLOCK;
1143         case EXDEV :                    /* Cross device move -- try copy */
1144            /* NOTE: with open file it's an error because after the copy we will 
1145             * get two files, it's fixable for our process (eg reopen the new file, get the
1146             * locks, and so on. But it doesn't solve the case with a second process
1147             */
1148             if (adp->ad_open_forks) {
1149                 /* FIXME  warning in syslog so admin'd know there's a conflict ?*/
1150                 return AFPERR_OLOCK; /* little lie */
1151             }
1152             if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1153                 /* on error copyfile delete dest */
1154                 return( rc );
1155             }
1156             return deletefile(vol, sdir_fd, src, 0);
1157         default :
1158             return( AFPERR_PARAM );
1159         }
1160     }
1161
1162     if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1163         int err;
1164         
1165         err = errno;        
1166         /* try to undo the data fork rename,
1167          * we know we are on the same device 
1168         */
1169         if (err) {
1170         unix_rename(-1, dst, sdir_fd, src ); 
1171             /* return the first error */
1172             switch ( err) {
1173             case ENOENT :
1174                 return AFPERR_NOOBJ;
1175             case EPERM:
1176             case EACCES :
1177                 return AFPERR_ACCESS ;
1178             case EROFS:
1179                 return AFPERR_VLOCK;
1180             default :
1181                 return AFPERR_PARAM ;
1182             }
1183         }
1184     }
1185
1186     /* don't care if we can't open the newly renamed ressource fork
1187      */
1188     if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1189         ad_setname(adp, newname);
1190         ad_flush( adp );
1191         ad_close( adp, ADFLAGS_HF );
1192     }
1193
1194     return( AFP_OK );
1195 }
1196
1197 /* ---------------- 
1198    convert a Mac long name to an utf8 name,
1199 */
1200 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1201 {
1202 size_t    outlen;
1203
1204     if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1205         return -1;
1206     }
1207     return outlen;
1208 }
1209
1210 /* ---------------- */
1211 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1212 {
1213 char        type = *ibuf;
1214 size_t      plen = 0;
1215 u_int16_t   len16;
1216 u_int32_t   hint;
1217
1218     if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1219         return -1;
1220     }
1221     ibuf++;
1222     switch (type) {
1223     case 2:
1224         if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1225             if (afp_version >= 30) {
1226                 /* convert it to UTF8 
1227                 */
1228                 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1229                    return -1;
1230             }
1231             else {
1232                 strncpy( newname, ibuf, plen );
1233                 newname[ plen ] = '\0';
1234             }
1235             if (strlen(newname) != plen) {
1236                 /* there's \0 in newname, e.g. it's a pathname not
1237                  * only a filename. 
1238                 */
1239                 return -1;
1240             }
1241         }
1242         break;
1243     case 3:
1244         memcpy(&hint, ibuf, sizeof(hint));
1245         ibuf += sizeof(hint);
1246            
1247         memcpy(&len16, ibuf, sizeof(len16));
1248         ibuf += sizeof(len16);
1249         plen = ntohs(len16);
1250         
1251         if (plen) {
1252             if (plen > AFPOBJ_TMPSIZ) {
1253                 return -1;
1254             }
1255             strncpy( newname, ibuf, plen );
1256             newname[ plen ] = '\0';
1257             if (strlen(newname) != plen) {
1258                 return -1;
1259             }
1260         }
1261         break;
1262     }
1263     return plen;
1264 }
1265
1266 /* -----------------------------------
1267 */
1268 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1269 {
1270     struct vol  *s_vol, *d_vol;
1271     struct dir  *dir;
1272     char        *newname, *p, *upath;
1273     struct path *s_path;
1274     u_int32_t   sdid, ddid;
1275     int         err, retvalue = AFP_OK;
1276     u_int16_t   svid, dvid;
1277
1278     struct adouble ad, *adp;
1279     int denyreadset;
1280     
1281     *rbuflen = 0;
1282     ibuf += 2;
1283
1284     memcpy(&svid, ibuf, sizeof( svid ));
1285     ibuf += sizeof( svid );
1286     if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1287         return( AFPERR_PARAM );
1288     }
1289
1290     memcpy(&sdid, ibuf, sizeof( sdid ));
1291     ibuf += sizeof( sdid );
1292     if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1293         return afp_errno;
1294     }
1295
1296     memcpy(&dvid, ibuf, sizeof( dvid ));
1297     ibuf += sizeof( dvid );
1298     memcpy(&ddid, ibuf, sizeof( ddid ));
1299     ibuf += sizeof( ddid );
1300
1301     if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1302         return get_afp_errno(AFPERR_PARAM);
1303     }
1304     if ( path_isadir(s_path) ) {
1305         return( AFPERR_BADTYPE );
1306     }
1307
1308     /* don't allow copies when the file is open.
1309      * XXX: the spec only calls for read/deny write access.
1310      *      however, copyfile doesn't have any of that info,
1311      *      and locks need to stay coherent. as a result,
1312      *      we just balk if the file is opened already. */
1313
1314     adp = of_ad(s_vol, s_path, &ad);
1315
1316     if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1317         return AFPERR_DENYCONF;
1318     }
1319     denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 || 
1320                   getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1321
1322     if (denyreadset) {
1323         retvalue = AFPERR_DENYCONF;
1324         goto copy_exit;
1325     }
1326
1327     newname = obj->newtmp;
1328     strcpy( newname, s_path->m_name );
1329
1330     p = ctoupath( s_vol, curdir, newname );
1331     if (!p) {
1332         retvalue = AFPERR_PARAM;
1333         goto copy_exit;
1334     }
1335
1336 #ifdef FORCE_UIDGID
1337     /* FIXME svid != dvid && dvid's user can't read svid */
1338 #endif
1339     if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1340         retvalue = AFPERR_PARAM;
1341         goto copy_exit;
1342     }
1343
1344     if (d_vol->v_flags & AFPVOL_RO) {
1345         retvalue = AFPERR_VLOCK;
1346         goto copy_exit;
1347     }
1348
1349     if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1350         retvalue = afp_errno;
1351         goto copy_exit;
1352     }
1353
1354     if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1355         retvalue = get_afp_errno(AFPERR_NOOBJ);
1356         goto copy_exit;
1357     }
1358     
1359     if ( *s_path->m_name != '\0' ) {
1360         retvalue =path_error(s_path, AFPERR_NOOBJ);
1361         goto copy_exit;
1362     }
1363
1364     /* one of the handful of places that knows about the path type */
1365     if (copy_path_name(d_vol, newname, ibuf) < 0) {
1366         retvalue = AFPERR_PARAM;
1367         goto copy_exit;
1368     }
1369     /* newname is always only a filename so curdir *is* its
1370      * parent folder
1371     */
1372     if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1373         retvalue =AFPERR_PARAM;
1374         goto copy_exit;
1375     }
1376
1377     if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1378         retvalue = err;
1379         goto copy_exit;
1380     }
1381     curdir->d_offcnt++;
1382
1383 #ifdef DROPKLUDGE
1384     if (vol->v_flags & AFPVOL_DROPBOX) {
1385         retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1386     }
1387 #endif /* DROPKLUDGE */
1388
1389     setvoltime(obj, d_vol );
1390
1391 copy_exit:
1392     ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1393     return( retvalue );
1394 }
1395
1396 /* ----------------------- */
1397 static int copy_all(const int dfd, const void *buf,
1398                                size_t buflen)
1399 {
1400     ssize_t cc;
1401
1402 #ifdef DEBUG
1403     LOG(log_debug9, logtype_afpd, "begin copy_all:");
1404 #endif /* DEBUG */
1405
1406     while (buflen > 0) {
1407         if ((cc = write(dfd, buf, buflen)) < 0) {
1408             switch (errno) {
1409             case EINTR:
1410                 continue;
1411             default:
1412                 return -1;
1413             }
1414         }
1415         buflen -= cc;
1416     }
1417
1418 #ifdef DEBUG
1419     LOG(log_debug9, logtype_afpd, "end copy_all:");
1420 #endif /* DEBUG */
1421
1422     return 0;
1423 }
1424
1425 /* -------------------------- 
1426  * copy only the fork data stream
1427 */
1428 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1429 {
1430     ssize_t cc;
1431     int     err = 0;
1432     char    filebuf[8192];
1433     int     sfd, dfd;
1434     
1435     if (eid == ADEID_DFORK) {
1436         sfd = ad_data_fileno(ads);
1437         dfd = ad_data_fileno(add);
1438     }
1439     else {
1440         sfd = ad_reso_fileno(ads);
1441         dfd = ad_reso_fileno(add);
1442     }        
1443
1444     if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1445         return -1;
1446
1447     if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1448         return -1;
1449         
1450 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1451     /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1452     off_t   offset = 0;
1453     size_t  size;
1454     struct stat         st;
1455     #define BUF 128*1024*1024
1456
1457     if (fstat(sfd, &st) == 0) {
1458         
1459         while (1) {
1460             if ( offset >= st.st_size) {
1461                return 0;
1462             }
1463             size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1464             if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1465                 switch (errno) {
1466                 case ENOSYS:
1467                 case EINVAL:  /* there's no guarantee that all fs support sendfile */
1468                     goto no_sendfile;
1469                 default:
1470                     return -1;
1471                 }
1472             }
1473         }
1474     }
1475     no_sendfile:
1476     lseek(sfd, offset, SEEK_SET);
1477 #endif 
1478
1479     while (1) {
1480         if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1481             if (errno == EINTR)
1482                 continue;
1483             err = -1;
1484             break;
1485         }
1486
1487         if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1488             break;
1489         }
1490     }
1491     return err;
1492 }
1493
1494 /* ----------------------------------
1495  * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1496  * because we are doing it elsewhere.
1497  * currently if newname is NULL then adp is NULL. 
1498  */
1499 int copyfile(const struct vol *s_vol,
1500              const struct vol *d_vol, 
1501              int sfd,
1502              char *src,
1503              char *dst,
1504              char *newname,
1505              struct adouble *adp)
1506 {
1507     struct adouble      ads, add;
1508     int                 err = 0;
1509     int                 ret_err = 0;
1510     int                 adflags;
1511     int                 stat_result;
1512     struct stat         st;
1513     
1514     LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1515         sfd, src, dst, newname);
1516
1517     if (adp == NULL) {
1518         ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options); 
1519         adp = &ads;
1520     }
1521
1522     adflags = ADFLAGS_DF;
1523     if (newname) {
1524         adflags |= ADFLAGS_HF;
1525     }
1526
1527     if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1528         ret_err = errno;
1529         goto done;
1530     }
1531
1532     if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1533         /* no resource fork, don't create one for dst file */
1534         adflags &= ~ADFLAGS_HF;
1535     }
1536
1537     stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1538
1539     if (stat_result < 0) {           
1540       /* unlikely but if fstat fails, the default file mode will be 0666. */
1541       st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1542     }
1543
1544     ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1545     if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1546         ret_err = errno;
1547         ad_close( adp, adflags );
1548         if (EEXIST != ret_err) {
1549             deletefile(d_vol, -1, dst, 0);
1550             goto done;
1551         }
1552         return AFPERR_EXIST;
1553     }
1554     
1555     /*
1556      * XXX if the source and the dest don't use the same resource type it's broken
1557      */
1558     if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1559         /* copy the data fork */
1560         if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1561             if (ad_meta_fileno(adp) != -1)
1562                 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1563         }
1564     }
1565
1566     if (err < 0) {
1567        ret_err = errno;
1568     }
1569
1570     if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1571         /* set the new name in the resource fork */
1572         ad_copy_header(&add, adp);
1573         ad_setname(&add, newname);
1574         ad_flush( &add );
1575     }
1576     ad_close( adp, adflags );
1577
1578     if (ad_close( &add, adflags ) <0) {
1579        ret_err = errno;
1580     } 
1581
1582     if (ret_err) {
1583         deletefile(d_vol, -1, dst, 0);
1584     }
1585     else if (stat_result == 0) {
1586         /* set dest modification date to src date */
1587         struct utimbuf  ut;
1588
1589         ut.actime = ut.modtime = st.st_mtime;
1590         utime(dst, &ut);
1591         /* FIXME netatalk doesn't use resource fork file date
1592          * but maybe we should set its modtime too.
1593         */
1594     }
1595
1596 done:
1597     switch ( ret_err ) {
1598     case 0:
1599         return AFP_OK;
1600     case EDQUOT:
1601     case EFBIG:
1602     case ENOSPC:
1603         LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1604         return AFPERR_DFULL;
1605     case ENOENT:
1606         return AFPERR_NOOBJ;
1607     case EACCES:
1608         return AFPERR_ACCESS;
1609     case EROFS:
1610         return AFPERR_VLOCK;
1611     }
1612     return AFPERR_PARAM;
1613 }
1614
1615
1616 /* -----------------------------------
1617    vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1618    checkAttrib:   1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1619
1620    when deletefile is called we don't have lock on it, file is closed (for us)
1621    untrue if called by renamefile
1622    
1623    ad_open always try to open file RDWR first and ad_lock takes care of
1624    WRITE lock on read only file.
1625 */
1626
1627 static int check_attrib(struct adouble *adp)
1628 {
1629 u_int16_t   bshort = 0;
1630
1631         ad_getattr(adp, &bshort);
1632     /*
1633      * Does kFPDeleteInhibitBit (bit 8) set?
1634      */
1635         if ((bshort & htons(ATTRBIT_NODELETE))) {
1636                 return AFPERR_OLOCK;
1637         }
1638     if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1639         return AFPERR_BUSY;
1640         }
1641         return 0;
1642 }
1643 /* 
1644  * dirfd can be used for unlinkat semantics
1645  */
1646 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1647 {
1648     struct adouble      ad;
1649     struct adouble      *adp = NULL;
1650     int                 adflags, err = AFP_OK;
1651     int                 meta = 0;
1652
1653     LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1654
1655     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1656     if (checkAttrib) {
1657         /* was EACCESS error try to get only metadata */
1658         /* we never want to create a resource fork here, we are going to delete it 
1659          * moreover sometimes deletefile is called with a no existent file and 
1660          * ad_open would create a 0 byte resource fork
1661         */
1662         if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1663             if ((err = check_attrib(&ad))) {
1664                ad_close_metadata(&ad);
1665                return err;
1666             }
1667             meta = 1;
1668         }
1669     }
1670  
1671     /* try to open both forks at once */
1672     adflags = ADFLAGS_DF;
1673     if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1674         switch (errno) {
1675         case ENOENT:
1676             err = AFPERR_NOOBJ;
1677             goto end;
1678         case EACCES: /* maybe it's a file with no write mode for us */
1679             break;   /* was return AFPERR_ACCESS;*/
1680         case EROFS:
1681             err = AFPERR_VLOCK;
1682             goto end;
1683         default:
1684             err = AFPERR_PARAM;
1685             goto end;
1686         }
1687     }
1688     else {
1689         adp = &ad;
1690     }
1691
1692     if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1693         adflags |= ADFLAGS_HF;
1694         /* FIXME we have a pb here because we want to know if a file is open 
1695          * there's a 'priority inversion' if you can't open the ressource fork RW
1696          * you can delete it if it's open because you can't get a write lock.
1697          * 
1698          * ADLOCK_FILELOCK means the whole ressource fork, not only after the 
1699          * metadatas
1700          *
1701          * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1702          */
1703         if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1704             err = AFPERR_BUSY;
1705             goto end;
1706         }
1707     }
1708
1709     if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1710         err = AFPERR_BUSY;
1711     } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1712         cnid_t id;
1713         if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1714             cnid_delete(vol->v_cdb, id);
1715         }
1716     }
1717
1718 end:
1719     if (meta)
1720         ad_close_metadata(&ad);
1721
1722     if (adp)
1723         ad_close( &ad, adflags );  /* ad_close removes locks if any */
1724
1725     return err;
1726 }
1727
1728 /* ------------------------------------ */
1729 /* return a file id */
1730 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1731 {
1732     struct stat         *st;
1733     struct vol          *vol;
1734     struct dir          *dir;
1735     char                *upath;
1736     int                 len;
1737     cnid_t              did, id;
1738     u_short             vid;
1739     struct path         *s_path;
1740
1741     *rbuflen = 0;
1742
1743     ibuf += 2;
1744
1745     memcpy(&vid, ibuf, sizeof(vid));
1746     ibuf += sizeof(vid);
1747
1748     if (NULL == ( vol = getvolbyvid( vid )) ) {
1749         return( AFPERR_PARAM);
1750     }
1751
1752     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1753         return AFPERR_NOOP;
1754     }
1755
1756     if (vol->v_flags & AFPVOL_RO)
1757         return AFPERR_VLOCK;
1758
1759     memcpy(&did, ibuf, sizeof( did ));
1760     ibuf += sizeof(did);
1761
1762     if (NULL == ( dir = dirlookup( vol, did )) ) {
1763         return afp_errno; /* was AFPERR_PARAM */
1764     }
1765
1766     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1767         return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1768     }
1769
1770     if ( path_isadir(s_path) ) {
1771         return( AFPERR_BADTYPE );
1772     }
1773
1774     upath = s_path->u_name;
1775     switch (s_path->st_errno) {
1776         case 0:
1777              break; /* success */
1778         case EPERM:
1779         case EACCES:
1780             return AFPERR_ACCESS;
1781         case ENOENT:
1782             return AFPERR_NOOBJ;
1783         default:
1784             return AFPERR_PARAM;
1785     }
1786     st = &s_path->st;
1787     if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1788         memcpy(rbuf, &id, sizeof(id));
1789         *rbuflen = sizeof(id);
1790         return AFPERR_EXISTID;
1791     }
1792
1793     if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1794         memcpy(rbuf, &id, sizeof(id));
1795         *rbuflen = sizeof(id);
1796         return AFP_OK;
1797     }
1798
1799     return afp_errno;
1800 }
1801
1802 /* ------------------------------- */
1803 struct reenum {
1804     struct vol *vol;
1805     cnid_t     did;
1806 };
1807
1808 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1809 {
1810     struct path   path;
1811     struct reenum *param = data;
1812     struct vol    *vol = param->vol;  
1813     cnid_t        did  = param->did;
1814     cnid_t        aint;
1815     
1816     if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
1817         return 0;
1818     
1819     /* update or add to cnid */
1820     aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1821
1822 #if AD_VERSION > AD_VERSION1
1823     if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1824         struct adouble  ad, *adp;
1825
1826         path.st_errno = 0;
1827         path.st_valid = 1;
1828         path.u_name = de->d_name;
1829             
1830         adp = of_ad(vol, &path, &ad);
1831             
1832         if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1833             return 0;
1834         }
1835         if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1836             ad_flush(adp);
1837         }
1838         ad_close_metadata(adp);
1839     }
1840 #endif /* AD_VERSION > AD_VERSION1 */
1841
1842     return 0;
1843 }
1844
1845 /* --------------------
1846  * Ok the db is out of synch with the dir.
1847  * but if it's a deleted file we don't want to do it again and again.
1848 */
1849 static int
1850 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1851 {
1852     int             ret;
1853     struct reenum   data;
1854     struct stat     st;
1855     
1856     if (vol->v_cdb == NULL) {
1857         return -1;
1858     }
1859     
1860     /* FIXME use of_statdir ? */
1861     if (ostat(name, &st, vol_syml_opt(vol))) {
1862         return -1;
1863     }
1864
1865     if (dirreenumerate(dir, &st)) {
1866         /* we already did it once and the dir haven't been modified */
1867         return dir->d_offcnt;
1868     }
1869     
1870     data.vol = vol;
1871     data.did = dir->d_did;
1872     if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1873         setdiroffcnt(curdir, &st,  ret);
1874         dir->d_flags |= DIRF_CNID;
1875     }
1876
1877     return ret;
1878 }
1879
1880 /* ------------------------------
1881    resolve a file id */
1882 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1883 {
1884     struct vol          *vol;
1885     struct dir          *dir;
1886     char                *upath;
1887     struct path         path;
1888     int                 err, retry=0;
1889     size_t              buflen;
1890     cnid_t              id, cnid;
1891     u_int16_t           vid, bitmap;
1892
1893     static char buffer[12 + MAXPATHLEN + 1];
1894     int len = 12 + MAXPATHLEN + 1;
1895
1896     *rbuflen = 0;
1897     ibuf += 2;
1898
1899     memcpy(&vid, ibuf, sizeof(vid));
1900     ibuf += sizeof(vid);
1901
1902     if (NULL == ( vol = getvolbyvid( vid )) ) {
1903         return( AFPERR_PARAM);
1904     }
1905
1906     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1907         return AFPERR_NOOP;
1908     }
1909
1910     memcpy(&id, ibuf, sizeof( id ));
1911     ibuf += sizeof(id);
1912     cnid = id;
1913     
1914     if (!id) {
1915         /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1916         return AFPERR_NOID;
1917     }
1918 retry:
1919     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1920         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1921     }
1922
1923     if (NULL == ( dir = dirlookup( vol, id )) ) {
1924         return AFPERR_NOID; /* idem AFPERR_PARAM */
1925     }
1926     if (movecwd(vol, dir) < 0) {
1927         switch (errno) {
1928         case EACCES:
1929         case EPERM:
1930             return AFPERR_ACCESS;
1931         case ENOENT:
1932             return AFPERR_NOID;
1933         default:
1934             return AFPERR_PARAM;
1935         }
1936     }
1937
1938     memset(&path, 0, sizeof(path));
1939     path.u_name = upath;
1940     if (of_stat(vol, &path) < 0 ) {
1941 #ifdef ESTALE
1942         /* with nfs and our working directory is deleted */
1943         if (errno == ESTALE) {
1944             errno = ENOENT;
1945         }
1946 #endif  
1947         if ( errno == ENOENT && !retry) {
1948             /* cnid db is out of sync, reenumerate the directory and update ids */
1949             reenumerate_id(vol, ".", dir);
1950             id = cnid;
1951             retry = 1;
1952             goto retry;
1953         }
1954         switch (errno) {
1955         case EACCES:
1956         case EPERM:
1957             return AFPERR_ACCESS;
1958         case ENOENT:
1959             return AFPERR_NOID;
1960         default:
1961             return AFPERR_PARAM;
1962         }
1963     }
1964
1965     /* directories are bad */
1966     if (S_ISDIR(path.st.st_mode)) {
1967         /* OS9 and OSX don't return the same error code  */
1968         return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1969     }
1970
1971     memcpy(&bitmap, ibuf, sizeof(bitmap));
1972     bitmap = ntohs( bitmap );
1973     if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1974         return AFPERR_NOID;
1975     }
1976     path.id = cnid;
1977     if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir, 
1978                             rbuf + sizeof(bitmap), &buflen))) {
1979         return err;
1980     }
1981     *rbuflen = buflen + sizeof(bitmap);
1982     memcpy(rbuf, ibuf, sizeof(bitmap));
1983
1984     return AFP_OK;
1985 }
1986
1987 /* ------------------------------ */
1988 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1989 {
1990     struct stat         st;
1991     struct vol          *vol;
1992     struct dir          *dir;
1993     char                *upath;
1994     int                 err;
1995     cnid_t              id;
1996     cnid_t              fileid;
1997     u_short             vid;
1998     static char buffer[12 + MAXPATHLEN + 1];
1999     int len = 12 + MAXPATHLEN + 1;
2000
2001     *rbuflen = 0;
2002     ibuf += 2;
2003
2004     memcpy(&vid, ibuf, sizeof(vid));
2005     ibuf += sizeof(vid);
2006
2007     if (NULL == ( vol = getvolbyvid( vid )) ) {
2008         return( AFPERR_PARAM);
2009     }
2010
2011     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
2012         return AFPERR_NOOP;
2013     }
2014
2015     if (vol->v_flags & AFPVOL_RO)
2016         return AFPERR_VLOCK;
2017
2018     memcpy(&id, ibuf, sizeof( id ));
2019     ibuf += sizeof(id);
2020     fileid = id;
2021
2022     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
2023         return AFPERR_NOID;
2024     }
2025
2026     if (NULL == ( dir = dirlookup( vol, id )) ) {
2027         if (afp_errno == AFPERR_NOOBJ) {
2028             err = AFPERR_NOOBJ;
2029             goto delete;
2030         }
2031         return( AFPERR_PARAM );
2032     }
2033
2034     err = AFP_OK;
2035     if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
2036         switch (errno) {
2037         case EACCES:
2038         case EPERM:
2039             return AFPERR_ACCESS;
2040 #ifdef ESTALE
2041         case ESTALE:
2042 #endif  
2043         case ENOENT:
2044             /* still try to delete the id */
2045             err = AFPERR_NOOBJ;
2046             break;
2047         default:
2048             return AFPERR_PARAM;
2049         }
2050     }
2051     else if (S_ISDIR(st.st_mode)) /* directories are bad */
2052         return AFPERR_BADTYPE;
2053
2054 delete:
2055     if (cnid_delete(vol->v_cdb, fileid)) {
2056         switch (errno) {
2057         case EROFS:
2058             return AFPERR_VLOCK;
2059         case EPERM:
2060         case EACCES:
2061             return AFPERR_ACCESS;
2062         default:
2063             return AFPERR_PARAM;
2064         }
2065     }
2066
2067     return err;
2068 }
2069
2070 /* ------------------------------ */
2071 static struct adouble *find_adouble(const struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
2072 {
2073     int             ret;
2074
2075     if (path->st_errno) {
2076         switch (path->st_errno) {
2077         case ENOENT:
2078             afp_errno = AFPERR_NOID;
2079             break;
2080         case EPERM:
2081         case EACCES:
2082             afp_errno = AFPERR_ACCESS;
2083             break;
2084         default:
2085             afp_errno = AFPERR_PARAM;
2086             break;
2087         }
2088         return NULL;
2089     }
2090     /* we use file_access both for legacy Mac perm and
2091      * for unix privilege, rename will take care of folder perms
2092     */
2093     if (file_access(path, OPENACC_WR ) < 0) {
2094         afp_errno = AFPERR_ACCESS;
2095         return NULL;
2096     }
2097     
2098     if ((*of = of_findname(vol, path))) {
2099         /* reuse struct adouble so it won't break locks */
2100         adp = (*of)->of_ad;
2101     }
2102     else {
2103         ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2104         /* META and HF */
2105         if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2106             /* from AFP spec.
2107              * The user must have the Read & Write privilege for both files in order to use this command.
2108              */
2109             ad_close(adp, ADFLAGS_HF);
2110             afp_errno = AFPERR_ACCESS;
2111             return NULL;
2112         }
2113     }
2114     return adp;
2115 }
2116
2117 #define APPLETEMP ".AppleTempXXXXXX"
2118
2119 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2120 {
2121     struct stat         srcst, destst;
2122     struct vol          *vol;
2123     struct dir          *dir, *sdir;
2124     char                *spath, temp[17], *p;
2125     char                *supath, *upath;
2126     struct path         *path;
2127     int                 err;
2128     struct adouble      ads;
2129     struct adouble      add;
2130     struct adouble      *adsp = NULL;
2131     struct adouble      *addp = NULL;
2132     struct ofork        *s_of = NULL;
2133     struct ofork        *d_of = NULL;
2134     int                 crossdev;
2135     
2136     int                 slen, dlen;
2137     u_int32_t           sid, did;
2138     u_int16_t           vid;
2139
2140     uid_t              uid;
2141     gid_t              gid;
2142
2143     *rbuflen = 0;
2144     ibuf += 2;
2145
2146     memcpy(&vid, ibuf, sizeof(vid));
2147     ibuf += sizeof(vid);
2148
2149     if (NULL == ( vol = getvolbyvid( vid )) ) {
2150         return( AFPERR_PARAM);
2151     }
2152
2153     if ((vol->v_flags & AFPVOL_RO))
2154         return AFPERR_VLOCK;
2155
2156     /* source and destination dids */
2157     memcpy(&sid, ibuf, sizeof(sid));
2158     ibuf += sizeof(sid);
2159     memcpy(&did, ibuf, sizeof(did));
2160     ibuf += sizeof(did);
2161
2162     /* source file */
2163     if (NULL == (dir = dirlookup( vol, sid )) ) {
2164         return afp_errno; /* was AFPERR_PARAM */
2165     }
2166
2167     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2168         return get_afp_errno(AFPERR_NOOBJ); 
2169     }
2170
2171     if ( path_isadir(path) ) {
2172         return AFPERR_BADTYPE;   /* it's a dir */
2173     }
2174
2175     /* save some stuff */
2176     srcst = path->st;
2177     sdir = curdir;
2178     spath = obj->oldtmp;
2179     supath = obj->newtmp;
2180     strcpy(spath, path->m_name);
2181     strcpy(supath, path->u_name); /* this is for the cnid changing */
2182     p = absupath( vol, sdir, supath);
2183     if (!p) {
2184         /* pathname too long */
2185         return AFPERR_PARAM ;
2186     }
2187     
2188     ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2189     if (!(adsp = find_adouble(vol, path, &s_of, &ads))) {
2190         return afp_errno;
2191     }
2192
2193     /* ***** from here we may have resource fork open **** */
2194     
2195     /* look for the source cnid. if it doesn't exist, don't worry about
2196      * it. */
2197     sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2198
2199     if (NULL == ( dir = dirlookup( vol, did )) ) {
2200         err = afp_errno; /* was AFPERR_PARAM */
2201         goto err_exchangefile;
2202     }
2203
2204     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2205         err = get_afp_errno(AFPERR_NOOBJ); 
2206         goto err_exchangefile;
2207     }
2208
2209     if ( path_isadir(path) ) {
2210         err = AFPERR_BADTYPE;
2211         goto err_exchangefile;
2212     }
2213
2214     /* FPExchangeFiles is the only call that can return the SameObj
2215      * error */
2216     if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2217         err = AFPERR_SAMEOBJ;
2218         goto err_exchangefile;
2219     }
2220
2221     ad_init(&add, vol->v_adouble, vol->v_ad_options);
2222     if (!(addp = find_adouble(vol, path, &d_of, &add))) {
2223         err = afp_errno;
2224         goto err_exchangefile;
2225     }
2226     destst = path->st;
2227
2228     /* they are not on the same device and at least one is open
2229      * FIXME broken for for crossdev and adouble v2
2230      * return an error 
2231     */
2232     crossdev = (srcst.st_dev != destst.st_dev);
2233     if (/* (d_of || s_of)  && */ crossdev) {
2234         err = AFPERR_MISC;
2235         goto err_exchangefile;
2236     }
2237
2238     /* look for destination id. */
2239     upath = path->u_name;
2240     did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2241
2242     /* construct a temp name.
2243      * NOTE: the temp file will be in the dest file's directory. it
2244      * will also be inaccessible from AFP. */
2245     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2246     if (!mktemp(temp)) {
2247         err = AFPERR_MISC;
2248         goto err_exchangefile;
2249     }
2250     
2251     if (crossdev) {
2252         /* FIXME we need to close fork for copy, both s_of and d_of are null */
2253        ad_close(adsp, ADFLAGS_HF);
2254        ad_close(addp, ADFLAGS_HF);
2255     }
2256
2257     /* now, quickly rename the file. we error if we can't. */
2258     if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2259         goto err_exchangefile;
2260     of_rename(vol, s_of, sdir, spath, curdir, temp);
2261
2262     /* rename destination to source */
2263     if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2264         goto err_src_to_tmp;
2265     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2266
2267     /* rename temp to destination */
2268     if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2269         goto err_dest_to_src;
2270     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2271
2272     /* id's need switching. src -> dest and dest -> src. 
2273      * we need to re-stat() if it was a cross device copy.
2274     */
2275     if (sid)
2276         cnid_delete(vol->v_cdb, sid);
2277     if (did)
2278         cnid_delete(vol->v_cdb, did);
2279
2280     if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) || 
2281                 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2282        ||
2283         (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
2284                 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2285     ) {
2286         switch (errno) {
2287         case EPERM:
2288         case EACCES:
2289             err = AFPERR_ACCESS;
2290             break;
2291         default:
2292             err = AFPERR_PARAM;
2293         }
2294         goto err_temp_to_dest;
2295     }
2296     
2297     /* here we need to reopen if crossdev */
2298     if (sid && ad_setid(addp, destst.st_dev, destst.st_ino,  sid, sdir->d_did, vol->v_stamp)) 
2299     {
2300        ad_flush( addp );
2301     }
2302         
2303     if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino,  did, curdir->d_did, vol->v_stamp)) 
2304     {
2305        ad_flush( adsp );
2306     }
2307
2308     /* change perms, src gets dest perm and vice versa */
2309
2310     uid = geteuid();
2311     gid = getegid();
2312     if (seteuid(0)) {
2313         LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2314         err = AFP_OK; /* ignore error */
2315         goto err_temp_to_dest;
2316     }
2317
2318     /*
2319      * we need to exchange ACL entries as well
2320      */
2321     /* exchange_acls(vol, p, upath); */
2322
2323     path->st = srcst;
2324     path->st_valid = 1;
2325     path->st_errno = 0;
2326     path->m_name = NULL;
2327     path->u_name = upath;
2328
2329     setfilunixmode(vol, path, destst.st_mode);
2330     setfilowner(vol, destst.st_uid, destst.st_gid, path);
2331
2332     path->st = destst;
2333     path->st_valid = 1;
2334     path->st_errno = 0;
2335     path->u_name = p;
2336
2337     setfilunixmode(vol, path, srcst.st_mode);
2338     setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2339
2340     if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2341         LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2342         exit(EXITERR_SYS);
2343     }
2344
2345     err = AFP_OK;
2346     goto err_exchangefile;
2347
2348     /* all this stuff is so that we can unwind a failed operation
2349      * properly. */
2350 err_temp_to_dest:
2351     /* rename dest to temp */
2352     renamefile(vol, -1, upath, temp, temp, adsp);
2353     of_rename(vol, s_of, curdir, upath, curdir, temp);
2354
2355 err_dest_to_src:
2356     /* rename source back to dest */
2357     renamefile(vol, -1, p, upath, path->m_name, addp);
2358     of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2359
2360 err_src_to_tmp:
2361     /* rename temp back to source */
2362     renamefile(vol, -1, temp, p, spath, adsp);
2363     of_rename(vol, s_of, curdir, temp, sdir, spath);
2364
2365 err_exchangefile:
2366     if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2367        ad_close(adsp, ADFLAGS_HF);
2368     }
2369     if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2370        ad_close(addp, ADFLAGS_HF);
2371     }
2372
2373     struct dir *cached;
2374     if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2375         (void)dir_remove(vol, cached);
2376     if ((cached = dircache_search_by_did(vol, did)) != NULL)
2377         (void)dir_remove(vol, cached);
2378
2379     return err;
2380 }