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