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