]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/file.c
in setfilparam when switching to a symlink use of_stat rathert than lstat, of_stat...
[netatalk.git] / etc / afpd / file.c
1 /*
2  * $Id: file.c,v 1.137 2010-02-17 01:41:58 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     ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1211     if (denyreadset) {
1212         return AFPERR_DENYCONF;
1213     }
1214
1215     newname = obj->newtmp;
1216     strcpy( newname, s_path->m_name );
1217
1218     p = ctoupath( s_vol, curdir, newname );
1219     if (!p) {
1220         return AFPERR_PARAM;
1221     
1222     }
1223 #ifdef FORCE_UIDGID
1224     /* FIXME svid != dvid && dvid's user can't read svid */
1225 #endif
1226     if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1227         return( AFPERR_PARAM );
1228     }
1229
1230     if (d_vol->v_flags & AFPVOL_RO)
1231         return AFPERR_VLOCK;
1232
1233     if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1234         return afp_errno;
1235     }
1236
1237     if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1238         return get_afp_errno(AFPERR_NOOBJ); 
1239     }
1240     if ( *s_path->m_name != '\0' ) {
1241         path_error(s_path, AFPERR_PARAM);
1242     }
1243
1244     /* one of the handful of places that knows about the path type */
1245     if (copy_path_name(d_vol, newname, ibuf) < 0) {
1246         return( AFPERR_PARAM );
1247     }
1248     /* newname is always only a filename so curdir *is* its
1249      * parent folder
1250     */
1251     if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1252         return( AFPERR_PARAM );
1253     }
1254     if ( (err = copyfile(s_vol, d_vol, p, upath , newname, adp)) < 0 ) {
1255         return err;
1256     }
1257     curdir->offcnt++;
1258
1259 #ifdef DROPKLUDGE
1260     if (vol->v_flags & AFPVOL_DROPBOX) {
1261         retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1262     }
1263 #endif /* DROPKLUDGE */
1264
1265     setvoltime(obj, d_vol );
1266
1267     return( retvalue );
1268 }
1269
1270 /* ----------------------- */
1271 static int copy_all(const int dfd, const void *buf,
1272                                size_t buflen)
1273 {
1274     ssize_t cc;
1275
1276 #ifdef DEBUG
1277     LOG(log_debug9, logtype_afpd, "begin copy_all:");
1278 #endif /* DEBUG */
1279
1280     while (buflen > 0) {
1281         if ((cc = write(dfd, buf, buflen)) < 0) {
1282             switch (errno) {
1283             case EINTR:
1284                 continue;
1285             default:
1286                 return -1;
1287             }
1288         }
1289         buflen -= cc;
1290     }
1291
1292 #ifdef DEBUG
1293     LOG(log_debug9, logtype_afpd, "end copy_all:");
1294 #endif /* DEBUG */
1295
1296     return 0;
1297 }
1298
1299 /* -------------------------- 
1300  * copy only the fork data stream
1301 */
1302 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1303 {
1304     ssize_t cc;
1305     int     err = 0;
1306     char    filebuf[8192];
1307     int     sfd, dfd;
1308     
1309     if (eid == ADEID_DFORK) {
1310         sfd = ad_data_fileno(ads);
1311         dfd = ad_data_fileno(add);
1312     }
1313     else {
1314         sfd = ad_reso_fileno(ads);
1315         dfd = ad_reso_fileno(add);
1316     }        
1317
1318     if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1319         return -1;
1320
1321     if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1322         return -1;
1323         
1324 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1325     /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1326     off_t   offset = 0;
1327     size_t  size;
1328     struct stat         st;
1329     #define BUF 128*1024*1024
1330
1331     if (fstat(sfd, &st) == 0) {
1332         
1333         while (1) {
1334             if ( offset >= st.st_size) {
1335                return 0;
1336             }
1337             size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1338             if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1339                 switch (errno) {
1340                 case ENOSYS:
1341                 case EINVAL:  /* there's no guarantee that all fs support sendfile */
1342                     goto no_sendfile;
1343                 default:
1344                     return -1;
1345                 }
1346             }
1347         }
1348     }
1349     no_sendfile:
1350     lseek(sfd, offset, SEEK_SET);
1351 #endif 
1352
1353     while (1) {
1354         if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1355             if (errno == EINTR)
1356                 continue;
1357             err = -1;
1358             break;
1359         }
1360
1361         if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1362             break;
1363         }
1364     }
1365     return err;
1366 }
1367
1368 /* ----------------------------------
1369  * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1370  * because we are doing it elsewhere.
1371  * currently if newname is NULL then adp is NULL. 
1372  */
1373 int copyfile(const struct vol *s_vol, const struct vol*d_vol, 
1374     char *src, char *dst, char *newname, struct adouble *adp)
1375 {
1376     struct adouble      ads, add;
1377     int                 err = 0;
1378     int                 ret_err = 0;
1379     int                 adflags;
1380     int                 stat_result;
1381     struct stat         st;
1382     
1383 #ifdef DEBUG
1384     LOG(log_debug9, logtype_afpd, "begin copyfile:");
1385 #endif /* DEBUG */
1386
1387     if (adp == NULL) {
1388         ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options); 
1389         adp = &ads;
1390     }
1391     ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1392     adflags = ADFLAGS_DF;
1393     if (newname) {
1394         adflags |= ADFLAGS_HF;
1395     }
1396
1397     if (ad_open(src , adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1398         ret_err = errno;
1399         goto done;
1400     }
1401
1402     if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1403         /* no resource fork, don't create one for dst file */
1404         adflags &= ~ADFLAGS_HF;
1405     }
1406
1407     stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1408
1409     if (stat_result < 0) {           
1410       /* unlikely but if fstat fails, the default file mode will be 0666. */
1411       st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1412     }
1413
1414     if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1415         ret_err = errno;
1416         ad_close( adp, adflags );
1417         if (EEXIST != ret_err) {
1418             deletefile(d_vol, dst, 0);
1419             goto done;
1420         }
1421         return AFPERR_EXIST;
1422     }
1423     
1424     /*
1425      * XXX if the source and the dest don't use the same resource type it's broken
1426      */
1427     if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1428         /* copy the data fork */
1429         if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1430             err = d_vol->vfs->vfs_copyfile(d_vol, src, dst);
1431         }
1432     }
1433
1434     if (err < 0) {
1435        ret_err = errno;
1436     }
1437
1438     if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1439         /* set the new name in the resource fork */
1440         ad_copy_header(&add, adp);
1441         ad_setname(&add, newname);
1442         ad_flush( &add );
1443     }
1444     ad_close( adp, adflags );
1445
1446     if (ad_close( &add, adflags ) <0) {
1447        ret_err = errno;
1448     } 
1449
1450     if (ret_err) {
1451         deletefile(d_vol, dst, 0);
1452     }
1453     else if (stat_result == 0) {
1454         /* set dest modification date to src date */
1455         struct utimbuf  ut;
1456
1457         ut.actime = ut.modtime = st.st_mtime;
1458         utime(dst, &ut);
1459         /* FIXME netatalk doesn't use resource fork file date
1460          * but maybe we should set its modtime too.
1461         */
1462     }
1463
1464 #ifdef DEBUG
1465     LOG(log_debug9, logtype_afpd, "end copyfile:");
1466 #endif /* DEBUG */
1467
1468 done:
1469     switch ( ret_err ) {
1470     case 0:
1471         return AFP_OK;
1472     case EDQUOT:
1473     case EFBIG:
1474     case ENOSPC:
1475         return AFPERR_DFULL;
1476     case ENOENT:
1477         return AFPERR_NOOBJ;
1478     case EACCES:
1479         return AFPERR_ACCESS;
1480     case EROFS:
1481         return AFPERR_VLOCK;
1482     }
1483     return AFPERR_PARAM;
1484 }
1485
1486
1487 /* -----------------------------------
1488    vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1489    checkAttrib:   1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1490
1491    when deletefile is called we don't have lock on it, file is closed (for us)
1492    untrue if called by renamefile
1493    
1494    ad_open always try to open file RDWR first and ad_lock takes care of
1495    WRITE lock on read only file.
1496 */
1497
1498 static int check_attrib(struct adouble *adp)
1499 {
1500 u_int16_t   bshort = 0;
1501
1502         ad_getattr(adp, &bshort);
1503     /*
1504      * Does kFPDeleteInhibitBit (bit 8) set?
1505      */
1506         if ((bshort & htons(ATTRBIT_NODELETE))) {
1507                 return AFPERR_OLOCK;
1508         }
1509     if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1510         return AFPERR_BUSY;
1511         }
1512         return 0;
1513 }
1514
1515 int deletefile(const struct vol *vol, char *file, int checkAttrib)
1516 {
1517     struct adouble      ad;
1518     struct adouble      *adp = &ad;
1519     int                 adflags, err = AFP_OK;
1520
1521 #ifdef DEBUG
1522     LOG(log_debug9, logtype_afpd, "begin deletefile:");
1523 #endif /* DEBUG */
1524
1525     /* try to open both forks at once */
1526     adflags = ADFLAGS_DF|ADFLAGS_HF;
1527     if (checkAttrib) {
1528         /* was EACCESS error try to get only metadata */
1529         ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1530         /* we never want to create a resource fork here, we are going to delete it 
1531          * moreover sometimes deletefile is called with a no existent file and 
1532          * ad_open would create a 0 byte resource fork
1533         */
1534         if ( ad_metadata( file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1535             ad_close( &ad, adflags );
1536             if ((err = check_attrib(&ad))) {
1537                return err;
1538             }
1539         }
1540     }
1541  
1542     while(1) {
1543         ad_init(&ad, vol->v_adouble, vol->v_ad_options);  /* OK */
1544         if ( ad_open( file, adflags, O_RDONLY, 0, &ad ) < 0 ) {
1545             switch (errno) {
1546             case ENOENT:
1547                 if (adflags == ADFLAGS_DF)
1548                     return AFPERR_NOOBJ;
1549                    
1550                 /* that failed. now try to open just the data fork */
1551                 adflags = ADFLAGS_DF;
1552                 continue;
1553
1554             case EACCES:
1555                 adp = NULL; /* maybe it's a file with no write mode for us */
1556                 break;      /* was return AFPERR_ACCESS;*/
1557             case EROFS:
1558                 return AFPERR_VLOCK;
1559             default:
1560                 return( AFPERR_PARAM );
1561             }
1562         }
1563         break;  /* from the while */
1564     }
1565
1566     if (adp && (adflags & ADFLAGS_HF) ) {
1567         /* FIXME we have a pb here because we want to know if a file is open 
1568          * there's a 'priority inversion' if you can't open the ressource fork RW
1569          * you can delete it if it's open because you can't get a write lock.
1570          * 
1571          * ADLOCK_FILELOCK means the whole ressource fork, not only after the 
1572          * metadatas
1573          *
1574          * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1575          */
1576         if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1577             ad_close( &ad, adflags );
1578             return( AFPERR_BUSY );
1579         }
1580     }
1581
1582     if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1583         err = AFPERR_BUSY;
1584     }
1585     else if (!(err = vol->vfs->vfs_deletefile(vol, file)) && !(err = netatalk_unlink( file )) ) {
1586         cnid_t id;
1587         if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) 
1588         {
1589             cnid_delete(vol->v_cdb, id);
1590         }
1591     }
1592     if (adp)
1593         ad_close( &ad, adflags );  /* ad_close removes locks if any */
1594
1595 #ifdef DEBUG
1596     LOG(log_debug9, logtype_afpd, "end deletefile:");
1597 #endif /* DEBUG */
1598
1599     return err;
1600 }
1601
1602 /* ------------------------------------ */
1603 /* return a file id */
1604 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1605 {
1606     struct stat         *st;
1607     struct vol          *vol;
1608     struct dir          *dir;
1609     char                *upath;
1610     int                 len;
1611     cnid_t              did, id;
1612     u_short             vid;
1613     struct path         *s_path;
1614
1615     *rbuflen = 0;
1616
1617     ibuf += 2;
1618
1619     memcpy(&vid, ibuf, sizeof(vid));
1620     ibuf += sizeof(vid);
1621
1622     if (NULL == ( vol = getvolbyvid( vid )) ) {
1623         return( AFPERR_PARAM);
1624     }
1625
1626     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1627         return AFPERR_NOOP;
1628     }
1629
1630     if (vol->v_flags & AFPVOL_RO)
1631         return AFPERR_VLOCK;
1632
1633     memcpy(&did, ibuf, sizeof( did ));
1634     ibuf += sizeof(did);
1635
1636     if (NULL == ( dir = dirlookup( vol, did )) ) {
1637         return afp_errno; /* was AFPERR_PARAM */
1638     }
1639
1640     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1641         return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1642     }
1643
1644     if ( path_isadir(s_path) ) {
1645         return( AFPERR_BADTYPE );
1646     }
1647
1648     upath = s_path->u_name;
1649     switch (s_path->st_errno) {
1650         case 0:
1651              break; /* success */
1652         case EPERM:
1653         case EACCES:
1654             return AFPERR_ACCESS;
1655         case ENOENT:
1656             return AFPERR_NOOBJ;
1657         default:
1658             return AFPERR_PARAM;
1659     }
1660     st = &s_path->st;
1661     if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1662         memcpy(rbuf, &id, sizeof(id));
1663         *rbuflen = sizeof(id);
1664         return AFPERR_EXISTID;
1665     }
1666
1667     if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1668         memcpy(rbuf, &id, sizeof(id));
1669         *rbuflen = sizeof(id);
1670         return AFP_OK;
1671     }
1672
1673     return afp_errno;
1674 }
1675
1676 /* ------------------------------- */
1677 struct reenum {
1678     struct vol *vol;
1679     cnid_t     did;
1680 };
1681
1682 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1683 {
1684     struct path   path;
1685     struct reenum *param = data;
1686     struct vol    *vol = param->vol;  
1687     cnid_t        did  = param->did;
1688     cnid_t        aint;
1689     
1690     if ( lstat(de->d_name, &path.st)<0 )
1691         return 0;
1692     
1693     /* update or add to cnid */
1694     aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1695
1696 #if AD_VERSION > AD_VERSION1
1697     if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1698         struct adouble  ad, *adp;
1699
1700         path.st_errno = 0;
1701         path.st_valid = 1;
1702         path.u_name = de->d_name;
1703             
1704         adp = of_ad(vol, &path, &ad);
1705             
1706         if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1707             return 0;
1708         }
1709         if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1710             ad_flush(adp);
1711         }
1712         ad_close_metadata(adp);
1713     }
1714 #endif /* AD_VERSION > AD_VERSION1 */
1715
1716     return 0;
1717 }
1718
1719 /* --------------------
1720  * Ok the db is out of synch with the dir.
1721  * but if it's a deleted file we don't want to do it again and again.
1722 */
1723 static int
1724 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1725 {
1726     int             ret;
1727     struct reenum   data;
1728     struct stat     st;
1729     
1730     if (vol->v_cdb == NULL) {
1731         return -1;
1732     }
1733     
1734     /* FIXME use of_statdir ? */
1735     if (lstat(name, &st)) {
1736         return -1;
1737     }
1738
1739     if (dirreenumerate(dir, &st)) {
1740         /* we already did it once and the dir haven't been modified */
1741         return dir->offcnt;
1742     }
1743     
1744     data.vol = vol;
1745     data.did = dir->d_did;
1746     if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1747         setdiroffcnt(curdir, &st,  ret);
1748         dir->d_flags |= DIRF_CNID;
1749     }
1750
1751     return ret;
1752 }
1753
1754 /* ------------------------------
1755    resolve a file id */
1756 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1757 {
1758     struct vol          *vol;
1759     struct dir          *dir;
1760     char                *upath;
1761     struct path         path;
1762     int                 err, retry=0;
1763     size_t              buflen;
1764     cnid_t              id, cnid;
1765     u_int16_t           vid, bitmap;
1766
1767     static char buffer[12 + MAXPATHLEN + 1];
1768     int len = 12 + MAXPATHLEN + 1;
1769
1770     *rbuflen = 0;
1771     ibuf += 2;
1772
1773     memcpy(&vid, ibuf, sizeof(vid));
1774     ibuf += sizeof(vid);
1775
1776     if (NULL == ( vol = getvolbyvid( vid )) ) {
1777         return( AFPERR_PARAM);
1778     }
1779
1780     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1781         return AFPERR_NOOP;
1782     }
1783
1784     memcpy(&id, ibuf, sizeof( id ));
1785     ibuf += sizeof(id);
1786     cnid = id;
1787     
1788     if (!id) {
1789         /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1790         return AFPERR_NOID;
1791     }
1792 retry:
1793     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1794         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1795     }
1796
1797     if (NULL == ( dir = dirlookup( vol, id )) ) {
1798         return AFPERR_NOID; /* idem AFPERR_PARAM */
1799     }
1800     if (movecwd(vol, dir) < 0) {
1801         switch (errno) {
1802         case EACCES:
1803         case EPERM:
1804             return AFPERR_ACCESS;
1805         case ENOENT:
1806             return AFPERR_NOID;
1807         default:
1808             return AFPERR_PARAM;
1809         }
1810     }
1811
1812     memset(&path, 0, sizeof(path));
1813     path.u_name = upath;
1814     if ( of_stat(&path) < 0 ) {
1815 #ifdef ESTALE
1816         /* with nfs and our working directory is deleted */
1817         if (errno == ESTALE) {
1818             errno = ENOENT;
1819         }
1820 #endif  
1821         if ( errno == ENOENT && !retry) {
1822             /* cnid db is out of sync, reenumerate the directory and update ids */
1823             reenumerate_id(vol, ".", dir);
1824             id = cnid;
1825             retry = 1;
1826             goto retry;
1827         }
1828         switch (errno) {
1829         case EACCES:
1830         case EPERM:
1831             return AFPERR_ACCESS;
1832         case ENOENT:
1833             return AFPERR_NOID;
1834         default:
1835             return AFPERR_PARAM;
1836         }
1837     }
1838
1839     /* directories are bad */
1840     if (S_ISDIR(path.st.st_mode)) {
1841         /* OS9 and OSX don't return the same error code  */
1842         return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1843     }
1844
1845     memcpy(&bitmap, ibuf, sizeof(bitmap));
1846     bitmap = ntohs( bitmap );
1847     if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1848         return AFPERR_NOID;
1849     }
1850     path.id = cnid;
1851     if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir, 
1852                             rbuf + sizeof(bitmap), &buflen))) {
1853         return err;
1854     }
1855     *rbuflen = buflen + sizeof(bitmap);
1856     memcpy(rbuf, ibuf, sizeof(bitmap));
1857
1858     return AFP_OK;
1859 }
1860
1861 /* ------------------------------ */
1862 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1863 {
1864     struct stat         st;
1865     struct vol          *vol;
1866     struct dir          *dir;
1867     char                *upath;
1868     int                 err;
1869     cnid_t              id;
1870     cnid_t              fileid;
1871     u_short             vid;
1872     static char buffer[12 + MAXPATHLEN + 1];
1873     int len = 12 + MAXPATHLEN + 1;
1874
1875     *rbuflen = 0;
1876     ibuf += 2;
1877
1878     memcpy(&vid, ibuf, sizeof(vid));
1879     ibuf += sizeof(vid);
1880
1881     if (NULL == ( vol = getvolbyvid( vid )) ) {
1882         return( AFPERR_PARAM);
1883     }
1884
1885     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1886         return AFPERR_NOOP;
1887     }
1888
1889     if (vol->v_flags & AFPVOL_RO)
1890         return AFPERR_VLOCK;
1891
1892     memcpy(&id, ibuf, sizeof( id ));
1893     ibuf += sizeof(id);
1894     fileid = id;
1895
1896     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1897         return AFPERR_NOID;
1898     }
1899
1900     if (NULL == ( dir = dirlookup( vol, id )) ) {
1901         return( AFPERR_PARAM );
1902     }
1903
1904     err = AFP_OK;
1905     if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1906         switch (errno) {
1907         case EACCES:
1908         case EPERM:
1909             return AFPERR_ACCESS;
1910 #ifdef ESTALE
1911         case ESTALE:
1912 #endif  
1913         case ENOENT:
1914             /* still try to delete the id */
1915             err = AFPERR_NOOBJ;
1916             break;
1917         default:
1918             return AFPERR_PARAM;
1919         }
1920     }
1921     else if (S_ISDIR(st.st_mode)) /* directories are bad */
1922         return AFPERR_BADTYPE;
1923
1924     if (cnid_delete(vol->v_cdb, fileid)) {
1925         switch (errno) {
1926         case EROFS:
1927             return AFPERR_VLOCK;
1928         case EPERM:
1929         case EACCES:
1930             return AFPERR_ACCESS;
1931         default:
1932             return AFPERR_PARAM;
1933         }
1934     }
1935
1936     return err;
1937 }
1938
1939 /* ------------------------------ */
1940 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1941 {
1942     int             ret;
1943
1944     if (path->st_errno) {
1945         switch (path->st_errno) {
1946         case ENOENT:
1947             afp_errno = AFPERR_NOID;
1948             break;
1949         case EPERM:
1950         case EACCES:
1951             afp_errno = AFPERR_ACCESS;
1952             break;
1953         default:
1954             afp_errno = AFPERR_PARAM;
1955             break;
1956         }
1957         return NULL;
1958     }
1959     /* we use file_access both for legacy Mac perm and
1960      * for unix privilege, rename will take care of folder perms
1961     */
1962     if (file_access(path, OPENACC_WR ) < 0) {
1963         afp_errno = AFPERR_ACCESS;
1964         return NULL;
1965     }
1966     
1967     if ((*of = of_findname(path))) {
1968         /* reuse struct adouble so it won't break locks */
1969         adp = (*of)->of_ad;
1970     }
1971     else {
1972         ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
1973         /* META and HF */
1974         if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1975             /* from AFP spec.
1976              * The user must have the Read & Write privilege for both files in order to use this command.
1977              */
1978             ad_close(adp, ADFLAGS_HF);
1979             afp_errno = AFPERR_ACCESS;
1980             return NULL;
1981         }
1982     }
1983     return adp;
1984 }
1985
1986 #define APPLETEMP ".AppleTempXXXXXX"
1987
1988 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1989 {
1990     struct stat         srcst, destst;
1991     struct vol          *vol;
1992     struct dir          *dir, *sdir;
1993     char                *spath, temp[17], *p;
1994     char                *supath, *upath;
1995     struct path         *path;
1996     int                 err;
1997     struct adouble      ads;
1998     struct adouble      add;
1999     struct adouble      *adsp = NULL;
2000     struct adouble      *addp = NULL;
2001     struct ofork        *s_of = NULL;
2002     struct ofork        *d_of = NULL;
2003     int                 crossdev;
2004     
2005     int                 slen, dlen;
2006     u_int32_t           sid, did;
2007     u_int16_t           vid;
2008
2009     uid_t              uid;
2010     gid_t              gid;
2011
2012     *rbuflen = 0;
2013     ibuf += 2;
2014
2015     memcpy(&vid, ibuf, sizeof(vid));
2016     ibuf += sizeof(vid);
2017
2018     if (NULL == ( vol = getvolbyvid( vid )) ) {
2019         return( AFPERR_PARAM);
2020     }
2021
2022     if ((vol->v_flags & AFPVOL_RO))
2023         return AFPERR_VLOCK;
2024
2025     /* source and destination dids */
2026     memcpy(&sid, ibuf, sizeof(sid));
2027     ibuf += sizeof(sid);
2028     memcpy(&did, ibuf, sizeof(did));
2029     ibuf += sizeof(did);
2030
2031     /* source file */
2032     if (NULL == (dir = dirlookup( vol, sid )) ) {
2033         return afp_errno; /* was AFPERR_PARAM */
2034     }
2035
2036     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2037         return get_afp_errno(AFPERR_NOOBJ); 
2038     }
2039
2040     if ( path_isadir(path) ) {
2041         return AFPERR_BADTYPE;   /* it's a dir */
2042     }
2043
2044     /* save some stuff */
2045     srcst = path->st;
2046     sdir = curdir;
2047     spath = obj->oldtmp;
2048     supath = obj->newtmp;
2049     strcpy(spath, path->m_name);
2050     strcpy(supath, path->u_name); /* this is for the cnid changing */
2051     p = absupath( vol, sdir, supath);
2052     if (!p) {
2053         /* pathname too long */
2054         return AFPERR_PARAM ;
2055     }
2056     
2057     ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2058     if (!(adsp = find_adouble( path, &s_of, &ads))) {
2059         return afp_errno;
2060     }
2061
2062     /* ***** from here we may have resource fork open **** */
2063     
2064     /* look for the source cnid. if it doesn't exist, don't worry about
2065      * it. */
2066     sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2067
2068     if (NULL == ( dir = dirlookup( vol, did )) ) {
2069         err = afp_errno; /* was AFPERR_PARAM */
2070         goto err_exchangefile;
2071     }
2072
2073     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2074         err = get_afp_errno(AFPERR_NOOBJ); 
2075         goto err_exchangefile;
2076     }
2077
2078     if ( path_isadir(path) ) {
2079         err = AFPERR_BADTYPE;
2080         goto err_exchangefile;
2081     }
2082
2083     /* FPExchangeFiles is the only call that can return the SameObj
2084      * error */
2085     if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2086         err = AFPERR_SAMEOBJ;
2087         goto err_exchangefile;
2088     }
2089
2090     ad_init(&add, vol->v_adouble, vol->v_ad_options);
2091     if (!(addp = find_adouble( path, &d_of, &add))) {
2092         err = afp_errno;
2093         goto err_exchangefile;
2094     }
2095     destst = path->st;
2096
2097     /* they are not on the same device and at least one is open
2098      * FIXME broken for for crossdev and adouble v2
2099      * return an error 
2100     */
2101     crossdev = (srcst.st_dev != destst.st_dev);
2102     if (/* (d_of || s_of)  && */ crossdev) {
2103         err = AFPERR_MISC;
2104         goto err_exchangefile;
2105     }
2106
2107     /* look for destination id. */
2108     upath = path->u_name;
2109     did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2110
2111     /* construct a temp name.
2112      * NOTE: the temp file will be in the dest file's directory. it
2113      * will also be inaccessible from AFP. */
2114     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2115     if (!mktemp(temp)) {
2116         err = AFPERR_MISC;
2117         goto err_exchangefile;
2118     }
2119     
2120     if (crossdev) {
2121         /* FIXME we need to close fork for copy, both s_of and d_of are null */
2122        ad_close(adsp, ADFLAGS_HF);
2123        ad_close(addp, ADFLAGS_HF);
2124     }
2125
2126     /* now, quickly rename the file. we error if we can't. */
2127     if ((err = renamefile(vol, p, temp, temp, adsp)) != AFP_OK)
2128         goto err_exchangefile;
2129     of_rename(vol, s_of, sdir, spath, curdir, temp);
2130
2131     /* rename destination to source */
2132     if ((err = renamefile(vol, upath, p, spath, addp)) != AFP_OK)
2133         goto err_src_to_tmp;
2134     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2135
2136     /* rename temp to destination */
2137     if ((err = renamefile(vol, temp, upath, path->m_name, adsp)) != AFP_OK)
2138         goto err_dest_to_src;
2139     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2140
2141     /* id's need switching. src -> dest and dest -> src. 
2142      * we need to re-stat() if it was a cross device copy.
2143     */
2144     if (sid) {
2145         cnid_delete(vol->v_cdb, sid);
2146     }
2147     if (did) {
2148         cnid_delete(vol->v_cdb, did);
2149     }
2150     if ((did && ( (crossdev && lstat( upath, &srcst) < 0) || 
2151                 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2152        ||
2153        (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2154                 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2155     ) {
2156         switch (errno) {
2157         case EPERM:
2158         case EACCES:
2159             err = AFPERR_ACCESS;
2160             break;
2161         default:
2162             err = AFPERR_PARAM;
2163         }
2164         goto err_temp_to_dest;
2165     }
2166     
2167     /* here we need to reopen if crossdev */
2168     if (sid && ad_setid(addp, destst.st_dev, destst.st_ino,  sid, sdir->d_did, vol->v_stamp)) 
2169     {
2170        ad_flush( addp );
2171     }
2172         
2173     if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino,  did, curdir->d_did, vol->v_stamp)) 
2174     {
2175        ad_flush( adsp );
2176     }
2177
2178     /* change perms, src gets dest perm and vice versa */
2179
2180     uid = geteuid();
2181     gid = getegid();
2182     if (seteuid(0)) {
2183         LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2184         err = AFP_OK; /* ignore error */
2185         goto err_temp_to_dest;
2186     }
2187
2188     /*
2189      * we need to exchange ACL entries as well
2190      */
2191     /* exchange_acls(vol, p, upath); */
2192
2193     path->st = srcst;
2194     path->st_valid = 1;
2195     path->st_errno = 0;
2196     path->m_name = NULL;
2197     path->u_name = upath;
2198
2199     setfilunixmode(vol, path, destst.st_mode);
2200     setfilowner(vol, destst.st_uid, destst.st_gid, path);
2201
2202     path->st = destst;
2203     path->st_valid = 1;
2204     path->st_errno = 0;
2205     path->u_name = p;
2206
2207     setfilunixmode(vol, path, srcst.st_mode);
2208     setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2209
2210     if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2211         LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2212         exit(EXITERR_SYS);
2213     }
2214
2215     err = AFP_OK;
2216     goto err_exchangefile;
2217
2218     /* all this stuff is so that we can unwind a failed operation
2219      * properly. */
2220 err_temp_to_dest:
2221     /* rename dest to temp */
2222     renamefile(vol, upath, temp, temp, adsp);
2223     of_rename(vol, s_of, curdir, upath, curdir, temp);
2224
2225 err_dest_to_src:
2226     /* rename source back to dest */
2227     renamefile(vol, p, upath, path->m_name, addp);
2228     of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2229
2230 err_src_to_tmp:
2231     /* rename temp back to source */
2232     renamefile(vol, temp, p, spath, adsp);
2233     of_rename(vol, s_of, curdir, temp, sdir, spath);
2234
2235 err_exchangefile:
2236     if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2237        ad_close(adsp, ADFLAGS_HF);
2238     }
2239     if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2240        ad_close(addp, ADFLAGS_HF);
2241     }
2242
2243     return err;
2244 }