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