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