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