]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/file.c
9fe0185108454659cdd99de28b0c4195bb0815fa
[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,
596                  struct vol *vol,
597                  uint16_t bitmap,
598                  struct path *path, struct dir *dir, 
599                  char *buf, size_t *buflen )
600 {
601     struct adouble      ad, *adp;
602     int                 opened = 0;
603     int rc;    
604     int flags;
605
606     LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
607
608     opened = PARAM_NEED_ADP(bitmap);
609     adp = NULL;
610
611     if (opened) {
612         char *upath;
613         flags = (bitmap & (1 << FILPBIT_ATTR)) ? ADFLAGS_CHECK_OF : 0;
614
615         adp = of_ad(vol, path, &ad);
616         upath = path->u_name;
617
618         if ( ad_metadata( upath, flags, adp) < 0 ) {
619             switch (errno) {
620             case EACCES:
621                 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
622                 upath, strerror(errno));
623                 return AFPERR_ACCESS;
624             case EIO:
625                 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
626                 /* fall through */
627             case ENOENT:
628             default:
629                 adp = NULL;
630                 break;
631             }
632         }
633     }
634     rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
635     ad_close(adp, ADFLAGS_HF | flags);
636
637     return( rc );
638 }
639
640 /* ----------------------------- */
641 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
642 {
643     struct adouble      ad;
644     struct vol          *vol;
645     struct dir          *dir;
646     struct ofork        *of = NULL;
647     char                *path, *upath;
648     int                 creatf, did, openf, retvalue = AFP_OK;
649     uint16_t            vid;
650     struct path         *s_path;
651     
652     *rbuflen = 0;
653     ibuf++;
654     creatf = (unsigned char) *ibuf++;
655
656     memcpy(&vid, ibuf, sizeof( vid ));
657     ibuf += sizeof( vid );
658
659     if (NULL == ( vol = getvolbyvid( vid )) )
660         return( AFPERR_PARAM );
661
662     if (vol->v_flags & AFPVOL_RO)
663         return AFPERR_VLOCK;
664
665     memcpy(&did, ibuf, sizeof( did));
666     ibuf += sizeof( did );
667
668     if (NULL == ( dir = dirlookup( vol, did )) )
669         return afp_errno;
670
671     if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
672         return get_afp_errno(AFPERR_PARAM);
673     if ( *s_path->m_name == '\0' )
674         return( AFPERR_BADTYPE );
675
676     upath = s_path->u_name;
677     ad_init(&ad, vol);
678     
679     /* if upath is deleted we already in trouble anyway */
680     if ((of = of_findname(s_path))) {
681         if (creatf)
682             return AFPERR_BUSY;
683         else
684             return AFPERR_EXIST;
685     }
686
687     if (creatf)
688         openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
689     else
690         /* on a soft create, if the file is open then ad_open won't fail
691            because open syscall is not called */
692         openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
693
694     if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
695         switch ( errno ) {
696         case EROFS:
697             return AFPERR_VLOCK;
698         case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
699             return ( AFPERR_NOOBJ );
700         case EEXIST :
701             return( AFPERR_EXIST );
702         case EACCES :
703             return( AFPERR_ACCESS );
704         case EDQUOT:
705         case ENOSPC :
706             return( AFPERR_DFULL );
707         default :
708             return( AFPERR_PARAM );
709         }
710     }
711     if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
712          /* FIXME with hard create on an existing file, we already
713           * corrupted the data file.
714           */
715          netatalk_unlink( upath );
716          ad_close( &ad, ADFLAGS_DF );
717          return AFPERR_ACCESS;
718     }
719
720     path = s_path->m_name;
721     ad_setname(&ad, path);
722
723     struct stat st;
724     if (lstat(upath, &st) != 0) {
725         LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
726             upath, strerror(errno));
727         ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
728         return AFPERR_MISC;
729     }
730
731     cnid_t id;
732     if ((id = get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
733         LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
734         goto createfile_iderr;
735     }
736     (void)ad_setid(&ad, st.st_dev, st.st_ino, id, dir->d_did, vol->v_stamp);
737
738 createfile_iderr:
739     ad_flush(&ad);
740     ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
741     fce_register_new_file(s_path);
742
743 createfile_done:
744     curdir->d_offcnt++;
745
746     setvoltime(obj, vol );
747
748     return (retvalue);
749 }
750
751 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
752 {
753     struct vol  *vol;
754     struct dir  *dir;
755     struct path *s_path;
756     int         did, rc;
757     uint16_t    vid, bitmap;
758
759     *rbuflen = 0;
760     ibuf += 2;
761
762     memcpy(&vid, ibuf, sizeof( vid ));
763     ibuf += sizeof( vid );
764     if (NULL == ( vol = getvolbyvid( vid )) ) {
765         return( AFPERR_PARAM );
766     }
767
768     if (vol->v_flags & AFPVOL_RO)
769         return AFPERR_VLOCK;
770
771     memcpy(&did, ibuf, sizeof( did ));
772     ibuf += sizeof( did );
773     if (NULL == ( dir = dirlookup( vol, did )) ) {
774         return afp_errno; /* was AFPERR_NOOBJ */
775     }
776
777     memcpy(&bitmap, ibuf, sizeof( bitmap ));
778     bitmap = ntohs( bitmap );
779     ibuf += sizeof( bitmap );
780
781     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
782         return get_afp_errno(AFPERR_PARAM);
783     }
784
785     if (path_isadir(s_path)) {
786         return( AFPERR_BADTYPE ); /* it's a directory */
787     }
788
789     if ( s_path->st_errno != 0 ) {
790         return( AFPERR_NOOBJ );
791     }
792
793     if ((u_long)ibuf & 1 ) {
794         ibuf++;
795     }
796
797     if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
798         setvoltime(obj, vol );
799     }
800
801     return( rc );
802 }
803
804 /*
805  * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic  
806  * 
807 */
808 extern struct path Cur_Path;
809
810 int setfilparams(const AFPObj *obj, struct vol *vol,
811                  struct path *path, uint16_t f_bitmap, char *buf )
812 {
813     struct adouble      ad, *adp;
814     struct extmap       *em;
815     int                 bit, isad = 1, err = AFP_OK;
816     char                *upath;
817     u_char              achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
818     uint16_t            ashort, bshort, oshort;
819     uint32_t            aint;
820     uint32_t            upriv;
821     uint16_t           upriv_bit = 0;
822         struct utimbuf  ut;
823     int                 change_mdate = 0;
824     int                 change_parent_mdate = 0;
825     int                 newdate = 0;
826     struct timeval      tv;
827     uid_t               f_uid;
828     gid_t               f_gid;
829     uint16_t           bitmap = f_bitmap;
830     uint32_t           cdate,bdate;
831     u_char              finder_buf[32];
832     int symlinked = 0;
833
834 #ifdef DEBUG
835     LOG(log_debug9, logtype_afpd, "begin setfilparams:");
836 #endif /* DEBUG */
837
838     adp = of_ad(vol, path, &ad);
839     upath = path->u_name;
840
841     if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
842         return AFPERR_ACCESS;
843     }
844
845     /* with unix priv maybe we have to change adouble file priv first */
846     bit = 0;
847     while ( bitmap != 0 ) {
848         while (( bitmap & 1 ) == 0 ) {
849             bitmap = bitmap>>1;
850             bit++;
851         }
852         switch(  bit ) {
853         case FILPBIT_ATTR :
854             change_mdate = 1;
855             memcpy(&ashort, buf, sizeof( ashort ));
856             buf += sizeof( ashort );
857             break;
858         case FILPBIT_CDATE :
859             change_mdate = 1;
860             memcpy(&cdate, buf, sizeof(cdate));
861             buf += sizeof( cdate );
862             break;
863         case FILPBIT_MDATE :
864             memcpy(&newdate, buf, sizeof( newdate ));
865             buf += sizeof( newdate );
866             break;
867         case FILPBIT_BDATE :
868             change_mdate = 1;
869             memcpy(&bdate, buf, sizeof( bdate));
870             buf += sizeof( bdate );
871             break;
872         case FILPBIT_FINFO :
873             change_mdate = 1;
874             memcpy(finder_buf, buf, 32 );
875             if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
876                 int fp;
877                 ssize_t len;
878                 int erc=1;
879                 char buf[PATH_MAX+1];
880                 if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
881                     if ((len = read(fp, buf, PATH_MAX+1))) {
882                         if (unlink(path->u_name) == 0) {
883                             buf[len] = 0;
884                             erc = symlink(buf, path->u_name);
885                             if (!erc)
886                                 of_stat(path);
887                         }
888                     }
889                     close(fp);
890                 }
891                 if (erc != 0) {
892                     err=AFPERR_BITMAP;
893                     goto setfilparam_done;
894                 }
895                 symlinked = 1;
896             }
897             buf += 32;
898             break;
899         case FILPBIT_UNIXPR :
900             if (!vol_unix_priv(vol)) {
901                 /* this volume doesn't use unix priv */
902                 err = AFPERR_BITMAP;
903                 bitmap = 0;
904                 break;
905             }
906             change_mdate = 1;
907             change_parent_mdate = 1;
908
909             memcpy( &aint, buf, sizeof( aint ));
910             f_uid = ntohl (aint);
911             buf += sizeof( aint );
912             memcpy( &aint, buf, sizeof( aint ));
913             f_gid = ntohl (aint);
914             buf += sizeof( aint );
915             setfilowner(vol, f_uid, f_gid, path);
916
917             memcpy( &upriv, buf, sizeof( upriv ));
918             buf += sizeof( upriv );
919             upriv = ntohl (upriv);
920             if ((upriv & S_IWUSR)) {
921                 setfilunixmode(vol, path, upriv);
922             }
923             else {
924                 /* do it later */
925                 upriv_bit = 1;
926             }
927             break;
928         case FILPBIT_PDINFO :
929             if (obj->afp_version < 30) { /* else it's UTF8 name */
930                 achar = *buf;
931                 buf += 2;
932                 /* Keep special case to support crlf translations */
933                 if ((unsigned int) achar == 0x04) {
934                     fdType = (u_char *)"TEXT";
935                     buf += 2;
936                 } else {
937                     xyy[0] = ( u_char ) 'p';
938                     xyy[1] = achar;
939                     xyy[3] = *buf++;
940                     xyy[2] = *buf++;
941                     fdType = xyy;
942                 }
943                 break;
944             }
945             /* fallthrough */
946         default :
947             err = AFPERR_BITMAP;
948             /* break while loop */
949             bitmap = 0;
950             break;
951         }
952
953         bitmap = bitmap>>1;
954         bit++;
955     }
956
957     /* second try with adouble open 
958     */
959     if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
960         LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
961         /*
962          * For some things, we don't need an adouble header:
963          * - change of modification date
964          * - UNIX privs (Bug-ID #2863424)
965          */
966         if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
967             LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
968             return AFPERR_ACCESS;
969         }
970         LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
971         isad = 0;
972     } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
973         ad_setname(adp, path->m_name);
974     }
975     
976     bit = 0;
977     bitmap = f_bitmap;
978     while ( bitmap != 0 ) {
979         while (( bitmap & 1 ) == 0 ) {
980             bitmap = bitmap>>1;
981             bit++;
982         }
983
984         switch(  bit ) {
985         case FILPBIT_ATTR :
986             ad_getattr(adp, &bshort);
987             oshort = bshort;
988             if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
989                 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
990             } else {
991                 bshort &= ~ashort;
992             }
993             if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
994                 change_parent_mdate = 1;
995             ad_setattr(adp, bshort);
996             break;
997         case FILPBIT_CDATE :
998             ad_setdate(adp, AD_DATE_CREATE, cdate);
999             break;
1000         case FILPBIT_MDATE :
1001             break;
1002         case FILPBIT_BDATE :
1003             ad_setdate(adp, AD_DATE_BACKUP, bdate);
1004             break;
1005         case FILPBIT_FINFO :
1006             memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1007             break;
1008         case FILPBIT_UNIXPR :
1009             if (upriv_bit) {
1010                 setfilunixmode(vol, path, upriv);
1011             }
1012             break;
1013         case FILPBIT_PDINFO :
1014             if (obj->afp_version < 30) { /* else it's UTF8 name */
1015                 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1016                 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1017                 break;
1018             }
1019             /* fallthrough */
1020         default :
1021             err = AFPERR_BITMAP;
1022             goto setfilparam_done;
1023         }
1024         bitmap = bitmap>>1;
1025         bit++;
1026     }
1027
1028 setfilparam_done:
1029     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1030        newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1031     }
1032     if (newdate) {
1033        if (isad)
1034           ad_setdate(adp, AD_DATE_MODIFY, newdate);
1035        ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1036        utime(upath, &ut);
1037     }
1038
1039     if (isad) {
1040         ad_flush(adp);
1041         ad_close(adp, ADFLAGS_HF);
1042     }
1043
1044     if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1045         newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1046         bitmap = 1<<FILPBIT_MDATE;
1047         setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1048     }
1049
1050 #ifdef DEBUG
1051     LOG(log_debug9, logtype_afpd, "end setfilparams:");
1052 #endif /* DEBUG */
1053     return err;
1054 }
1055
1056 /*
1057  * renamefile and copyfile take the old and new unix pathnames
1058  * and the new mac name.
1059  *
1060  * sdir_fd     source dir fd to which src path is relative (for openat et al semantics)
1061  *             passing -1 means this is not used, src path is a full path
1062  * src         the source path 
1063  * dst         the dest filename in current dir
1064  * newname     the dest mac name
1065  * adp         adouble struct of src file, if open, or & zeroed one
1066  *
1067  */
1068 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1069 {
1070     int         rc;
1071
1072     LOG(log_debug, logtype_afpd,
1073         "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1074
1075     if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1076         switch ( errno ) {
1077         case ENOENT :
1078             return( AFPERR_NOOBJ );
1079         case EPERM:
1080         case EACCES :
1081             return( AFPERR_ACCESS );
1082         case EROFS:
1083             return AFPERR_VLOCK;
1084         case EXDEV :                    /* Cross device move -- try copy */
1085            /* NOTE: with open file it's an error because after the copy we will 
1086             * get two files, it's fixable for our process (eg reopen the new file, get the
1087             * locks, and so on. But it doesn't solve the case with a second process
1088             */
1089             if (adp->ad_open_forks) {
1090                 /* FIXME  warning in syslog so admin'd know there's a conflict ?*/
1091                 return AFPERR_OLOCK; /* little lie */
1092             }
1093             if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1094                 /* on error copyfile delete dest */
1095                 return( rc );
1096             }
1097             return deletefile(vol, sdir_fd, src, 0);
1098         default :
1099             return( AFPERR_PARAM );
1100         }
1101     }
1102
1103     if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1104         int err;
1105         
1106         err = errno;        
1107         /* try to undo the data fork rename,
1108          * we know we are on the same device 
1109         */
1110         if (err) {
1111         unix_rename(-1, dst, sdir_fd, src ); 
1112             /* return the first error */
1113             switch ( err) {
1114             case ENOENT :
1115                 return AFPERR_NOOBJ;
1116             case EPERM:
1117             case EACCES :
1118                 return AFPERR_ACCESS ;
1119             case EROFS:
1120                 return AFPERR_VLOCK;
1121             default :
1122                 return AFPERR_PARAM ;
1123             }
1124         }
1125     }
1126
1127     /* don't care if we can't open the newly renamed ressource fork */
1128     if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1129         ad_setname(adp, newname);
1130         ad_flush( adp );
1131         ad_close( adp, ADFLAGS_HF );
1132     }
1133
1134     return( AFP_OK );
1135 }
1136
1137 /* ---------------- 
1138    convert a Mac long name to an utf8 name,
1139 */
1140 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1141 {
1142 size_t    outlen;
1143
1144     if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1145         return -1;
1146     }
1147     return outlen;
1148 }
1149
1150 /* ---------------- */
1151 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1152 {
1153 char        type = *ibuf;
1154 size_t      plen = 0;
1155 uint16_t   len16;
1156 uint32_t   hint;
1157
1158     if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1159         return -1;
1160     }
1161     ibuf++;
1162     switch (type) {
1163     case 2:
1164         if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1165             if (vol->v_obj->afp_version >= 30) {
1166                 /* convert it to UTF8 
1167                 */
1168                 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1169                    return -1;
1170             }
1171             else {
1172                 strncpy( newname, ibuf, plen );
1173                 newname[ plen ] = '\0';
1174             }
1175             if (strlen(newname) != plen) {
1176                 /* there's \0 in newname, e.g. it's a pathname not
1177                  * only a filename. 
1178                 */
1179                 return -1;
1180             }
1181         }
1182         break;
1183     case 3:
1184         memcpy(&hint, ibuf, sizeof(hint));
1185         ibuf += sizeof(hint);
1186            
1187         memcpy(&len16, ibuf, sizeof(len16));
1188         ibuf += sizeof(len16);
1189         plen = ntohs(len16);
1190         
1191         if (plen) {
1192             if (plen > AFPOBJ_TMPSIZ) {
1193                 return -1;
1194             }
1195             strncpy( newname, ibuf, plen );
1196             newname[ plen ] = '\0';
1197             if (strlen(newname) != plen) {
1198                 return -1;
1199             }
1200         }
1201         break;
1202     }
1203     return plen;
1204 }
1205
1206 /* -----------------------------------
1207 */
1208 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1209 {
1210     struct vol  *s_vol, *d_vol;
1211     struct dir  *dir;
1212     char        *newname, *p, *upath;
1213     struct path *s_path;
1214     uint32_t    sdid, ddid;
1215     int         err, retvalue = AFP_OK;
1216     uint16_t    svid, dvid;
1217
1218     struct adouble ad, *adp;
1219     int denyreadset;
1220     
1221     *rbuflen = 0;
1222     ibuf += 2;
1223
1224     memcpy(&svid, ibuf, sizeof( svid ));
1225     ibuf += sizeof( svid );
1226     if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1227         return( AFPERR_PARAM );
1228     }
1229
1230     memcpy(&sdid, ibuf, sizeof( sdid ));
1231     ibuf += sizeof( sdid );
1232     if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1233         return afp_errno;
1234     }
1235
1236     memcpy(&dvid, ibuf, sizeof( dvid ));
1237     ibuf += sizeof( dvid );
1238     memcpy(&ddid, ibuf, sizeof( ddid ));
1239     ibuf += sizeof( ddid );
1240
1241     if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1242         return get_afp_errno(AFPERR_PARAM);
1243     }
1244     if ( path_isadir(s_path) ) {
1245         return( AFPERR_BADTYPE );
1246     }
1247
1248     /* don't allow copies when the file is open.
1249      * XXX: the spec only calls for read/deny write access.
1250      *      however, copyfile doesn't have any of that info,
1251      *      and locks need to stay coherent. as a result,
1252      *      we just balk if the file is opened already. */
1253
1254     adp = of_ad(s_vol, s_path, &ad);
1255
1256     if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1257         return AFPERR_DENYCONF;
1258     }
1259     denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 || 
1260                   ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1261
1262     if (denyreadset) {
1263         retvalue = AFPERR_DENYCONF;
1264         goto copy_exit;
1265     }
1266
1267     newname = obj->newtmp;
1268     strcpy( newname, s_path->m_name );
1269
1270     p = ctoupath( s_vol, curdir, newname );
1271     if (!p) {
1272         retvalue = AFPERR_PARAM;
1273         goto copy_exit;
1274     }
1275
1276     if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1277         retvalue = AFPERR_PARAM;
1278         goto copy_exit;
1279     }
1280
1281     if (d_vol->v_flags & AFPVOL_RO) {
1282         retvalue = AFPERR_VLOCK;
1283         goto copy_exit;
1284     }
1285
1286     if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1287         retvalue = afp_errno;
1288         goto copy_exit;
1289     }
1290
1291     if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1292         retvalue = get_afp_errno(AFPERR_NOOBJ);
1293         goto copy_exit;
1294     }
1295     
1296     if ( *s_path->m_name != '\0' ) {
1297         retvalue =path_error(s_path, AFPERR_NOOBJ);
1298         goto copy_exit;
1299     }
1300
1301     /* one of the handful of places that knows about the path type */
1302     if (copy_path_name(d_vol, newname, ibuf) < 0) {
1303         retvalue = AFPERR_PARAM;
1304         goto copy_exit;
1305     }
1306     /* newname is always only a filename so curdir *is* its
1307      * parent folder
1308     */
1309     if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1310         retvalue =AFPERR_PARAM;
1311         goto copy_exit;
1312     }
1313
1314     if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1315         retvalue = err;
1316         goto copy_exit;
1317     }
1318     curdir->d_offcnt++;
1319
1320     setvoltime(obj, d_vol );
1321
1322 copy_exit:
1323     ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1324     return( retvalue );
1325 }
1326
1327 /* ----------------------------------
1328  * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1329  * because we are doing it elsewhere.
1330  * currently if newname is NULL then adp is NULL. 
1331  */
1332 int copyfile(const struct vol *s_vol,
1333              const struct vol *d_vol, 
1334              int sfd,
1335              char *src,
1336              char *dst,
1337              char *newname,
1338              struct adouble *adp)
1339 {
1340     struct adouble      ads, add;
1341     int                 err = 0;
1342     int                 ret_err = 0;
1343     int                 adflags;
1344     int                 stat_result;
1345     struct stat         st;
1346     
1347     LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1348         sfd, src, dst, newname);
1349
1350     if (adp == NULL) {
1351         ad_init(&ads, s_vol);
1352         adp = &ads;
1353     }
1354
1355     adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1356
1357     if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1358         ret_err = errno;
1359         goto done;
1360     }
1361
1362     if (!AD_RSRC_OPEN(adp))
1363         /* no resource fork, don't create one for dst file */
1364         adflags &= ~ADFLAGS_RF;
1365
1366     stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1367
1368     if (stat_result < 0) {           
1369       /* unlikely but if fstat fails, the default file mode will be 0666. */
1370       st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1371     }
1372
1373     ad_init(&add, d_vol);
1374     if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1375         ret_err = errno;
1376         ad_close( adp, adflags );
1377         if (EEXIST != ret_err) {
1378             deletefile(d_vol, -1, dst, 0);
1379             goto done;
1380         }
1381         return AFPERR_EXIST;
1382     }
1383
1384     if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1385         LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1386
1387     if (err == 0)
1388         if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1389             LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1390
1391     if (err < 0)
1392        ret_err = errno;
1393
1394     if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1395         /* set the new name in the resource fork */
1396         ad_copy_header(&add, adp);
1397         ad_setname(&add, newname);
1398         ad_flush( &add );
1399     }
1400     ad_close( adp, adflags );
1401
1402     if (ad_close( &add, adflags ) <0) {
1403        ret_err = errno;
1404     } 
1405
1406     if (ret_err) {
1407         deletefile(d_vol, -1, dst, 0);
1408     }
1409     else if (stat_result == 0) {
1410         /* set dest modification date to src date */
1411         struct utimbuf  ut;
1412
1413         ut.actime = ut.modtime = st.st_mtime;
1414         utime(dst, &ut);
1415         /* FIXME netatalk doesn't use resource fork file date
1416          * but maybe we should set its modtime too.
1417         */
1418     }
1419
1420 done:
1421     switch ( ret_err ) {
1422     case 0:
1423         return AFP_OK;
1424     case EDQUOT:
1425     case EFBIG:
1426     case ENOSPC:
1427         return AFPERR_DFULL;
1428     case ENOENT:
1429         return AFPERR_NOOBJ;
1430     case EACCES:
1431         return AFPERR_ACCESS;
1432     case EROFS:
1433         return AFPERR_VLOCK;
1434     }
1435     return AFPERR_PARAM;
1436 }
1437
1438
1439 /* -----------------------------------
1440    vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1441    checkAttrib:   1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1442
1443    when deletefile is called we don't have lock on it, file is closed (for us)
1444    untrue if called by renamefile
1445    
1446    ad_open always try to open file RDWR first and ad_lock takes care of
1447    WRITE lock on read only file.
1448 */
1449
1450 static int check_attrib(struct adouble *adp)
1451 {
1452 uint16_t   bshort = 0;
1453
1454         ad_getattr(adp, &bshort);
1455     /*
1456      * Does kFPDeleteInhibitBit (bit 8) set?
1457      */
1458         if ((bshort & htons(ATTRBIT_NODELETE))) {
1459                 return AFPERR_OLOCK;
1460         }
1461     if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1462         return AFPERR_BUSY;
1463         }
1464         return 0;
1465 }
1466 /* 
1467  * dirfd can be used for unlinkat semantics
1468  */
1469 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1470 {
1471     struct adouble      ad;
1472     struct adouble      *adp = NULL;
1473     int                 adflags, err = AFP_OK;
1474     int                 meta = 0;
1475
1476     LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1477
1478     ad_init(&ad, vol);
1479     if (checkAttrib) {
1480         /* was EACCESS error try to get only metadata */
1481         /* we never want to create a resource fork here, we are going to delete it 
1482          * moreover sometimes deletefile is called with a no existent file and 
1483          * ad_open would create a 0 byte resource fork
1484         */
1485         if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1486             if ((err = check_attrib(&ad))) {
1487                 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1488                return err;
1489             }
1490             meta = 1;
1491         }
1492     }
1493  
1494     /* try to open both forks at once */
1495     adflags = ADFLAGS_DF;
1496     if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1497         switch (errno) {
1498         case ENOENT:
1499             err = AFPERR_NOOBJ;
1500             goto end;
1501         case EACCES: /* maybe it's a file with no write mode for us */
1502             break;   /* was return AFPERR_ACCESS;*/
1503         case EROFS:
1504             err = AFPERR_VLOCK;
1505             goto end;
1506         default:
1507             err = AFPERR_PARAM;
1508             goto end;
1509         }
1510     }
1511     else {
1512         adp = &ad;
1513     }
1514
1515     if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1516         adflags |= ADFLAGS_RF;
1517         /* FIXME we have a pb here because we want to know if a file is open 
1518          * there's a 'priority inversion' if you can't open the ressource fork RW
1519          * you can delete it if it's open because you can't get a write lock.
1520          * 
1521          * ADLOCK_FILELOCK means the whole ressource fork, not only after the 
1522          * metadatas
1523          *
1524          * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1525          */
1526         if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1527             err = AFPERR_BUSY;
1528             goto end;
1529         }
1530     }
1531
1532     if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1533         LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1534         err = AFPERR_BUSY;
1535     } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1536         cnid_t id;
1537         if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1538             cnid_delete(vol->v_cdb, id);
1539         }
1540     }
1541
1542 end:
1543     if (meta)
1544         ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1545
1546     if (adp)
1547         ad_close( &ad, adflags );  /* ad_close removes locks if any */
1548
1549     return err;
1550 }
1551
1552 /* ------------------------------------ */
1553 /* return a file id */
1554 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1555 {
1556     struct stat         *st;
1557     struct vol          *vol;
1558     struct dir          *dir;
1559     char                *upath;
1560     int                 len;
1561     cnid_t              did, id;
1562     u_short             vid;
1563     struct path         *s_path;
1564
1565     *rbuflen = 0;
1566
1567     ibuf += 2;
1568
1569     memcpy(&vid, ibuf, sizeof(vid));
1570     ibuf += sizeof(vid);
1571
1572     if (NULL == ( vol = getvolbyvid( vid )) ) {
1573         return( AFPERR_PARAM);
1574     }
1575
1576     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1577         return AFPERR_NOOP;
1578     }
1579
1580     if (vol->v_flags & AFPVOL_RO)
1581         return AFPERR_VLOCK;
1582
1583     memcpy(&did, ibuf, sizeof( did ));
1584     ibuf += sizeof(did);
1585
1586     if (NULL == ( dir = dirlookup( vol, did )) ) {
1587         return afp_errno; /* was AFPERR_PARAM */
1588     }
1589
1590     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1591         return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1592     }
1593
1594     if ( path_isadir(s_path) ) {
1595         return( AFPERR_BADTYPE );
1596     }
1597
1598     upath = s_path->u_name;
1599     switch (s_path->st_errno) {
1600         case 0:
1601              break; /* success */
1602         case EPERM:
1603         case EACCES:
1604             return AFPERR_ACCESS;
1605         case ENOENT:
1606             return AFPERR_NOOBJ;
1607         default:
1608             return AFPERR_PARAM;
1609     }
1610     st = &s_path->st;
1611     if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1612         memcpy(rbuf, &id, sizeof(id));
1613         *rbuflen = sizeof(id);
1614         return AFPERR_EXISTID;
1615     }
1616
1617     if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1618         memcpy(rbuf, &id, sizeof(id));
1619         *rbuflen = sizeof(id);
1620         return AFP_OK;
1621     }
1622
1623     return afp_errno;
1624 }
1625
1626 /* ------------------------------- */
1627 struct reenum {
1628     struct vol *vol;
1629     cnid_t     did;
1630 };
1631
1632 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1633 {
1634     struct path   path;
1635     struct reenum *param = data;
1636     struct vol    *vol = param->vol;  
1637     cnid_t        did  = param->did;
1638     cnid_t        aint;
1639     
1640     if ( lstat(de->d_name, &path.st) < 0 )
1641         return 0;
1642     
1643     /* update or add to cnid */
1644     aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1645
1646     return 0;
1647 }
1648
1649 /* --------------------
1650  * Ok the db is out of synch with the dir.
1651  * but if it's a deleted file we don't want to do it again and again.
1652 */
1653 static int
1654 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1655 {
1656     int             ret;
1657     struct reenum   data;
1658     struct stat     st;
1659     
1660     if (vol->v_cdb == NULL) {
1661         return -1;
1662     }
1663     
1664     /* FIXME use of_statdir ? */
1665     if (lstat(name, &st)) {
1666         return -1;
1667     }
1668
1669     if (dirreenumerate(dir, &st)) {
1670         /* we already did it once and the dir haven't been modified */
1671         return dir->d_offcnt;
1672     }
1673     
1674     data.vol = vol;
1675     data.did = dir->d_did;
1676     if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1677         setdiroffcnt(curdir, &st,  ret);
1678         dir->d_flags |= DIRF_CNID;
1679     }
1680
1681     return ret;
1682 }
1683
1684 /* ------------------------------
1685    resolve a file id */
1686 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1687 {
1688     struct vol          *vol;
1689     struct dir          *dir;
1690     char                *upath;
1691     struct path         path;
1692     int                 err, retry=0;
1693     size_t              buflen;
1694     cnid_t              id, cnid;
1695     uint16_t            vid, bitmap;
1696
1697     static char buffer[12 + MAXPATHLEN + 1];
1698     int len = 12 + MAXPATHLEN + 1;
1699
1700     *rbuflen = 0;
1701     ibuf += 2;
1702
1703     memcpy(&vid, ibuf, sizeof(vid));
1704     ibuf += sizeof(vid);
1705
1706     if (NULL == ( vol = getvolbyvid( vid )) ) {
1707         return( AFPERR_PARAM);
1708     }
1709
1710     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1711         return AFPERR_NOOP;
1712     }
1713
1714     memcpy(&id, ibuf, sizeof( id ));
1715     ibuf += sizeof(id);
1716     cnid = id;
1717     
1718     if (!id) {
1719         /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1720         return AFPERR_NOID;
1721     }
1722 retry:
1723     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1724         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1725     }
1726
1727     if (NULL == ( dir = dirlookup( vol, id )) ) {
1728         return AFPERR_NOID; /* idem AFPERR_PARAM */
1729     }
1730     if (movecwd(vol, dir) < 0) {
1731         switch (errno) {
1732         case EACCES:
1733         case EPERM:
1734             return AFPERR_ACCESS;
1735         case ENOENT:
1736             return AFPERR_NOID;
1737         default:
1738             return AFPERR_PARAM;
1739         }
1740     }
1741
1742     memset(&path, 0, sizeof(path));
1743     path.u_name = upath;
1744     if ( of_stat(&path) < 0 ) {
1745 #ifdef ESTALE
1746         /* with nfs and our working directory is deleted */
1747         if (errno == ESTALE) {
1748             errno = ENOENT;
1749         }
1750 #endif  
1751         if ( errno == ENOENT && !retry) {
1752             /* cnid db is out of sync, reenumerate the directory and update ids */
1753             reenumerate_id(vol, ".", dir);
1754             id = cnid;
1755             retry = 1;
1756             goto retry;
1757         }
1758         switch (errno) {
1759         case EACCES:
1760         case EPERM:
1761             return AFPERR_ACCESS;
1762         case ENOENT:
1763             return AFPERR_NOID;
1764         default:
1765             return AFPERR_PARAM;
1766         }
1767     }
1768
1769     /* directories are bad */
1770     if (S_ISDIR(path.st.st_mode)) {
1771         /* OS9 and OSX don't return the same error code  */
1772         return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1773     }
1774
1775     memcpy(&bitmap, ibuf, sizeof(bitmap));
1776     bitmap = ntohs( bitmap );
1777     if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1778         return AFPERR_NOID;
1779     }
1780     path.id = cnid;
1781     if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir, 
1782                                       rbuf + sizeof(bitmap), &buflen))) {
1783         return err;
1784     }
1785     *rbuflen = buflen + sizeof(bitmap);
1786     memcpy(rbuf, ibuf, sizeof(bitmap));
1787
1788     return AFP_OK;
1789 }
1790
1791 /* ------------------------------ */
1792 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1793 {
1794     struct stat         st;
1795     struct vol          *vol;
1796     struct dir          *dir;
1797     char                *upath;
1798     int                 err;
1799     cnid_t              id;
1800     cnid_t              fileid;
1801     u_short             vid;
1802     static char buffer[12 + MAXPATHLEN + 1];
1803     int len = 12 + MAXPATHLEN + 1;
1804
1805     *rbuflen = 0;
1806     ibuf += 2;
1807
1808     memcpy(&vid, ibuf, sizeof(vid));
1809     ibuf += sizeof(vid);
1810
1811     if (NULL == ( vol = getvolbyvid( vid )) ) {
1812         return( AFPERR_PARAM);
1813     }
1814
1815     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1816         return AFPERR_NOOP;
1817     }
1818
1819     if (vol->v_flags & AFPVOL_RO)
1820         return AFPERR_VLOCK;
1821
1822     memcpy(&id, ibuf, sizeof( id ));
1823     ibuf += sizeof(id);
1824     fileid = id;
1825
1826     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1827         return AFPERR_NOID;
1828     }
1829
1830     if (NULL == ( dir = dirlookup( vol, id )) ) {
1831         if (afp_errno == AFPERR_NOOBJ) {
1832             err = AFPERR_NOOBJ;
1833             goto delete;
1834         }
1835         return( AFPERR_PARAM );
1836     }
1837
1838     err = AFP_OK;
1839     if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1840         switch (errno) {
1841         case EACCES:
1842         case EPERM:
1843             return AFPERR_ACCESS;
1844 #ifdef ESTALE
1845         case ESTALE:
1846 #endif  
1847         case ENOENT:
1848             /* still try to delete the id */
1849             err = AFPERR_NOOBJ;
1850             break;
1851         default:
1852             return AFPERR_PARAM;
1853         }
1854     }
1855     else if (S_ISDIR(st.st_mode)) /* directories are bad */
1856         return AFPERR_BADTYPE;
1857
1858 delete:
1859     if (cnid_delete(vol->v_cdb, fileid)) {
1860         switch (errno) {
1861         case EROFS:
1862             return AFPERR_VLOCK;
1863         case EPERM:
1864         case EACCES:
1865             return AFPERR_ACCESS;
1866         default:
1867             return AFPERR_PARAM;
1868         }
1869     }
1870
1871     return err;
1872 }
1873
1874 /* ------------------------------ */
1875 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1876 {
1877     int             ret;
1878
1879     if (path->st_errno) {
1880         switch (path->st_errno) {
1881         case ENOENT:
1882             afp_errno = AFPERR_NOID;
1883             break;
1884         case EPERM:
1885         case EACCES:
1886             afp_errno = AFPERR_ACCESS;
1887             break;
1888         default:
1889             afp_errno = AFPERR_PARAM;
1890             break;
1891         }
1892         return NULL;
1893     }
1894     /* we use file_access both for legacy Mac perm and
1895      * for unix privilege, rename will take care of folder perms
1896     */
1897     if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1898         afp_errno = AFPERR_ACCESS;
1899         return NULL;
1900     }
1901     
1902     if ((*of = of_findname(path))) {
1903         /* reuse struct adouble so it won't break locks */
1904         adp = (*of)->of_ad;
1905     }
1906     else {
1907         ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1908         /* META and HF */
1909         if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1910             /* from AFP spec.
1911              * The user must have the Read & Write privilege for both files in order to use this command.
1912              */
1913             ad_close(adp, ADFLAGS_HF);
1914             afp_errno = AFPERR_ACCESS;
1915             return NULL;
1916         }
1917     }
1918     return adp;
1919 }
1920
1921 #define APPLETEMP ".AppleTempXXXXXX"
1922
1923 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1924 {
1925     struct stat         srcst, destst;
1926     struct vol          *vol;
1927     struct dir          *dir, *sdir;
1928     char                *spath, temp[17], *p;
1929     char                *supath, *upath;
1930     struct path         *path;
1931     int                 err;
1932     struct adouble      ads;
1933     struct adouble      add;
1934     struct adouble      *adsp = NULL;
1935     struct adouble      *addp = NULL;
1936     struct ofork        *s_of = NULL;
1937     struct ofork        *d_of = NULL;
1938     int                 crossdev;
1939     
1940     int                 slen, dlen;
1941     uint32_t            sid, did;
1942     uint16_t            vid;
1943
1944     uid_t              uid;
1945     gid_t              gid;
1946
1947     *rbuflen = 0;
1948     ibuf += 2;
1949
1950     memcpy(&vid, ibuf, sizeof(vid));
1951     ibuf += sizeof(vid);
1952
1953     if (NULL == ( vol = getvolbyvid( vid )) ) {
1954         return( AFPERR_PARAM);
1955     }
1956
1957     if ((vol->v_flags & AFPVOL_RO))
1958         return AFPERR_VLOCK;
1959
1960     /* source and destination dids */
1961     memcpy(&sid, ibuf, sizeof(sid));
1962     ibuf += sizeof(sid);
1963     memcpy(&did, ibuf, sizeof(did));
1964     ibuf += sizeof(did);
1965
1966     /* source file */
1967     if (NULL == (dir = dirlookup( vol, sid )) ) {
1968         return afp_errno; /* was AFPERR_PARAM */
1969     }
1970
1971     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1972         return get_afp_errno(AFPERR_NOOBJ); 
1973     }
1974
1975     if ( path_isadir(path) ) {
1976         return AFPERR_BADTYPE;   /* it's a dir */
1977     }
1978
1979     /* save some stuff */
1980     srcst = path->st;
1981     sdir = curdir;
1982     spath = obj->oldtmp;
1983     supath = obj->newtmp;
1984     strcpy(spath, path->m_name);
1985     strcpy(supath, path->u_name); /* this is for the cnid changing */
1986     p = absupath( vol, sdir, supath);
1987     if (!p) {
1988         /* pathname too long */
1989         return AFPERR_PARAM ;
1990     }
1991     
1992     ad_init(&ads, vol);
1993     if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
1994         return afp_errno;
1995     }
1996
1997     /* ***** from here we may have resource fork open **** */
1998     
1999     /* look for the source cnid. if it doesn't exist, don't worry about
2000      * it. */
2001     sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2002
2003     if (NULL == ( dir = dirlookup( vol, did )) ) {
2004         err = afp_errno; /* was AFPERR_PARAM */
2005         goto err_exchangefile;
2006     }
2007
2008     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2009         err = get_afp_errno(AFPERR_NOOBJ); 
2010         goto err_exchangefile;
2011     }
2012
2013     if ( path_isadir(path) ) {
2014         err = AFPERR_BADTYPE;
2015         goto err_exchangefile;
2016     }
2017
2018     /* FPExchangeFiles is the only call that can return the SameObj
2019      * error */
2020     if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2021         err = AFPERR_SAMEOBJ;
2022         goto err_exchangefile;
2023     }
2024
2025     ad_init(&add, vol);
2026     if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2027         err = afp_errno;
2028         goto err_exchangefile;
2029     }
2030     destst = path->st;
2031
2032     /* they are not on the same device and at least one is open
2033      * FIXME broken for for crossdev and adouble v2
2034      * return an error 
2035     */
2036     crossdev = (srcst.st_dev != destst.st_dev);
2037     if (/* (d_of || s_of)  && */ crossdev) {
2038         err = AFPERR_MISC;
2039         goto err_exchangefile;
2040     }
2041
2042     /* look for destination id. */
2043     upath = path->u_name;
2044     did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2045
2046     /* construct a temp name.
2047      * NOTE: the temp file will be in the dest file's directory. it
2048      * will also be inaccessible from AFP. */
2049     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2050     if (!mktemp(temp)) {
2051         err = AFPERR_MISC;
2052         goto err_exchangefile;
2053     }
2054     
2055     if (crossdev) {
2056         /* FIXME we need to close fork for copy, both s_of and d_of are null */
2057        ad_close(adsp, ADFLAGS_HF);
2058        ad_close(addp, ADFLAGS_HF);
2059     }
2060
2061     /* now, quickly rename the file. we error if we can't. */
2062     if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2063         goto err_exchangefile;
2064     of_rename(vol, s_of, sdir, spath, curdir, temp);
2065
2066     /* rename destination to source */
2067     if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2068         goto err_src_to_tmp;
2069     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2070
2071     /* rename temp to destination */
2072     if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2073         goto err_dest_to_src;
2074     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2075
2076     /* id's need switching. src -> dest and dest -> src. 
2077      * we need to re-stat() if it was a cross device copy.
2078     */
2079     if (sid)
2080         cnid_delete(vol->v_cdb, sid);
2081     if (did)
2082         cnid_delete(vol->v_cdb, did);
2083
2084     if ((did && ( (crossdev && lstat( upath, &srcst) < 0) || 
2085                 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2086        ||
2087        (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2088                 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2089     ) {
2090         switch (errno) {
2091         case EPERM:
2092         case EACCES:
2093             err = AFPERR_ACCESS;
2094             break;
2095         default:
2096             err = AFPERR_PARAM;
2097         }
2098         goto err_temp_to_dest;
2099     }
2100     
2101     /* here we need to reopen if crossdev */
2102     if (sid && ad_setid(addp, destst.st_dev, destst.st_ino,  sid, sdir->d_did, vol->v_stamp)) 
2103     {
2104        ad_flush( addp );
2105     }
2106         
2107     if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino,  did, curdir->d_did, vol->v_stamp)) 
2108     {
2109        ad_flush( adsp );
2110     }
2111
2112     /* change perms, src gets dest perm and vice versa */
2113
2114     uid = geteuid();
2115     gid = getegid();
2116     if (seteuid(0)) {
2117         LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2118         err = AFP_OK; /* ignore error */
2119         goto err_temp_to_dest;
2120     }
2121
2122     /*
2123      * we need to exchange ACL entries as well
2124      */
2125     /* exchange_acls(vol, p, upath); */
2126
2127     path->st = srcst;
2128     path->st_valid = 1;
2129     path->st_errno = 0;
2130     path->m_name = NULL;
2131     path->u_name = upath;
2132
2133     setfilunixmode(vol, path, destst.st_mode);
2134     setfilowner(vol, destst.st_uid, destst.st_gid, path);
2135
2136     path->st = destst;
2137     path->st_valid = 1;
2138     path->st_errno = 0;
2139     path->u_name = p;
2140
2141     setfilunixmode(vol, path, srcst.st_mode);
2142     setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2143
2144     if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2145         LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2146         exit(EXITERR_SYS);
2147     }
2148
2149     err = AFP_OK;
2150     goto err_exchangefile;
2151
2152     /* all this stuff is so that we can unwind a failed operation
2153      * properly. */
2154 err_temp_to_dest:
2155     /* rename dest to temp */
2156     renamefile(vol, -1, upath, temp, temp, adsp);
2157     of_rename(vol, s_of, curdir, upath, curdir, temp);
2158
2159 err_dest_to_src:
2160     /* rename source back to dest */
2161     renamefile(vol, -1, p, upath, path->m_name, addp);
2162     of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2163
2164 err_src_to_tmp:
2165     /* rename temp back to source */
2166     renamefile(vol, -1, temp, p, spath, adsp);
2167     of_rename(vol, s_of, curdir, temp, sdir, spath);
2168
2169 err_exchangefile:
2170     if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2171        ad_close(adsp, ADFLAGS_HF);
2172     }
2173     if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2174        ad_close(addp, ADFLAGS_HF);
2175     }
2176
2177     struct dir *cached;
2178     if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2179         (void)dir_remove(vol, cached);
2180     if ((cached = dircache_search_by_did(vol, did)) != NULL)
2181         (void)dir_remove(vol, cached);
2182
2183     return err;
2184 }