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