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