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