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