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