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