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