]> 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     if (ad_open(adp, dst, ADFLAGS_HF, O_RDWR) == 0) {
1126         ad_setname(adp, newname);
1127         ad_flush( adp );
1128         ad_close( adp, ADFLAGS_HF );
1129     }
1130
1131     return( AFP_OK );
1132 }
1133
1134 /* ---------------- 
1135    convert a Mac long name to an utf8 name,
1136 */
1137 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1138 {
1139 size_t    outlen;
1140
1141     if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1142         return -1;
1143     }
1144     return outlen;
1145 }
1146
1147 /* ---------------- */
1148 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1149 {
1150 char        type = *ibuf;
1151 size_t      plen = 0;
1152 u_int16_t   len16;
1153 u_int32_t   hint;
1154
1155     if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1156         return -1;
1157     }
1158     ibuf++;
1159     switch (type) {
1160     case 2:
1161         if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1162             if (afp_version >= 30) {
1163                 /* convert it to UTF8 
1164                 */
1165                 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1166                    return -1;
1167             }
1168             else {
1169                 strncpy( newname, ibuf, plen );
1170                 newname[ plen ] = '\0';
1171             }
1172             if (strlen(newname) != plen) {
1173                 /* there's \0 in newname, e.g. it's a pathname not
1174                  * only a filename. 
1175                 */
1176                 return -1;
1177             }
1178         }
1179         break;
1180     case 3:
1181         memcpy(&hint, ibuf, sizeof(hint));
1182         ibuf += sizeof(hint);
1183            
1184         memcpy(&len16, ibuf, sizeof(len16));
1185         ibuf += sizeof(len16);
1186         plen = ntohs(len16);
1187         
1188         if (plen) {
1189             if (plen > AFPOBJ_TMPSIZ) {
1190                 return -1;
1191             }
1192             strncpy( newname, ibuf, plen );
1193             newname[ plen ] = '\0';
1194             if (strlen(newname) != plen) {
1195                 return -1;
1196             }
1197         }
1198         break;
1199     }
1200     return plen;
1201 }
1202
1203 /* -----------------------------------
1204 */
1205 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1206 {
1207     struct vol  *s_vol, *d_vol;
1208     struct dir  *dir;
1209     char        *newname, *p, *upath;
1210     struct path *s_path;
1211     u_int32_t   sdid, ddid;
1212     int         err, retvalue = AFP_OK;
1213     u_int16_t   svid, dvid;
1214
1215     struct adouble ad, *adp;
1216     int denyreadset;
1217     
1218     *rbuflen = 0;
1219     ibuf += 2;
1220
1221     memcpy(&svid, ibuf, sizeof( svid ));
1222     ibuf += sizeof( svid );
1223     if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1224         return( AFPERR_PARAM );
1225     }
1226
1227     memcpy(&sdid, ibuf, sizeof( sdid ));
1228     ibuf += sizeof( sdid );
1229     if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1230         return afp_errno;
1231     }
1232
1233     memcpy(&dvid, ibuf, sizeof( dvid ));
1234     ibuf += sizeof( dvid );
1235     memcpy(&ddid, ibuf, sizeof( ddid ));
1236     ibuf += sizeof( ddid );
1237
1238     if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1239         return get_afp_errno(AFPERR_PARAM);
1240     }
1241     if ( path_isadir(s_path) ) {
1242         return( AFPERR_BADTYPE );
1243     }
1244
1245     /* don't allow copies when the file is open.
1246      * XXX: the spec only calls for read/deny write access.
1247      *      however, copyfile doesn't have any of that info,
1248      *      and locks need to stay coherent. as a result,
1249      *      we just balk if the file is opened already. */
1250
1251     adp = of_ad(s_vol, s_path, &ad);
1252
1253     if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0) {
1254         return AFPERR_DENYCONF;
1255     }
1256     denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 || 
1257                   ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1258
1259     if (denyreadset) {
1260         retvalue = AFPERR_DENYCONF;
1261         goto copy_exit;
1262     }
1263
1264     newname = obj->newtmp;
1265     strcpy( newname, s_path->m_name );
1266
1267     p = ctoupath( s_vol, curdir, newname );
1268     if (!p) {
1269         retvalue = AFPERR_PARAM;
1270         goto copy_exit;
1271     }
1272
1273 #ifdef FORCE_UIDGID
1274     /* FIXME svid != dvid && dvid's user can't read svid */
1275 #endif
1276     if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1277         retvalue = AFPERR_PARAM;
1278         goto copy_exit;
1279     }
1280
1281     if (d_vol->v_flags & AFPVOL_RO) {
1282         retvalue = AFPERR_VLOCK;
1283         goto copy_exit;
1284     }
1285
1286     if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1287         retvalue = afp_errno;
1288         goto copy_exit;
1289     }
1290
1291     if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1292         retvalue = get_afp_errno(AFPERR_NOOBJ);
1293         goto copy_exit;
1294     }
1295     
1296     if ( *s_path->m_name != '\0' ) {
1297         retvalue =path_error(s_path, AFPERR_NOOBJ);
1298         goto copy_exit;
1299     }
1300
1301     /* one of the handful of places that knows about the path type */
1302     if (copy_path_name(d_vol, newname, ibuf) < 0) {
1303         retvalue = AFPERR_PARAM;
1304         goto copy_exit;
1305     }
1306     /* newname is always only a filename so curdir *is* its
1307      * parent folder
1308     */
1309     if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1310         retvalue =AFPERR_PARAM;
1311         goto copy_exit;
1312     }
1313
1314     if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1315         retvalue = err;
1316         goto copy_exit;
1317     }
1318     curdir->offcnt++;
1319
1320 #ifdef DROPKLUDGE
1321     if (vol->v_flags & AFPVOL_DROPBOX) {
1322         retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1323     }
1324 #endif /* DROPKLUDGE */
1325
1326     setvoltime(obj, d_vol );
1327
1328 copy_exit:
1329     ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1330     return( retvalue );
1331 }
1332
1333 /* ----------------------- */
1334 static int copy_all(const int dfd, const void *buf,
1335                                size_t buflen)
1336 {
1337     ssize_t cc;
1338
1339 #ifdef DEBUG
1340     LOG(log_debug9, logtype_afpd, "begin copy_all:");
1341 #endif /* DEBUG */
1342
1343     while (buflen > 0) {
1344         if ((cc = write(dfd, buf, buflen)) < 0) {
1345             switch (errno) {
1346             case EINTR:
1347                 continue;
1348             default:
1349                 return -1;
1350             }
1351         }
1352         buflen -= cc;
1353     }
1354
1355 #ifdef DEBUG
1356     LOG(log_debug9, logtype_afpd, "end copy_all:");
1357 #endif /* DEBUG */
1358
1359     return 0;
1360 }
1361
1362 /* -------------------------- 
1363  * copy only the fork data stream
1364 */
1365 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1366 {
1367     ssize_t cc;
1368     int     err = 0;
1369     char    filebuf[8192];
1370     int     sfd, dfd;
1371     
1372     if (eid == ADEID_DFORK) {
1373         sfd = ad_data_fileno(ads);
1374         dfd = ad_data_fileno(add);
1375     }
1376     else {
1377         sfd = ad_reso_fileno(ads);
1378         dfd = ad_reso_fileno(add);
1379     }        
1380
1381     if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1382         return -1;
1383
1384     if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1385         return -1;
1386         
1387 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1388     /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1389     off_t   offset = 0;
1390     size_t  size;
1391     struct stat         st;
1392     #define BUF 128*1024*1024
1393
1394     if (fstat(sfd, &st) == 0) {
1395         
1396         while (1) {
1397             if ( offset >= st.st_size) {
1398                return 0;
1399             }
1400             size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1401             if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1402                 switch (errno) {
1403                 case ENOSYS:
1404                 case EINVAL:  /* there's no guarantee that all fs support sendfile */
1405                     goto no_sendfile;
1406                 default:
1407                     return -1;
1408                 }
1409             }
1410         }
1411     }
1412     no_sendfile:
1413     lseek(sfd, offset, SEEK_SET);
1414 #endif 
1415
1416     while (1) {
1417         if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1418             if (errno == EINTR)
1419                 continue;
1420             err = -1;
1421             break;
1422         }
1423
1424         if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1425             break;
1426         }
1427     }
1428     return err;
1429 }
1430
1431 /* ----------------------------------
1432  * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1433  * because we are doing it elsewhere.
1434  * currently if newname is NULL then adp is NULL. 
1435  */
1436 int copyfile(const struct vol *s_vol,
1437              const struct vol *d_vol, 
1438              int sfd,
1439              char *src,
1440              char *dst,
1441              char *newname,
1442              struct adouble *adp)
1443 {
1444     struct adouble      ads, add;
1445     int                 err = 0;
1446     int                 ret_err = 0;
1447     int                 adflags;
1448     int                 stat_result;
1449     struct stat         st;
1450     
1451     LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1452         sfd, src, dst, newname);
1453
1454     if (adp == NULL) {
1455         ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options); 
1456         adp = &ads;
1457     }
1458
1459     adflags = ADFLAGS_DF;
1460     if (newname) {
1461         adflags |= ADFLAGS_HF;
1462     }
1463
1464     if (ad_openat(adp, sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0) {
1465         ret_err = errno;
1466         goto done;
1467     }
1468
1469     if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1470         /* no resource fork, don't create one for dst file */
1471         adflags &= ~ADFLAGS_HF;
1472     }
1473
1474     stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1475
1476     if (stat_result < 0) {           
1477       /* unlikely but if fstat fails, the default file mode will be 0666. */
1478       st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1479     }
1480
1481     ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1482     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) {
1483         ret_err = errno;
1484         ad_close( adp, adflags );
1485         if (EEXIST != ret_err) {
1486             deletefile(d_vol, -1, dst, 0);
1487             goto done;
1488         }
1489         return AFPERR_EXIST;
1490     }
1491     
1492     /*
1493      * XXX if the source and the dest don't use the same resource type it's broken
1494      */
1495     if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1496         /* copy the data fork */
1497         if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1498             err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1499         }
1500     }
1501
1502     if (err < 0) {
1503        ret_err = errno;
1504     }
1505
1506     if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1507         /* set the new name in the resource fork */
1508         ad_copy_header(&add, adp);
1509         ad_setname(&add, newname);
1510         ad_flush( &add );
1511     }
1512     ad_close( adp, adflags );
1513
1514     if (ad_close( &add, adflags ) <0) {
1515        ret_err = errno;
1516     } 
1517
1518     if (ret_err) {
1519         deletefile(d_vol, -1, dst, 0);
1520     }
1521     else if (stat_result == 0) {
1522         /* set dest modification date to src date */
1523         struct utimbuf  ut;
1524
1525         ut.actime = ut.modtime = st.st_mtime;
1526         utime(dst, &ut);
1527         /* FIXME netatalk doesn't use resource fork file date
1528          * but maybe we should set its modtime too.
1529         */
1530     }
1531
1532 done:
1533     switch ( ret_err ) {
1534     case 0:
1535         return AFP_OK;
1536     case EDQUOT:
1537     case EFBIG:
1538     case ENOSPC:
1539         return AFPERR_DFULL;
1540     case ENOENT:
1541         return AFPERR_NOOBJ;
1542     case EACCES:
1543         return AFPERR_ACCESS;
1544     case EROFS:
1545         return AFPERR_VLOCK;
1546     }
1547     return AFPERR_PARAM;
1548 }
1549
1550
1551 /* -----------------------------------
1552    vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1553    checkAttrib:   1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1554
1555    when deletefile is called we don't have lock on it, file is closed (for us)
1556    untrue if called by renamefile
1557    
1558    ad_open always try to open file RDWR first and ad_lock takes care of
1559    WRITE lock on read only file.
1560 */
1561
1562 static int check_attrib(struct adouble *adp)
1563 {
1564 u_int16_t   bshort = 0;
1565
1566         ad_getattr(adp, &bshort);
1567     /*
1568      * Does kFPDeleteInhibitBit (bit 8) set?
1569      */
1570         if ((bshort & htons(ATTRBIT_NODELETE))) {
1571                 return AFPERR_OLOCK;
1572         }
1573     if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1574         return AFPERR_BUSY;
1575         }
1576         return 0;
1577 }
1578 /* 
1579  * dirfd can be used for unlinkat semantics
1580  */
1581 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1582 {
1583     struct adouble      ad;
1584     struct adouble      *adp = NULL;
1585     int                 adflags, err = AFP_OK;
1586     int                 meta = 0;
1587
1588     LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1589
1590     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1591     if (checkAttrib) {
1592         /* was EACCESS error try to get only metadata */
1593         /* we never want to create a resource fork here, we are going to delete it 
1594          * moreover sometimes deletefile is called with a no existent file and 
1595          * ad_open would create a 0 byte resource fork
1596         */
1597         if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1598             if ((err = check_attrib(&ad))) {
1599                ad_close_metadata(&ad);
1600                return err;
1601             }
1602             meta = 1;
1603         }
1604     }
1605  
1606     /* try to open both forks at once */
1607     adflags = ADFLAGS_DF;
1608     if ( ad_openat(&ad, dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0 ) {
1609         switch (errno) {
1610         case ENOENT:
1611             err = AFPERR_NOOBJ;
1612             goto end;
1613         case EACCES: /* maybe it's a file with no write mode for us */
1614             break;   /* was return AFPERR_ACCESS;*/
1615         case EROFS:
1616             err = AFPERR_VLOCK;
1617             goto end;
1618         default:
1619             err = AFPERR_PARAM;
1620             goto end;
1621         }
1622     }
1623     else {
1624         adp = &ad;
1625     }
1626
1627     if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1628         adflags |= ADFLAGS_HF;
1629         /* FIXME we have a pb here because we want to know if a file is open 
1630          * there's a 'priority inversion' if you can't open the ressource fork RW
1631          * you can delete it if it's open because you can't get a write lock.
1632          * 
1633          * ADLOCK_FILELOCK means the whole ressource fork, not only after the 
1634          * metadatas
1635          *
1636          * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1637          */
1638         if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1639             err = AFPERR_BUSY;
1640             goto end;
1641         }
1642     }
1643
1644     if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1645         err = AFPERR_BUSY;
1646     } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1647         cnid_t id;
1648         if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1649             cnid_delete(vol->v_cdb, id);
1650         }
1651     }
1652
1653 end:
1654     if (meta)
1655         ad_close_metadata(&ad);
1656
1657     if (adp)
1658         ad_close( &ad, adflags );  /* ad_close removes locks if any */
1659
1660     return err;
1661 }
1662
1663 /* ------------------------------------ */
1664 /* return a file id */
1665 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1666 {
1667     struct stat         *st;
1668     struct vol          *vol;
1669     struct dir          *dir;
1670     char                *upath;
1671     int                 len;
1672     cnid_t              did, id;
1673     u_short             vid;
1674     struct path         *s_path;
1675
1676     *rbuflen = 0;
1677
1678     ibuf += 2;
1679
1680     memcpy(&vid, ibuf, sizeof(vid));
1681     ibuf += sizeof(vid);
1682
1683     if (NULL == ( vol = getvolbyvid( vid )) ) {
1684         return( AFPERR_PARAM);
1685     }
1686
1687     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1688         return AFPERR_NOOP;
1689     }
1690
1691     if (vol->v_flags & AFPVOL_RO)
1692         return AFPERR_VLOCK;
1693
1694     memcpy(&did, ibuf, sizeof( did ));
1695     ibuf += sizeof(did);
1696
1697     if (NULL == ( dir = dirlookup( vol, did )) ) {
1698         return afp_errno; /* was AFPERR_PARAM */
1699     }
1700
1701     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1702         return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1703     }
1704
1705     if ( path_isadir(s_path) ) {
1706         return( AFPERR_BADTYPE );
1707     }
1708
1709     upath = s_path->u_name;
1710     switch (s_path->st_errno) {
1711         case 0:
1712              break; /* success */
1713         case EPERM:
1714         case EACCES:
1715             return AFPERR_ACCESS;
1716         case ENOENT:
1717             return AFPERR_NOOBJ;
1718         default:
1719             return AFPERR_PARAM;
1720     }
1721     st = &s_path->st;
1722     if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1723         memcpy(rbuf, &id, sizeof(id));
1724         *rbuflen = sizeof(id);
1725         return AFPERR_EXISTID;
1726     }
1727
1728     if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1729         memcpy(rbuf, &id, sizeof(id));
1730         *rbuflen = sizeof(id);
1731         return AFP_OK;
1732     }
1733
1734     return afp_errno;
1735 }
1736
1737 /* ------------------------------- */
1738 struct reenum {
1739     struct vol *vol;
1740     cnid_t     did;
1741 };
1742
1743 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1744 {
1745     struct path   path;
1746     struct reenum *param = data;
1747     struct vol    *vol = param->vol;  
1748     cnid_t        did  = param->did;
1749     cnid_t        aint;
1750     
1751     if ( lstat(de->d_name, &path.st) < 0 )
1752         return 0;
1753     
1754     /* update or add to cnid */
1755     aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1756
1757     return 0;
1758 }
1759
1760 /* --------------------
1761  * Ok the db is out of synch with the dir.
1762  * but if it's a deleted file we don't want to do it again and again.
1763 */
1764 static int
1765 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1766 {
1767     int             ret;
1768     struct reenum   data;
1769     struct stat     st;
1770     
1771     if (vol->v_cdb == NULL) {
1772         return -1;
1773     }
1774     
1775     /* FIXME use of_statdir ? */
1776     if (lstat(name, &st)) {
1777         return -1;
1778     }
1779
1780     if (dirreenumerate(dir, &st)) {
1781         /* we already did it once and the dir haven't been modified */
1782         return dir->offcnt;
1783     }
1784     
1785     data.vol = vol;
1786     data.did = dir->d_did;
1787     if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1788         setdiroffcnt(curdir, &st,  ret);
1789         dir->d_flags |= DIRF_CNID;
1790     }
1791
1792     return ret;
1793 }
1794
1795 /* ------------------------------
1796    resolve a file id */
1797 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1798 {
1799     struct vol          *vol;
1800     struct dir          *dir;
1801     char                *upath;
1802     struct path         path;
1803     int                 err, retry=0;
1804     size_t              buflen;
1805     cnid_t              id, cnid;
1806     u_int16_t           vid, bitmap;
1807
1808     static char buffer[12 + MAXPATHLEN + 1];
1809     int len = 12 + MAXPATHLEN + 1;
1810
1811     *rbuflen = 0;
1812     ibuf += 2;
1813
1814     memcpy(&vid, ibuf, sizeof(vid));
1815     ibuf += sizeof(vid);
1816
1817     if (NULL == ( vol = getvolbyvid( vid )) ) {
1818         return( AFPERR_PARAM);
1819     }
1820
1821     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1822         return AFPERR_NOOP;
1823     }
1824
1825     memcpy(&id, ibuf, sizeof( id ));
1826     ibuf += sizeof(id);
1827     cnid = id;
1828     
1829     if (!id) {
1830         /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1831         return AFPERR_NOID;
1832     }
1833 retry:
1834     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1835         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1836     }
1837
1838     if (NULL == ( dir = dirlookup( vol, id )) ) {
1839         return AFPERR_NOID; /* idem AFPERR_PARAM */
1840     }
1841     if (movecwd(vol, dir) < 0) {
1842         switch (errno) {
1843         case EACCES:
1844         case EPERM:
1845             return AFPERR_ACCESS;
1846         case ENOENT:
1847             return AFPERR_NOID;
1848         default:
1849             return AFPERR_PARAM;
1850         }
1851     }
1852
1853     memset(&path, 0, sizeof(path));
1854     path.u_name = upath;
1855     if ( of_stat(&path) < 0 ) {
1856 #ifdef ESTALE
1857         /* with nfs and our working directory is deleted */
1858         if (errno == ESTALE) {
1859             errno = ENOENT;
1860         }
1861 #endif  
1862         if ( errno == ENOENT && !retry) {
1863             /* cnid db is out of sync, reenumerate the directory and update ids */
1864             reenumerate_id(vol, ".", dir);
1865             id = cnid;
1866             retry = 1;
1867             goto retry;
1868         }
1869         switch (errno) {
1870         case EACCES:
1871         case EPERM:
1872             return AFPERR_ACCESS;
1873         case ENOENT:
1874             return AFPERR_NOID;
1875         default:
1876             return AFPERR_PARAM;
1877         }
1878     }
1879
1880     /* directories are bad */
1881     if (S_ISDIR(path.st.st_mode)) {
1882         /* OS9 and OSX don't return the same error code  */
1883         return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1884     }
1885
1886     memcpy(&bitmap, ibuf, sizeof(bitmap));
1887     bitmap = ntohs( bitmap );
1888     if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1889         return AFPERR_NOID;
1890     }
1891     path.id = cnid;
1892     if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir, 
1893                             rbuf + sizeof(bitmap), &buflen))) {
1894         return err;
1895     }
1896     *rbuflen = buflen + sizeof(bitmap);
1897     memcpy(rbuf, ibuf, sizeof(bitmap));
1898
1899     return AFP_OK;
1900 }
1901
1902 /* ------------------------------ */
1903 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1904 {
1905     struct stat         st;
1906     struct vol          *vol;
1907     struct dir          *dir;
1908     char                *upath;
1909     int                 err;
1910     cnid_t              id;
1911     cnid_t              fileid;
1912     u_short             vid;
1913     static char buffer[12 + MAXPATHLEN + 1];
1914     int len = 12 + MAXPATHLEN + 1;
1915
1916     *rbuflen = 0;
1917     ibuf += 2;
1918
1919     memcpy(&vid, ibuf, sizeof(vid));
1920     ibuf += sizeof(vid);
1921
1922     if (NULL == ( vol = getvolbyvid( vid )) ) {
1923         return( AFPERR_PARAM);
1924     }
1925
1926     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1927         return AFPERR_NOOP;
1928     }
1929
1930     if (vol->v_flags & AFPVOL_RO)
1931         return AFPERR_VLOCK;
1932
1933     memcpy(&id, ibuf, sizeof( id ));
1934     ibuf += sizeof(id);
1935     fileid = id;
1936
1937     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1938         return AFPERR_NOID;
1939     }
1940
1941     if (NULL == ( dir = dirlookup( vol, id )) ) {
1942         if (afp_errno == AFPERR_NOOBJ) {
1943             err = AFPERR_NOOBJ;
1944             goto delete;
1945         }
1946         return( AFPERR_PARAM );
1947     }
1948
1949     err = AFP_OK;
1950     if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1951         switch (errno) {
1952         case EACCES:
1953         case EPERM:
1954             return AFPERR_ACCESS;
1955 #ifdef ESTALE
1956         case ESTALE:
1957 #endif  
1958         case ENOENT:
1959             /* still try to delete the id */
1960             err = AFPERR_NOOBJ;
1961             break;
1962         default:
1963             return AFPERR_PARAM;
1964         }
1965     }
1966     else if (S_ISDIR(st.st_mode)) /* directories are bad */
1967         return AFPERR_BADTYPE;
1968
1969 delete:
1970     if (cnid_delete(vol->v_cdb, fileid)) {
1971         switch (errno) {
1972         case EROFS:
1973             return AFPERR_VLOCK;
1974         case EPERM:
1975         case EACCES:
1976             return AFPERR_ACCESS;
1977         default:
1978             return AFPERR_PARAM;
1979         }
1980     }
1981
1982     return err;
1983 }
1984
1985 /* ------------------------------ */
1986 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1987 {
1988     int             ret;
1989
1990     if (path->st_errno) {
1991         switch (path->st_errno) {
1992         case ENOENT:
1993             afp_errno = AFPERR_NOID;
1994             break;
1995         case EPERM:
1996         case EACCES:
1997             afp_errno = AFPERR_ACCESS;
1998             break;
1999         default:
2000             afp_errno = AFPERR_PARAM;
2001             break;
2002         }
2003         return NULL;
2004     }
2005     /* we use file_access both for legacy Mac perm and
2006      * for unix privilege, rename will take care of folder perms
2007     */
2008     if (file_access(path, OPENACC_WR ) < 0) {
2009         afp_errno = AFPERR_ACCESS;
2010         return NULL;
2011     }
2012     
2013     if ((*of = of_findname(path))) {
2014         /* reuse struct adouble so it won't break locks */
2015         adp = (*of)->of_ad;
2016     }
2017     else {
2018         ret = ad_open(adp, path->u_name, ADFLAGS_HF, O_RDONLY);
2019         /* META and HF */
2020         if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2021             /* from AFP spec.
2022              * The user must have the Read & Write privilege for both files in order to use this command.
2023              */
2024             ad_close(adp, ADFLAGS_HF);
2025             afp_errno = AFPERR_ACCESS;
2026             return NULL;
2027         }
2028     }
2029     return adp;
2030 }
2031
2032 #define APPLETEMP ".AppleTempXXXXXX"
2033
2034 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2035 {
2036     struct stat         srcst, destst;
2037     struct vol          *vol;
2038     struct dir          *dir, *sdir;
2039     char                *spath, temp[17], *p;
2040     char                *supath, *upath;
2041     struct path         *path;
2042     int                 err;
2043     struct adouble      ads;
2044     struct adouble      add;
2045     struct adouble      *adsp = NULL;
2046     struct adouble      *addp = NULL;
2047     struct ofork        *s_of = NULL;
2048     struct ofork        *d_of = NULL;
2049     int                 crossdev;
2050     
2051     int                 slen, dlen;
2052     u_int32_t           sid, did;
2053     u_int16_t           vid;
2054
2055     uid_t              uid;
2056     gid_t              gid;
2057
2058     *rbuflen = 0;
2059     ibuf += 2;
2060
2061     memcpy(&vid, ibuf, sizeof(vid));
2062     ibuf += sizeof(vid);
2063
2064     if (NULL == ( vol = getvolbyvid( vid )) ) {
2065         return( AFPERR_PARAM);
2066     }
2067
2068     if ((vol->v_flags & AFPVOL_RO))
2069         return AFPERR_VLOCK;
2070
2071     /* source and destination dids */
2072     memcpy(&sid, ibuf, sizeof(sid));
2073     ibuf += sizeof(sid);
2074     memcpy(&did, ibuf, sizeof(did));
2075     ibuf += sizeof(did);
2076
2077     /* source file */
2078     if (NULL == (dir = dirlookup( vol, sid )) ) {
2079         return afp_errno; /* was AFPERR_PARAM */
2080     }
2081
2082     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2083         return get_afp_errno(AFPERR_NOOBJ); 
2084     }
2085
2086     if ( path_isadir(path) ) {
2087         return AFPERR_BADTYPE;   /* it's a dir */
2088     }
2089
2090     /* save some stuff */
2091     srcst = path->st;
2092     sdir = curdir;
2093     spath = obj->oldtmp;
2094     supath = obj->newtmp;
2095     strcpy(spath, path->m_name);
2096     strcpy(supath, path->u_name); /* this is for the cnid changing */
2097     p = absupath( vol, sdir, supath);
2098     if (!p) {
2099         /* pathname too long */
2100         return AFPERR_PARAM ;
2101     }
2102     
2103     ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2104     if (!(adsp = find_adouble( path, &s_of, &ads))) {
2105         return afp_errno;
2106     }
2107
2108     /* ***** from here we may have resource fork open **** */
2109     
2110     /* look for the source cnid. if it doesn't exist, don't worry about
2111      * it. */
2112     sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2113
2114     if (NULL == ( dir = dirlookup( vol, did )) ) {
2115         err = afp_errno; /* was AFPERR_PARAM */
2116         goto err_exchangefile;
2117     }
2118
2119     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2120         err = get_afp_errno(AFPERR_NOOBJ); 
2121         goto err_exchangefile;
2122     }
2123
2124     if ( path_isadir(path) ) {
2125         err = AFPERR_BADTYPE;
2126         goto err_exchangefile;
2127     }
2128
2129     /* FPExchangeFiles is the only call that can return the SameObj
2130      * error */
2131     if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2132         err = AFPERR_SAMEOBJ;
2133         goto err_exchangefile;
2134     }
2135
2136     ad_init(&add, vol->v_adouble, vol->v_ad_options);
2137     if (!(addp = find_adouble( path, &d_of, &add))) {
2138         err = afp_errno;
2139         goto err_exchangefile;
2140     }
2141     destst = path->st;
2142
2143     /* they are not on the same device and at least one is open
2144      * FIXME broken for for crossdev and adouble v2
2145      * return an error 
2146     */
2147     crossdev = (srcst.st_dev != destst.st_dev);
2148     if (/* (d_of || s_of)  && */ crossdev) {
2149         err = AFPERR_MISC;
2150         goto err_exchangefile;
2151     }
2152
2153     /* look for destination id. */
2154     upath = path->u_name;
2155     did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2156
2157     /* construct a temp name.
2158      * NOTE: the temp file will be in the dest file's directory. it
2159      * will also be inaccessible from AFP. */
2160     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2161     if (!mktemp(temp)) {
2162         err = AFPERR_MISC;
2163         goto err_exchangefile;
2164     }
2165     
2166     if (crossdev) {
2167         /* FIXME we need to close fork for copy, both s_of and d_of are null */
2168        ad_close(adsp, ADFLAGS_HF);
2169        ad_close(addp, ADFLAGS_HF);
2170     }
2171
2172     /* now, quickly rename the file. we error if we can't. */
2173     if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2174         goto err_exchangefile;
2175     of_rename(vol, s_of, sdir, spath, curdir, temp);
2176
2177     /* rename destination to source */
2178     if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2179         goto err_src_to_tmp;
2180     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2181
2182     /* rename temp to destination */
2183     if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2184         goto err_dest_to_src;
2185     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2186
2187     /* id's need switching. src -> dest and dest -> src. 
2188      * we need to re-stat() if it was a cross device copy.
2189     */
2190     if (sid)
2191         cnid_delete(vol->v_cdb, sid);
2192     if (did)
2193         cnid_delete(vol->v_cdb, did);
2194
2195     if ((did && ( (crossdev && lstat( upath, &srcst) < 0) || 
2196                 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2197        ||
2198        (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2199                 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2200     ) {
2201         switch (errno) {
2202         case EPERM:
2203         case EACCES:
2204             err = AFPERR_ACCESS;
2205             break;
2206         default:
2207             err = AFPERR_PARAM;
2208         }
2209         goto err_temp_to_dest;
2210     }
2211     
2212     /* here we need to reopen if crossdev */
2213     if (sid && ad_setid(addp, destst.st_dev, destst.st_ino,  sid, sdir->d_did, vol->v_stamp)) 
2214     {
2215        ad_flush( addp );
2216     }
2217         
2218     if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino,  did, curdir->d_did, vol->v_stamp)) 
2219     {
2220        ad_flush( adsp );
2221     }
2222
2223     /* change perms, src gets dest perm and vice versa */
2224
2225     uid = geteuid();
2226     gid = getegid();
2227     if (seteuid(0)) {
2228         LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2229         err = AFP_OK; /* ignore error */
2230         goto err_temp_to_dest;
2231     }
2232
2233     /*
2234      * we need to exchange ACL entries as well
2235      */
2236     /* exchange_acls(vol, p, upath); */
2237
2238     path->st = srcst;
2239     path->st_valid = 1;
2240     path->st_errno = 0;
2241     path->m_name = NULL;
2242     path->u_name = upath;
2243
2244     setfilunixmode(vol, path, destst.st_mode);
2245     setfilowner(vol, destst.st_uid, destst.st_gid, path);
2246
2247     path->st = destst;
2248     path->st_valid = 1;
2249     path->st_errno = 0;
2250     path->u_name = p;
2251
2252     setfilunixmode(vol, path, srcst.st_mode);
2253     setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2254
2255     if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2256         LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2257         exit(EXITERR_SYS);
2258     }
2259
2260     err = AFP_OK;
2261     goto err_exchangefile;
2262
2263     /* all this stuff is so that we can unwind a failed operation
2264      * properly. */
2265 err_temp_to_dest:
2266     /* rename dest to temp */
2267     renamefile(vol, -1, upath, temp, temp, adsp);
2268     of_rename(vol, s_of, curdir, upath, curdir, temp);
2269
2270 err_dest_to_src:
2271     /* rename source back to dest */
2272     renamefile(vol, -1, p, upath, path->m_name, addp);
2273     of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2274
2275 err_src_to_tmp:
2276     /* rename temp back to source */
2277     renamefile(vol, -1, temp, p, spath, adsp);
2278     of_rename(vol, s_of, curdir, temp, sdir, spath);
2279
2280 err_exchangefile:
2281     if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2282        ad_close(adsp, ADFLAGS_HF);
2283     }
2284     if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2285        ad_close(addp, ADFLAGS_HF);
2286     }
2287
2288     struct dir *cached;
2289     if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2290         (void)dir_remove(vol, cached);
2291     if ((cached = dircache_search_by_did(vol, did)) != NULL)
2292         (void)dir_remove(vol, cached);
2293
2294     return err;
2295 }