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