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