]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/file.c
Implement VFS chaining, EA VFS rm/mv
[netatalk.git] / etc / afpd / file.c
1 /*
2  * $Id: file.c,v 1.113 2009-10-14 15:04:00 franklahm Exp $
3  *
4  * Copyright (c) 1990,1993 Regents of The University of Michigan.
5  * All Rights Reserved.  See COPYRIGHT.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif /* HAVE_CONFIG_H */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14
15 /* STDC check */
16 #if STDC_HEADERS
17 #include <string.h>
18 #else /* STDC_HEADERS */
19 #ifndef HAVE_STRCHR
20 #define strchr index
21 #define strrchr index
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
24
25 #ifndef HAVE_MEMCPY
26 #define memcpy(d,s,n) bcopy ((s), (d), (n))
27 #define memmove(d,s,n) bcopy ((s), (d), (n))
28 #endif /* ! HAVE_MEMCPY */
29 #endif /* STDC_HEADERS */
30
31 #include <utime.h>
32 #include <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, int *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, int *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, int ibuflen _U_, char *rbuf _U_, int *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, int ibuflen _U_, char *rbuf _U_, int *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, int ibuflen _U_, char *rbuf _U_, int *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, int ibuflen _U_, char *rbuf, int *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, int ibuflen _U_, char *rbuf, int *rbuflen)
1758 {
1759     struct vol          *vol;
1760     struct dir          *dir;
1761     char                *upath;
1762     struct path         path;
1763     int                 err, buflen, retry=0;
1764     cnid_t              id, cnid;
1765     u_int16_t           vid, bitmap;
1766
1767     static char buffer[12 + MAXPATHLEN + 1];
1768     int len = 12 + MAXPATHLEN + 1;
1769
1770 #ifdef DEBUG
1771     LOG(log_info, logtype_afpd, "begin afp_resolveid:");
1772 #endif /* DEBUG */
1773
1774     *rbuflen = 0;
1775     ibuf += 2;
1776
1777     memcpy(&vid, ibuf, sizeof(vid));
1778     ibuf += sizeof(vid);
1779
1780     if (NULL == ( vol = getvolbyvid( vid )) ) {
1781         return( AFPERR_PARAM);
1782     }
1783
1784     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1785         return AFPERR_NOOP;
1786     }
1787
1788     memcpy(&id, ibuf, sizeof( id ));
1789     ibuf += sizeof(id);
1790     cnid = id;
1791     
1792     if (!id) {
1793         /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1794         return AFPERR_NOID;
1795     }
1796 retry:
1797     memset(&path, 0, sizeof(path));
1798     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1799         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1800     }
1801
1802     if (NULL == ( dir = dirlookup( vol, id )) ) {
1803         return AFPERR_NOID; /* idem AFPERR_PARAM */
1804     }
1805     path.u_name = upath;
1806     if (movecwd(vol, dir) < 0) {
1807         switch (errno) {
1808         case EACCES:
1809         case EPERM:
1810             return AFPERR_ACCESS;
1811         case ENOENT:
1812             return AFPERR_NOID;
1813         default:
1814             return AFPERR_PARAM;
1815         }
1816     }
1817
1818     if ( of_stat(&path) < 0 ) {
1819 #ifdef ESTALE
1820         /* with nfs and our working directory is deleted */
1821         if (errno == ESTALE) {
1822             errno = ENOENT;
1823         }
1824 #endif  
1825         if ( errno == ENOENT && !retry) {
1826             /* cnid db is out of sync, reenumerate the directory and update ids */
1827             reenumerate_id(vol, ".", dir);
1828             id = cnid;
1829             retry = 1;
1830             goto retry;
1831         }
1832         switch (errno) {
1833         case EACCES:
1834         case EPERM:
1835             return AFPERR_ACCESS;
1836         case ENOENT:
1837             return AFPERR_NOID;
1838         default:
1839             return AFPERR_PARAM;
1840         }
1841     }
1842
1843     /* directories are bad */
1844     if (S_ISDIR(path.st.st_mode)) {
1845         /* OS9 and OSX don't return the same error code  */
1846         return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1847     }
1848
1849     memcpy(&bitmap, ibuf, sizeof(bitmap));
1850     bitmap = ntohs( bitmap );
1851     if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1852         return AFPERR_NOID;
1853     }
1854     path.id = cnid;
1855     if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir, 
1856                             rbuf + sizeof(bitmap), &buflen))) {
1857         return err;
1858     }
1859     *rbuflen = buflen + sizeof(bitmap);
1860     memcpy(rbuf, ibuf, sizeof(bitmap));
1861
1862 #ifdef DEBUG
1863     LOG(log_info, logtype_afpd, "end afp_resolveid:");
1864 #endif /* DEBUG */
1865
1866     return AFP_OK;
1867 }
1868
1869 /* ------------------------------ */
1870 int afp_deleteid(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf _U_, int *rbuflen)
1871 {
1872     struct stat         st;
1873     struct vol          *vol;
1874     struct dir          *dir;
1875     char                *upath;
1876     int                 err;
1877     cnid_t              id;
1878     cnid_t              fileid;
1879     u_short             vid;
1880     static char buffer[12 + MAXPATHLEN + 1];
1881     int len = 12 + MAXPATHLEN + 1;
1882
1883 #ifdef DEBUG
1884     LOG(log_info, logtype_afpd, "begin afp_deleteid:");
1885 #endif /* DEBUG */
1886
1887     *rbuflen = 0;
1888     ibuf += 2;
1889
1890     memcpy(&vid, ibuf, sizeof(vid));
1891     ibuf += sizeof(vid);
1892
1893     if (NULL == ( vol = getvolbyvid( vid )) ) {
1894         return( AFPERR_PARAM);
1895     }
1896
1897     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1898         return AFPERR_NOOP;
1899     }
1900
1901     if (vol->v_flags & AFPVOL_RO)
1902         return AFPERR_VLOCK;
1903
1904     memcpy(&id, ibuf, sizeof( id ));
1905     ibuf += sizeof(id);
1906     fileid = id;
1907
1908     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1909         return AFPERR_NOID;
1910     }
1911
1912     if (NULL == ( dir = dirlookup( vol, id )) ) {
1913         return( AFPERR_PARAM );
1914     }
1915
1916     err = AFP_OK;
1917     if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) {
1918         switch (errno) {
1919         case EACCES:
1920         case EPERM:
1921             return AFPERR_ACCESS;
1922 #ifdef ESTALE
1923         case ESTALE:
1924 #endif  
1925         case ENOENT:
1926             /* still try to delete the id */
1927             err = AFPERR_NOOBJ;
1928             break;
1929         default:
1930             return AFPERR_PARAM;
1931         }
1932     }
1933     else if (S_ISDIR(st.st_mode)) /* directories are bad */
1934         return AFPERR_BADTYPE;
1935
1936     if (cnid_delete(vol->v_cdb, fileid)) {
1937         switch (errno) {
1938         case EROFS:
1939             return AFPERR_VLOCK;
1940         case EPERM:
1941         case EACCES:
1942             return AFPERR_ACCESS;
1943         default:
1944             return AFPERR_PARAM;
1945         }
1946     }
1947
1948 #ifdef DEBUG
1949     LOG(log_info, logtype_afpd, "end afp_deleteid:");
1950 #endif /* DEBUG */
1951
1952     return err;
1953 }
1954
1955 /* ------------------------------ */
1956 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1957 {
1958     int             ret;
1959
1960     if (path->st_errno) {
1961         switch (path->st_errno) {
1962         case ENOENT:
1963             afp_errno = AFPERR_NOID;
1964             break;
1965         case EPERM:
1966         case EACCES:
1967             afp_errno = AFPERR_ACCESS;
1968             break;
1969         default:
1970             afp_errno = AFPERR_PARAM;
1971             break;
1972         }
1973         return NULL;
1974     }
1975     /* we use file_access both for legacy Mac perm and
1976      * for unix privilege, rename will take care of folder perms
1977     */
1978     if (file_access(path, OPENACC_WR ) < 0) {
1979         afp_errno = AFPERR_ACCESS;
1980         return NULL;
1981     }
1982     
1983     if ((*of = of_findname(path))) {
1984         /* reuse struct adouble so it won't break locks */
1985         adp = (*of)->of_ad;
1986     }
1987     else {
1988         ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
1989         /* META and HF */
1990         if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1991             /* from AFP spec.
1992              * The user must have the Read & Write privilege for both files in order to use this command.
1993              */
1994             ad_close(adp, ADFLAGS_HF);
1995             afp_errno = AFPERR_ACCESS;
1996             return NULL;
1997         }
1998     }
1999     return adp;
2000 }
2001
2002 #define APPLETEMP ".AppleTempXXXXXX"
2003
2004 int afp_exchangefiles(AFPObj *obj, char *ibuf, int ibuflen _U_, char *rbuf _U_, int *rbuflen)
2005 {
2006     struct stat         srcst, destst;
2007     struct vol          *vol;
2008     struct dir          *dir, *sdir;
2009     char                *spath, temp[17], *p;
2010     char                *supath, *upath;
2011     struct path         *path;
2012     int                 err;
2013     struct adouble      ads;
2014     struct adouble      add;
2015     struct adouble      *adsp = NULL;
2016     struct adouble      *addp = NULL;
2017     struct ofork        *s_of = NULL;
2018     struct ofork        *d_of = NULL;
2019     int                 crossdev;
2020     
2021     int                 slen, dlen;
2022     u_int32_t           sid, did;
2023     u_int16_t           vid;
2024
2025     uid_t              uid;
2026     gid_t              gid;
2027
2028 #ifdef DEBUG
2029     LOG(log_info, logtype_afpd, "begin afp_exchangefiles:");
2030 #endif /* DEBUG */
2031
2032     *rbuflen = 0;
2033     ibuf += 2;
2034
2035     memcpy(&vid, ibuf, sizeof(vid));
2036     ibuf += sizeof(vid);
2037
2038     if (NULL == ( vol = getvolbyvid( vid )) ) {
2039         return( AFPERR_PARAM);
2040     }
2041
2042     if ((vol->v_flags & AFPVOL_RO))
2043         return AFPERR_VLOCK;
2044
2045     /* source and destination dids */
2046     memcpy(&sid, ibuf, sizeof(sid));
2047     ibuf += sizeof(sid);
2048     memcpy(&did, ibuf, sizeof(did));
2049     ibuf += sizeof(did);
2050
2051     /* source file */
2052     if (NULL == (dir = dirlookup( vol, sid )) ) {
2053         return afp_errno; /* was AFPERR_PARAM */
2054     }
2055
2056     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2057         return get_afp_errno(AFPERR_NOOBJ); 
2058     }
2059
2060     if ( path_isadir(path) ) {
2061         return AFPERR_BADTYPE;   /* it's a dir */
2062     }
2063
2064     /* save some stuff */
2065     srcst = path->st;
2066     sdir = curdir;
2067     spath = obj->oldtmp;
2068     supath = obj->newtmp;
2069     strcpy(spath, path->m_name);
2070     strcpy(supath, path->u_name); /* this is for the cnid changing */
2071     p = absupath( vol, sdir, supath);
2072     if (!p) {
2073         /* pathname too long */
2074         return AFPERR_PARAM ;
2075     }
2076     
2077     ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2078     if (!(adsp = find_adouble( path, &s_of, &ads))) {
2079         return afp_errno;
2080     }
2081
2082     /* ***** from here we may have resource fork open **** */
2083     
2084     /* look for the source cnid. if it doesn't exist, don't worry about
2085      * it. */
2086     sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2087
2088     if (NULL == ( dir = dirlookup( vol, did )) ) {
2089         err = afp_errno; /* was AFPERR_PARAM */
2090         goto err_exchangefile;
2091     }
2092
2093     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2094         err = get_afp_errno(AFPERR_NOOBJ); 
2095         goto err_exchangefile;
2096     }
2097
2098     if ( path_isadir(path) ) {
2099         err = AFPERR_BADTYPE;
2100         goto err_exchangefile;
2101     }
2102
2103     /* FPExchangeFiles is the only call that can return the SameObj
2104      * error */
2105     if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2106         err = AFPERR_SAMEOBJ;
2107         goto err_exchangefile;
2108     }
2109
2110     ad_init(&add, vol->v_adouble, vol->v_ad_options);
2111     if (!(addp = find_adouble( path, &d_of, &add))) {
2112         err = afp_errno;
2113         goto err_exchangefile;
2114     }
2115     destst = path->st;
2116
2117     /* they are not on the same device and at least one is open
2118      * FIXME broken for for crossdev and adouble v2
2119      * return an error 
2120     */
2121     crossdev = (srcst.st_dev != destst.st_dev);
2122     if (/* (d_of || s_of)  && */ crossdev) {
2123         err = AFPERR_MISC;
2124         goto err_exchangefile;
2125     }
2126
2127     /* look for destination id. */
2128     upath = path->u_name;
2129     did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2130
2131     /* construct a temp name.
2132      * NOTE: the temp file will be in the dest file's directory. it
2133      * will also be inaccessible from AFP. */
2134     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2135     if (!mktemp(temp)) {
2136         err = AFPERR_MISC;
2137         goto err_exchangefile;
2138     }
2139     
2140     if (crossdev) {
2141         /* FIXME we need to close fork for copy, both s_of and d_of are null */
2142        ad_close(adsp, ADFLAGS_HF);
2143        ad_close(addp, ADFLAGS_HF);
2144     }
2145
2146     /* now, quickly rename the file. we error if we can't. */
2147     if ((err = renamefile(vol, p, temp, temp, adsp)) != AFP_OK)
2148         goto err_exchangefile;
2149     of_rename(vol, s_of, sdir, spath, curdir, temp);
2150
2151     /* rename destination to source */
2152     if ((err = renamefile(vol, upath, p, spath, addp)) != AFP_OK)
2153         goto err_src_to_tmp;
2154     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2155
2156     /* rename temp to destination */
2157     if ((err = renamefile(vol, temp, upath, path->m_name, adsp)) != AFP_OK)
2158         goto err_dest_to_src;
2159     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2160
2161     /* id's need switching. src -> dest and dest -> src. 
2162      * we need to re-stat() if it was a cross device copy.
2163     */
2164     if (sid) {
2165         cnid_delete(vol->v_cdb, sid);
2166     }
2167     if (did) {
2168         cnid_delete(vol->v_cdb, did);
2169     }
2170     if ((did && ( (crossdev && stat( upath, &srcst) < 0) || 
2171                 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2172        ||
2173        (sid && ( (crossdev && stat(p, &destst) < 0) ||
2174                 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2175     ) {
2176         switch (errno) {
2177         case EPERM:
2178         case EACCES:
2179             err = AFPERR_ACCESS;
2180             break;
2181         default:
2182             err = AFPERR_PARAM;
2183         }
2184         goto err_temp_to_dest;
2185     }
2186     
2187     /* here we need to reopen if crossdev */
2188     if (sid && ad_setid(addp, destst.st_dev, destst.st_ino,  sid, sdir->d_did, vol->v_stamp)) 
2189     {
2190        ad_flush( addp );
2191     }
2192         
2193     if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino,  did, curdir->d_did, vol->v_stamp)) 
2194     {
2195        ad_flush( adsp );
2196     }
2197
2198     /* change perms, src gets dest perm and vice versa */
2199
2200     uid = geteuid();
2201     gid = getegid();
2202     if (seteuid(0)) {
2203         LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2204         err = AFP_OK; /* ignore error */
2205         goto err_temp_to_dest;
2206     }
2207
2208     /*
2209      * we need to exchange ACL entries as well
2210      */
2211     /* exchange_acls(vol, p, upath); */
2212
2213     path->st = srcst;
2214     path->st_valid = 1;
2215     path->st_errno = 0;
2216     path->m_name = NULL;
2217     path->u_name = upath;
2218
2219     setfilunixmode(vol, path, destst.st_mode);
2220     setfilowner(vol, destst.st_uid, destst.st_gid, path);
2221
2222     path->st = destst;
2223     path->st_valid = 1;
2224     path->st_errno = 0;
2225     path->u_name = p;
2226
2227     setfilunixmode(vol, path, srcst.st_mode);
2228     setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2229
2230     if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2231         LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2232         exit(EXITERR_SYS);
2233     }
2234
2235 #ifdef DEBUG
2236     LOG(log_info, logtype_afpd, "ending afp_exchangefiles:");
2237 #endif /* DEBUG */
2238
2239     err = AFP_OK;
2240     goto err_exchangefile;
2241
2242     /* all this stuff is so that we can unwind a failed operation
2243      * properly. */
2244 err_temp_to_dest:
2245     /* rename dest to temp */
2246     renamefile(vol, upath, temp, temp, adsp);
2247     of_rename(vol, s_of, curdir, upath, curdir, temp);
2248
2249 err_dest_to_src:
2250     /* rename source back to dest */
2251     renamefile(vol, p, upath, path->m_name, addp);
2252     of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2253
2254 err_src_to_tmp:
2255     /* rename temp back to source */
2256     renamefile(vol, temp, p, spath, adsp);
2257     of_rename(vol, s_of, curdir, temp, sdir, spath);
2258
2259 err_exchangefile:
2260     if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2261        ad_close(adsp, ADFLAGS_HF);
2262     }
2263     if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2264        ad_close(addp, ADFLAGS_HF);
2265     }
2266
2267     return err;
2268 }