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