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