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