]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/file.c
Fix copyfile: do nothing in the VFS
[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
681     if ( *s_path->m_name == '\0' )
682         return( AFPERR_BADTYPE );
683
684     upath = s_path->u_name;
685     ad_init(&ad, vol);
686     
687     /* if upath is deleted we already in trouble anyway */
688     if ((of = of_findname(s_path))) {
689         if (creatf)
690             return AFPERR_BUSY;
691         else
692             return AFPERR_EXIST;
693     }
694
695     if (creatf)
696         openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
697     else
698         /* on a soft create, if the file is open then ad_open won't fail
699            because open syscall is not called */
700         openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
701
702     if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
703         switch ( errno ) {
704         case EROFS:
705             return AFPERR_VLOCK;
706         case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
707             return ( AFPERR_NOOBJ );
708         case EEXIST :
709             return( AFPERR_EXIST );
710         case EACCES :
711             return( AFPERR_ACCESS );
712         case EDQUOT:
713         case ENOSPC :
714             return( AFPERR_DFULL );
715         default :
716             return( AFPERR_PARAM );
717         }
718     }
719     if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
720          /* FIXME with hard create on an existing file, we already
721           * corrupted the data file.
722           */
723          netatalk_unlink( upath );
724          ad_close( &ad, ADFLAGS_DF );
725          return AFPERR_ACCESS;
726     }
727
728     path = s_path->m_name;
729     ad_setname(&ad, path);
730
731     struct stat st;
732     if (lstat(upath, &st) != 0) {
733         LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
734             upath, strerror(errno));
735         ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
736         return AFPERR_MISC;
737     }
738
739     (void)get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath));
740
741     ad_flush(&ad);
742     ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
743     fce_register_new_file(s_path);
744
745 createfile_done:
746     curdir->d_offcnt++;
747
748     setvoltime(obj, vol );
749
750     return (retvalue);
751 }
752
753 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
754 {
755     struct vol  *vol;
756     struct dir  *dir;
757     struct path *s_path;
758     int         did, rc;
759     uint16_t    vid, bitmap;
760
761     *rbuflen = 0;
762     ibuf += 2;
763
764     memcpy(&vid, ibuf, sizeof( vid ));
765     ibuf += sizeof( vid );
766     if (NULL == ( vol = getvolbyvid( vid )) ) {
767         return( AFPERR_PARAM );
768     }
769
770     if (vol->v_flags & AFPVOL_RO)
771         return AFPERR_VLOCK;
772
773     memcpy(&did, ibuf, sizeof( did ));
774     ibuf += sizeof( did );
775     if (NULL == ( dir = dirlookup( vol, did )) ) {
776         return afp_errno; /* was AFPERR_NOOBJ */
777     }
778
779     memcpy(&bitmap, ibuf, sizeof( bitmap ));
780     bitmap = ntohs( bitmap );
781     ibuf += sizeof( bitmap );
782
783     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
784         return get_afp_errno(AFPERR_PARAM);
785     }
786
787     if (path_isadir(s_path)) {
788         return( AFPERR_BADTYPE ); /* it's a directory */
789     }
790
791     if ( s_path->st_errno != 0 ) {
792         return( AFPERR_NOOBJ );
793     }
794
795     if ((u_long)ibuf & 1 ) {
796         ibuf++;
797     }
798
799     if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
800         setvoltime(obj, vol );
801     }
802
803     return( rc );
804 }
805
806 /*
807  * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic  
808  * 
809 */
810 extern struct path Cur_Path;
811
812 int setfilparams(struct vol *vol,
813                  struct path *path, uint16_t f_bitmap, char *buf )
814 {
815     struct adouble      ad, *adp;
816     struct extmap       *em;
817     int                 bit, isad = 1, err = AFP_OK;
818     char                *upath;
819     u_char              achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
820     uint16_t            ashort, bshort, oshort;
821     uint32_t            aint;
822     uint32_t            upriv;
823     uint16_t           upriv_bit = 0;
824     
825     struct utimbuf      ut;
826
827     int                 change_mdate = 0;
828     int                 change_parent_mdate = 0;
829     int                 newdate = 0;
830     struct timeval      tv;
831     uid_t               f_uid;
832     gid_t               f_gid;
833     uint16_t           bitmap = f_bitmap;
834     uint32_t           cdate,bdate;
835     u_char              finder_buf[32];
836
837 #ifdef DEBUG
838     LOG(log_debug9, logtype_afpd, "begin setfilparams:");
839 #endif /* DEBUG */
840
841     adp = of_ad(vol, path, &ad);
842     upath = path->u_name;
843
844     if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
845         return AFPERR_ACCESS;
846     }
847
848     /* with unix priv maybe we have to change adouble file priv first */
849     bit = 0;
850     while ( bitmap != 0 ) {
851         while (( bitmap & 1 ) == 0 ) {
852             bitmap = bitmap>>1;
853             bit++;
854         }
855         switch(  bit ) {
856         case FILPBIT_ATTR :
857             change_mdate = 1;
858             memcpy(&ashort, buf, sizeof( ashort ));
859             buf += sizeof( ashort );
860             break;
861         case FILPBIT_CDATE :
862             change_mdate = 1;
863             memcpy(&cdate, buf, sizeof(cdate));
864             buf += sizeof( cdate );
865             break;
866         case FILPBIT_MDATE :
867             memcpy(&newdate, buf, sizeof( newdate ));
868             buf += sizeof( newdate );
869             break;
870         case FILPBIT_BDATE :
871             change_mdate = 1;
872             memcpy(&bdate, buf, sizeof( bdate));
873             buf += sizeof( bdate );
874             break;
875         case FILPBIT_FINFO :
876             change_mdate = 1;
877             memcpy(finder_buf, buf, 32 );
878             if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
879             // SLFINFO
880                 int fp;
881                 ssize_t len;
882                 int erc=1;
883                 char buf[PATH_MAX+1];
884                 if ((fp=open(path->u_name,O_RDONLY))>=0){
885                     if ((len=read(fp,buf,PATH_MAX+1))){
886                         if (unlink(path->u_name)==0){
887                             buf[len]=0;
888                             erc = symlink(buf, path->u_name);
889                             if (!erc)
890                                 of_stat(path);
891                         }
892                     }
893                     close(fp);
894                 }
895                 if (erc!=0){
896                     err=AFPERR_BITMAP;
897                     goto setfilparam_done;
898                 }
899             }
900             buf += 32;
901             break;
902         case FILPBIT_UNIXPR :
903             if (!vol_unix_priv(vol)) {
904                 /* this volume doesn't use unix priv */
905                 err = AFPERR_BITMAP;
906                 bitmap = 0;
907                 break;
908             }
909             change_mdate = 1;
910             change_parent_mdate = 1;
911
912             memcpy( &aint, buf, sizeof( aint ));
913             f_uid = ntohl (aint);
914             buf += sizeof( aint );
915             memcpy( &aint, buf, sizeof( aint ));
916             f_gid = ntohl (aint);
917             buf += sizeof( aint );
918             setfilowner(vol, f_uid, f_gid, path);
919
920             memcpy( &upriv, buf, sizeof( upriv ));
921             buf += sizeof( upriv );
922             upriv = ntohl (upriv);
923             if ((upriv & S_IWUSR)) {
924                 setfilunixmode(vol, path, upriv);
925             }
926             else {
927                 /* do it later */
928                 upriv_bit = 1;
929             }
930             break;
931         case FILPBIT_PDINFO :
932             if (afp_version < 30) { /* else it's UTF8 name */
933                 achar = *buf;
934                 buf += 2;
935                 /* Keep special case to support crlf translations */
936                 if ((unsigned int) achar == 0x04) {
937                     fdType = (u_char *)"TEXT";
938                     buf += 2;
939                 } else {
940                     xyy[0] = ( u_char ) 'p';
941                     xyy[1] = achar;
942                     xyy[3] = *buf++;
943                     xyy[2] = *buf++;
944                     fdType = xyy;
945                 }
946                 break;
947             }
948             /* fallthrough */
949         default :
950             err = AFPERR_BITMAP;
951             /* break while loop */
952             bitmap = 0;
953             break;
954         }
955
956         bitmap = bitmap>>1;
957         bit++;
958     }
959
960     /* second try with adouble open 
961     */
962     if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
963         LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
964         /*
965          * For some things, we don't need an adouble header:
966          * - change of modification date
967          * - UNIX privs (Bug-ID #2863424)
968          */
969         if (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
970             LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
971             return AFPERR_ACCESS;
972         }
973         LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
974         isad = 0;
975     } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
976         ad_setname(adp, path->m_name);
977     }
978     
979     bit = 0;
980     bitmap = f_bitmap;
981     while ( bitmap != 0 ) {
982         while (( bitmap & 1 ) == 0 ) {
983             bitmap = bitmap>>1;
984             bit++;
985         }
986
987         switch(  bit ) {
988         case FILPBIT_ATTR :
989             ad_getattr(adp, &bshort);
990             oshort = bshort;
991             if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
992                 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
993             } else {
994                 bshort &= ~ashort;
995             }
996             if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
997                 change_parent_mdate = 1;
998             ad_setattr(adp, bshort);
999             break;
1000         case FILPBIT_CDATE :
1001             ad_setdate(adp, AD_DATE_CREATE, cdate);
1002             break;
1003         case FILPBIT_MDATE :
1004             break;
1005         case FILPBIT_BDATE :
1006             ad_setdate(adp, AD_DATE_BACKUP, bdate);
1007             break;
1008         case FILPBIT_FINFO :
1009             if (default_type( ad_entry( adp, ADEID_FINDERI ))
1010                     && ( 
1011                      ((em = getextmap( path->m_name )) &&
1012                       !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1013                       !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1014                      || ((em = getdefextmap()) &&
1015                       !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1016                       !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1017             )) {
1018                 memcpy(finder_buf, ufinderi, 8 );
1019             }
1020             memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1021             break;
1022         case FILPBIT_UNIXPR :
1023             if (upriv_bit) {
1024                 setfilunixmode(vol, path, upriv);
1025             }
1026             break;
1027         case FILPBIT_PDINFO :
1028             if (afp_version < 30) { /* else it's UTF8 name */
1029                 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1030                 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1031                 break;
1032             }
1033             /* fallthrough */
1034         default :
1035             err = AFPERR_BITMAP;
1036             goto setfilparam_done;
1037         }
1038         bitmap = bitmap>>1;
1039         bit++;
1040     }
1041
1042 setfilparam_done:
1043     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1044        newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1045     }
1046     if (newdate) {
1047        if (isad)
1048           ad_setdate(adp, AD_DATE_MODIFY, newdate);
1049        ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1050        utime(upath, &ut);
1051     }
1052
1053     if (isad) {
1054         ad_flush(adp);
1055         ad_close(adp, ADFLAGS_HF);
1056     }
1057
1058     if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1059         newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1060         bitmap = 1<<FILPBIT_MDATE;
1061         setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1062     }
1063
1064 #ifdef DEBUG
1065     LOG(log_debug9, logtype_afpd, "end setfilparams:");
1066 #endif /* DEBUG */
1067     return err;
1068 }
1069
1070 /*
1071  * renamefile and copyfile take the old and new unix pathnames
1072  * and the new mac name.
1073  *
1074  * sdir_fd     source dir fd to which src path is relative (for openat et al semantics)
1075  *             passing -1 means this is not used, src path is a full path
1076  * src         the source path 
1077  * dst         the dest filename in current dir
1078  * newname     the dest mac name
1079  * adp         adouble struct of src file, if open, or & zeroed one
1080  *
1081  */
1082 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1083 {
1084     int         rc;
1085
1086     LOG(log_debug, logtype_afpd,
1087         "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1088
1089     if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1090         switch ( errno ) {
1091         case ENOENT :
1092             return( AFPERR_NOOBJ );
1093         case EPERM:
1094         case EACCES :
1095             return( AFPERR_ACCESS );
1096         case EROFS:
1097             return AFPERR_VLOCK;
1098         case EXDEV :                    /* Cross device move -- try copy */
1099            /* NOTE: with open file it's an error because after the copy we will 
1100             * get two files, it's fixable for our process (eg reopen the new file, get the
1101             * locks, and so on. But it doesn't solve the case with a second process
1102             */
1103             if (adp->ad_open_forks) {
1104                 /* FIXME  warning in syslog so admin'd know there's a conflict ?*/
1105                 return AFPERR_OLOCK; /* little lie */
1106             }
1107             if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1108                 /* on error copyfile delete dest */
1109                 return( rc );
1110             }
1111             return deletefile(vol, sdir_fd, src, 0);
1112         default :
1113             return( AFPERR_PARAM );
1114         }
1115     }
1116
1117     if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1118         int err;
1119         
1120         err = errno;        
1121         /* try to undo the data fork rename,
1122          * we know we are on the same device 
1123         */
1124         if (err) {
1125         unix_rename(-1, dst, sdir_fd, src ); 
1126             /* return the first error */
1127             switch ( err) {
1128             case ENOENT :
1129                 return AFPERR_NOOBJ;
1130             case EPERM:
1131             case EACCES :
1132                 return AFPERR_ACCESS ;
1133             case EROFS:
1134                 return AFPERR_VLOCK;
1135             default :
1136                 return AFPERR_PARAM ;
1137             }
1138         }
1139     }
1140
1141     /* don't care if we can't open the newly renamed ressource fork */
1142     if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1143         ad_setname(adp, newname);
1144         ad_flush( adp );
1145         ad_close( adp, ADFLAGS_HF );
1146     }
1147
1148     return( AFP_OK );
1149 }
1150
1151 /* ---------------- 
1152    convert a Mac long name to an utf8 name,
1153 */
1154 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1155 {
1156 size_t    outlen;
1157
1158     if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1159         return -1;
1160     }
1161     return outlen;
1162 }
1163
1164 /* ---------------- */
1165 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1166 {
1167 char        type = *ibuf;
1168 size_t      plen = 0;
1169 uint16_t   len16;
1170 uint32_t   hint;
1171
1172     if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1173         return -1;
1174     }
1175     ibuf++;
1176     switch (type) {
1177     case 2:
1178         if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1179             if (afp_version >= 30) {
1180                 /* convert it to UTF8 
1181                 */
1182                 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1183                    return -1;
1184             }
1185             else {
1186                 strncpy( newname, ibuf, plen );
1187                 newname[ plen ] = '\0';
1188             }
1189             if (strlen(newname) != plen) {
1190                 /* there's \0 in newname, e.g. it's a pathname not
1191                  * only a filename. 
1192                 */
1193                 return -1;
1194             }
1195         }
1196         break;
1197     case 3:
1198         memcpy(&hint, ibuf, sizeof(hint));
1199         ibuf += sizeof(hint);
1200            
1201         memcpy(&len16, ibuf, sizeof(len16));
1202         ibuf += sizeof(len16);
1203         plen = ntohs(len16);
1204         
1205         if (plen) {
1206             if (plen > AFPOBJ_TMPSIZ) {
1207                 return -1;
1208             }
1209             strncpy( newname, ibuf, plen );
1210             newname[ plen ] = '\0';
1211             if (strlen(newname) != plen) {
1212                 return -1;
1213             }
1214         }
1215         break;
1216     }
1217     return plen;
1218 }
1219
1220 /* -----------------------------------
1221 */
1222 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1223 {
1224     struct vol  *s_vol, *d_vol;
1225     struct dir  *dir;
1226     char        *newname, *p, *upath;
1227     struct path *s_path;
1228     uint32_t    sdid, ddid;
1229     int         err, retvalue = AFP_OK;
1230     uint16_t    svid, dvid;
1231
1232     struct adouble ad, *adp;
1233     int denyreadset;
1234     
1235     *rbuflen = 0;
1236     ibuf += 2;
1237
1238     memcpy(&svid, ibuf, sizeof( svid ));
1239     ibuf += sizeof( svid );
1240     if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1241         return( AFPERR_PARAM );
1242     }
1243
1244     memcpy(&sdid, ibuf, sizeof( sdid ));
1245     ibuf += sizeof( sdid );
1246     if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1247         return afp_errno;
1248     }
1249
1250     memcpy(&dvid, ibuf, sizeof( dvid ));
1251     ibuf += sizeof( dvid );
1252     memcpy(&ddid, ibuf, sizeof( ddid ));
1253     ibuf += sizeof( ddid );
1254
1255     if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1256         return get_afp_errno(AFPERR_PARAM);
1257     }
1258     if ( path_isadir(s_path) ) {
1259         return( AFPERR_BADTYPE );
1260     }
1261
1262     /* don't allow copies when the file is open.
1263      * XXX: the spec only calls for read/deny write access.
1264      *      however, copyfile doesn't have any of that info,
1265      *      and locks need to stay coherent. as a result,
1266      *      we just balk if the file is opened already. */
1267
1268     adp = of_ad(s_vol, s_path, &ad);
1269
1270     if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1271         return AFPERR_DENYCONF;
1272     }
1273     denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 || 
1274                   ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1275
1276     if (denyreadset) {
1277         retvalue = AFPERR_DENYCONF;
1278         goto copy_exit;
1279     }
1280
1281     newname = obj->newtmp;
1282     strcpy( newname, s_path->m_name );
1283
1284     p = ctoupath( s_vol, curdir, newname );
1285     if (!p) {
1286         retvalue = AFPERR_PARAM;
1287         goto copy_exit;
1288     }
1289
1290     if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1291         retvalue = AFPERR_PARAM;
1292         goto copy_exit;
1293     }
1294
1295     if (d_vol->v_flags & AFPVOL_RO) {
1296         retvalue = AFPERR_VLOCK;
1297         goto copy_exit;
1298     }
1299
1300     if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1301         retvalue = afp_errno;
1302         goto copy_exit;
1303     }
1304
1305     if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1306         retvalue = get_afp_errno(AFPERR_NOOBJ);
1307         goto copy_exit;
1308     }
1309     
1310     if ( *s_path->m_name != '\0' ) {
1311         retvalue =path_error(s_path, AFPERR_NOOBJ);
1312         goto copy_exit;
1313     }
1314
1315     /* one of the handful of places that knows about the path type */
1316     if (copy_path_name(d_vol, newname, ibuf) < 0) {
1317         retvalue = AFPERR_PARAM;
1318         goto copy_exit;
1319     }
1320     /* newname is always only a filename so curdir *is* its
1321      * parent folder
1322     */
1323     if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1324         retvalue =AFPERR_PARAM;
1325         goto copy_exit;
1326     }
1327
1328     if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1329         retvalue = err;
1330         goto copy_exit;
1331     }
1332     curdir->d_offcnt++;
1333
1334     setvoltime(obj, d_vol );
1335
1336 copy_exit:
1337     ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1338     return( retvalue );
1339 }
1340
1341 /* ----------------------- */
1342 static int copy_all(const int dfd, const void *buf,
1343                                size_t buflen)
1344 {
1345     ssize_t cc;
1346
1347 #ifdef DEBUG
1348     LOG(log_debug9, logtype_afpd, "begin copy_all:");
1349 #endif /* DEBUG */
1350
1351     while (buflen > 0) {
1352         if ((cc = write(dfd, buf, buflen)) < 0) {
1353             switch (errno) {
1354             case EINTR:
1355                 continue;
1356             default:
1357                 return -1;
1358             }
1359         }
1360         buflen -= cc;
1361     }
1362
1363 #ifdef DEBUG
1364     LOG(log_debug9, logtype_afpd, "end copy_all:");
1365 #endif /* DEBUG */
1366
1367     return 0;
1368 }
1369
1370 /* -------------------------- 
1371  * copy only the fork data stream
1372 */
1373 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1374 {
1375     ssize_t cc;
1376     int     err = 0;
1377     char    filebuf[8192];
1378     int     sfd, dfd;
1379     off_t   soff, doff;
1380
1381     if (eid == ADEID_DFORK) {
1382         sfd = ad_data_fileno(ads);
1383         dfd = ad_data_fileno(add);
1384     }
1385     else {
1386         sfd = ad_reso_fileno(ads);
1387         dfd = ad_reso_fileno(add);
1388     }        
1389
1390     if (add->ad_version == AD_VERSION2)
1391         soff = doff = ad_getentryoff(ads, eid);
1392     else {
1393         if (eid == ADEID_DFORK)
1394             soff = doff = ad_getentryoff(ads, eid);
1395         else {
1396 #ifdef HAVE_EAFD
1397             soff = doff = 0;
1398 #else
1399             soff = doff = ADEDOFF_RFORK_OSX;
1400 #endif
1401         }
1402     }
1403
1404     if ((off_t)-1 == lseek(sfd, soff, SEEK_SET))
1405         return -1;
1406
1407     if ((off_t)-1 == lseek(dfd, doff, SEEK_SET))
1408         return -1;
1409         
1410 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1411     /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1412     off_t   offset = 0;
1413     size_t  size;
1414     struct stat         st;
1415     #define BUF 128*1024*1024
1416
1417     if (fstat(sfd, &st) == 0) {
1418         
1419         while (1) {
1420             if ( offset >= st.st_size) {
1421                return 0;
1422             }
1423             size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1424             if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1425                 switch (errno) {
1426                 case ENOSYS:
1427                 case EINVAL:  /* there's no guarantee that all fs support sendfile */
1428                     goto no_sendfile;
1429                 default:
1430                     return -1;
1431                 }
1432             }
1433         }
1434     }
1435     no_sendfile:
1436     lseek(sfd, offset, SEEK_SET);
1437 #endif 
1438
1439     while (1) {
1440         if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1441             if (errno == EINTR)
1442                 continue;
1443             err = -1;
1444             break;
1445         }
1446
1447         if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1448             break;
1449         }
1450     }
1451     return err;
1452 }
1453
1454 /* ----------------------------------
1455  * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1456  * because we are doing it elsewhere.
1457  * currently if newname is NULL then adp is NULL. 
1458  */
1459 int copyfile(const struct vol *s_vol,
1460              const struct vol *d_vol, 
1461              int sfd,
1462              char *src,
1463              char *dst,
1464              char *newname,
1465              struct adouble *adp)
1466 {
1467     struct adouble      ads, add;
1468     int                 err = 0;
1469     int                 ret_err = 0;
1470     int                 adflags;
1471     int                 stat_result;
1472     struct stat         st;
1473     
1474     LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1475         sfd, src, dst, newname);
1476
1477     if (adp == NULL) {
1478         ad_init(&ads, s_vol);
1479         adp = &ads;
1480     }
1481
1482     adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1483     if (newname) {
1484         adflags |= ADFLAGS_HF;
1485     }
1486
1487     if (ad_openat(adp, sfd, src, adflags | ADFLAGS_NOHF | ADFLAGS_RDONLY) < 0) {
1488         ret_err = errno;
1489         goto done;
1490     }
1491
1492     if (!AD_RSRC_OPEN(adp))
1493         /* no resource fork, don't create one for dst file */
1494         adflags &= ~ADFLAGS_RF;
1495
1496     stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1497
1498     if (stat_result < 0) {           
1499       /* unlikely but if fstat fails, the default file mode will be 0666. */
1500       st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1501     }
1502
1503     ad_init(&add, d_vol);
1504     if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode) < 0) {
1505         ret_err = errno;
1506         ad_close( adp, adflags );
1507         if (EEXIST != ret_err) {
1508             deletefile(d_vol, -1, dst, 0);
1509             goto done;
1510         }
1511         return AFPERR_EXIST;
1512     }
1513
1514     if (AD_RSRC_OPEN(adp))
1515         err = copy_fork(ADEID_RFORK, &add, adp);
1516     
1517     if (err == 0)
1518         err = copy_fork(ADEID_DFORK, &add, adp);
1519
1520     if ((err == 0) && (ad_meta_fileno(adp) != -1))
1521         err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1522
1523     if (err < 0)
1524        ret_err = errno;
1525
1526     if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1527         /* set the new name in the resource fork */
1528         ad_copy_header(&add, adp);
1529         ad_setname(&add, newname);
1530         ad_flush( &add );
1531     }
1532     ad_close( adp, adflags );
1533
1534     if (ad_close( &add, adflags ) <0) {
1535        ret_err = errno;
1536     } 
1537
1538     if (ret_err) {
1539         deletefile(d_vol, -1, dst, 0);
1540     }
1541     else if (stat_result == 0) {
1542         /* set dest modification date to src date */
1543         struct utimbuf  ut;
1544
1545         ut.actime = ut.modtime = st.st_mtime;
1546         utime(dst, &ut);
1547         /* FIXME netatalk doesn't use resource fork file date
1548          * but maybe we should set its modtime too.
1549         */
1550     }
1551
1552 done:
1553     switch ( ret_err ) {
1554     case 0:
1555         return AFP_OK;
1556     case EDQUOT:
1557     case EFBIG:
1558     case ENOSPC:
1559         return AFPERR_DFULL;
1560     case ENOENT:
1561         return AFPERR_NOOBJ;
1562     case EACCES:
1563         return AFPERR_ACCESS;
1564     case EROFS:
1565         return AFPERR_VLOCK;
1566     }
1567     return AFPERR_PARAM;
1568 }
1569
1570
1571 /* -----------------------------------
1572    vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1573    checkAttrib:   1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1574
1575    when deletefile is called we don't have lock on it, file is closed (for us)
1576    untrue if called by renamefile
1577    
1578    ad_open always try to open file RDWR first and ad_lock takes care of
1579    WRITE lock on read only file.
1580 */
1581
1582 static int check_attrib(struct adouble *adp)
1583 {
1584 uint16_t   bshort = 0;
1585
1586         ad_getattr(adp, &bshort);
1587     /*
1588      * Does kFPDeleteInhibitBit (bit 8) set?
1589      */
1590         if ((bshort & htons(ATTRBIT_NODELETE))) {
1591                 return AFPERR_OLOCK;
1592         }
1593     if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1594         return AFPERR_BUSY;
1595         }
1596         return 0;
1597 }
1598 /* 
1599  * dirfd can be used for unlinkat semantics
1600  */
1601 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1602 {
1603     struct adouble      ad;
1604     struct adouble      *adp = NULL;
1605     int                 adflags, err = AFP_OK;
1606     int                 meta = 0;
1607
1608     LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1609
1610     ad_init(&ad, vol);
1611     if (checkAttrib) {
1612         /* was EACCESS error try to get only metadata */
1613         /* we never want to create a resource fork here, we are going to delete it 
1614          * moreover sometimes deletefile is called with a no existent file and 
1615          * ad_open would create a 0 byte resource fork
1616         */
1617         if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1618             if ((err = check_attrib(&ad))) {
1619                 ad_close(&ad, ADFLAGS_HF);
1620                return err;
1621             }
1622             meta = 1;
1623         }
1624     }
1625  
1626     /* try to open both forks at once */
1627     adflags = ADFLAGS_DF;
1628     if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1629         switch (errno) {
1630         case ENOENT:
1631             err = AFPERR_NOOBJ;
1632             goto end;
1633         case EACCES: /* maybe it's a file with no write mode for us */
1634             break;   /* was return AFPERR_ACCESS;*/
1635         case EROFS:
1636             err = AFPERR_VLOCK;
1637             goto end;
1638         default:
1639             err = AFPERR_PARAM;
1640             goto end;
1641         }
1642     }
1643     else {
1644         adp = &ad;
1645     }
1646
1647     if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1648         adflags |= ADFLAGS_RF;
1649         /* FIXME we have a pb here because we want to know if a file is open 
1650          * there's a 'priority inversion' if you can't open the ressource fork RW
1651          * you can delete it if it's open because you can't get a write lock.
1652          * 
1653          * ADLOCK_FILELOCK means the whole ressource fork, not only after the 
1654          * metadatas
1655          *
1656          * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1657          */
1658         if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1659             err = AFPERR_BUSY;
1660             goto end;
1661         }
1662     }
1663
1664     if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1665         LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1666         err = AFPERR_BUSY;
1667     } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1668         cnid_t id;
1669         if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1670             cnid_delete(vol->v_cdb, id);
1671         }
1672     }
1673
1674 end:
1675     if (meta)
1676         ad_close(&ad, ADFLAGS_HF);
1677
1678     if (adp)
1679         ad_close( &ad, adflags );  /* ad_close removes locks if any */
1680
1681     return err;
1682 }
1683
1684 /* ------------------------------------ */
1685 /* return a file id */
1686 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1687 {
1688     struct stat         *st;
1689     struct vol          *vol;
1690     struct dir          *dir;
1691     char                *upath;
1692     int                 len;
1693     cnid_t              did, id;
1694     u_short             vid;
1695     struct path         *s_path;
1696
1697     *rbuflen = 0;
1698
1699     ibuf += 2;
1700
1701     memcpy(&vid, ibuf, sizeof(vid));
1702     ibuf += sizeof(vid);
1703
1704     if (NULL == ( vol = getvolbyvid( vid )) ) {
1705         return( AFPERR_PARAM);
1706     }
1707
1708     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1709         return AFPERR_NOOP;
1710     }
1711
1712     if (vol->v_flags & AFPVOL_RO)
1713         return AFPERR_VLOCK;
1714
1715     memcpy(&did, ibuf, sizeof( did ));
1716     ibuf += sizeof(did);
1717
1718     if (NULL == ( dir = dirlookup( vol, did )) ) {
1719         return afp_errno; /* was AFPERR_PARAM */
1720     }
1721
1722     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1723         return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1724     }
1725
1726     if ( path_isadir(s_path) ) {
1727         return( AFPERR_BADTYPE );
1728     }
1729
1730     upath = s_path->u_name;
1731     switch (s_path->st_errno) {
1732         case 0:
1733              break; /* success */
1734         case EPERM:
1735         case EACCES:
1736             return AFPERR_ACCESS;
1737         case ENOENT:
1738             return AFPERR_NOOBJ;
1739         default:
1740             return AFPERR_PARAM;
1741     }
1742     st = &s_path->st;
1743     if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1744         memcpy(rbuf, &id, sizeof(id));
1745         *rbuflen = sizeof(id);
1746         return AFPERR_EXISTID;
1747     }
1748
1749     if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1750         memcpy(rbuf, &id, sizeof(id));
1751         *rbuflen = sizeof(id);
1752         return AFP_OK;
1753     }
1754
1755     return afp_errno;
1756 }
1757
1758 /* ------------------------------- */
1759 struct reenum {
1760     struct vol *vol;
1761     cnid_t     did;
1762 };
1763
1764 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1765 {
1766     struct path   path;
1767     struct reenum *param = data;
1768     struct vol    *vol = param->vol;  
1769     cnid_t        did  = param->did;
1770     cnid_t        aint;
1771     
1772     if ( lstat(de->d_name, &path.st) < 0 )
1773         return 0;
1774     
1775     /* update or add to cnid */
1776     aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1777
1778     return 0;
1779 }
1780
1781 /* --------------------
1782  * Ok the db is out of synch with the dir.
1783  * but if it's a deleted file we don't want to do it again and again.
1784 */
1785 static int
1786 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1787 {
1788     int             ret;
1789     struct reenum   data;
1790     struct stat     st;
1791     
1792     if (vol->v_cdb == NULL) {
1793         return -1;
1794     }
1795     
1796     /* FIXME use of_statdir ? */
1797     if (lstat(name, &st)) {
1798         return -1;
1799     }
1800
1801     if (dirreenumerate(dir, &st)) {
1802         /* we already did it once and the dir haven't been modified */
1803         return dir->d_offcnt;
1804     }
1805     
1806     data.vol = vol;
1807     data.did = dir->d_did;
1808     if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1809         setdiroffcnt(curdir, &st,  ret);
1810         dir->d_flags |= DIRF_CNID;
1811     }
1812
1813     return ret;
1814 }
1815
1816 /* ------------------------------
1817    resolve a file id */
1818 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1819 {
1820     struct vol          *vol;
1821     struct dir          *dir;
1822     char                *upath;
1823     struct path         path;
1824     int                 err, retry=0;
1825     size_t              buflen;
1826     cnid_t              id, cnid;
1827     uint16_t            vid, bitmap;
1828
1829     static char buffer[12 + MAXPATHLEN + 1];
1830     int len = 12 + MAXPATHLEN + 1;
1831
1832     *rbuflen = 0;
1833     ibuf += 2;
1834
1835     memcpy(&vid, ibuf, sizeof(vid));
1836     ibuf += sizeof(vid);
1837
1838     if (NULL == ( vol = getvolbyvid( vid )) ) {
1839         return( AFPERR_PARAM);
1840     }
1841
1842     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1843         return AFPERR_NOOP;
1844     }
1845
1846     memcpy(&id, ibuf, sizeof( id ));
1847     ibuf += sizeof(id);
1848     cnid = id;
1849     
1850     if (!id) {
1851         /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1852         return AFPERR_NOID;
1853     }
1854 retry:
1855     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1856         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1857     }
1858
1859     if (NULL == ( dir = dirlookup( vol, id )) ) {
1860         return AFPERR_NOID; /* idem AFPERR_PARAM */
1861     }
1862     if (movecwd(vol, dir) < 0) {
1863         switch (errno) {
1864         case EACCES:
1865         case EPERM:
1866             return AFPERR_ACCESS;
1867         case ENOENT:
1868             return AFPERR_NOID;
1869         default:
1870             return AFPERR_PARAM;
1871         }
1872     }
1873
1874     memset(&path, 0, sizeof(path));
1875     path.u_name = upath;
1876     if ( of_stat(&path) < 0 ) {
1877 #ifdef ESTALE
1878         /* with nfs and our working directory is deleted */
1879         if (errno == ESTALE) {
1880             errno = ENOENT;
1881         }
1882 #endif  
1883         if ( errno == ENOENT && !retry) {
1884             /* cnid db is out of sync, reenumerate the directory and update ids */
1885             reenumerate_id(vol, ".", dir);
1886             id = cnid;
1887             retry = 1;
1888             goto retry;
1889         }
1890         switch (errno) {
1891         case EACCES:
1892         case EPERM:
1893             return AFPERR_ACCESS;
1894         case ENOENT:
1895             return AFPERR_NOID;
1896         default:
1897             return AFPERR_PARAM;
1898         }
1899     }
1900
1901     /* directories are bad */
1902     if (S_ISDIR(path.st.st_mode)) {
1903         /* OS9 and OSX don't return the same error code  */
1904         return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1905     }
1906
1907     memcpy(&bitmap, ibuf, sizeof(bitmap));
1908     bitmap = ntohs( bitmap );
1909     if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1910         return AFPERR_NOID;
1911     }
1912     path.id = cnid;
1913     if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir, 
1914                             rbuf + sizeof(bitmap), &buflen))) {
1915         return err;
1916     }
1917     *rbuflen = buflen + sizeof(bitmap);
1918     memcpy(rbuf, ibuf, sizeof(bitmap));
1919
1920     return AFP_OK;
1921 }
1922
1923 /* ------------------------------ */
1924 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1925 {
1926     struct stat         st;
1927     struct vol          *vol;
1928     struct dir          *dir;
1929     char                *upath;
1930     int                 err;
1931     cnid_t              id;
1932     cnid_t              fileid;
1933     u_short             vid;
1934     static char buffer[12 + MAXPATHLEN + 1];
1935     int len = 12 + MAXPATHLEN + 1;
1936
1937     *rbuflen = 0;
1938     ibuf += 2;
1939
1940     memcpy(&vid, ibuf, sizeof(vid));
1941     ibuf += sizeof(vid);
1942
1943     if (NULL == ( vol = getvolbyvid( vid )) ) {
1944         return( AFPERR_PARAM);
1945     }
1946
1947     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1948         return AFPERR_NOOP;
1949     }
1950
1951     if (vol->v_flags & AFPVOL_RO)
1952         return AFPERR_VLOCK;
1953
1954     memcpy(&id, ibuf, sizeof( id ));
1955     ibuf += sizeof(id);
1956     fileid = id;
1957
1958     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1959         return AFPERR_NOID;
1960     }
1961
1962     if (NULL == ( dir = dirlookup( vol, id )) ) {
1963         if (afp_errno == AFPERR_NOOBJ) {
1964             err = AFPERR_NOOBJ;
1965             goto delete;
1966         }
1967         return( AFPERR_PARAM );
1968     }
1969
1970     err = AFP_OK;
1971     if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1972         switch (errno) {
1973         case EACCES:
1974         case EPERM:
1975             return AFPERR_ACCESS;
1976 #ifdef ESTALE
1977         case ESTALE:
1978 #endif  
1979         case ENOENT:
1980             /* still try to delete the id */
1981             err = AFPERR_NOOBJ;
1982             break;
1983         default:
1984             return AFPERR_PARAM;
1985         }
1986     }
1987     else if (S_ISDIR(st.st_mode)) /* directories are bad */
1988         return AFPERR_BADTYPE;
1989
1990 delete:
1991     if (cnid_delete(vol->v_cdb, fileid)) {
1992         switch (errno) {
1993         case EROFS:
1994             return AFPERR_VLOCK;
1995         case EPERM:
1996         case EACCES:
1997             return AFPERR_ACCESS;
1998         default:
1999             return AFPERR_PARAM;
2000         }
2001     }
2002
2003     return err;
2004 }
2005
2006 /* ------------------------------ */
2007 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2008 {
2009     int             ret;
2010
2011     if (path->st_errno) {
2012         switch (path->st_errno) {
2013         case ENOENT:
2014             afp_errno = AFPERR_NOID;
2015             break;
2016         case EPERM:
2017         case EACCES:
2018             afp_errno = AFPERR_ACCESS;
2019             break;
2020         default:
2021             afp_errno = AFPERR_PARAM;
2022             break;
2023         }
2024         return NULL;
2025     }
2026     /* we use file_access both for legacy Mac perm and
2027      * for unix privilege, rename will take care of folder perms
2028     */
2029     if (file_access(path, OPENACC_WR ) < 0) {
2030         afp_errno = AFPERR_ACCESS;
2031         return NULL;
2032     }
2033     
2034     if ((*of = of_findname(path))) {
2035         /* reuse struct adouble so it won't break locks */
2036         adp = (*of)->of_ad;
2037     }
2038     else {
2039         ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
2040         /* META and HF */
2041         if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2042             /* from AFP spec.
2043              * The user must have the Read & Write privilege for both files in order to use this command.
2044              */
2045             ad_close(adp, ADFLAGS_HF);
2046             afp_errno = AFPERR_ACCESS;
2047             return NULL;
2048         }
2049     }
2050     return adp;
2051 }
2052
2053 #define APPLETEMP ".AppleTempXXXXXX"
2054
2055 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2056 {
2057     struct stat         srcst, destst;
2058     struct vol          *vol;
2059     struct dir          *dir, *sdir;
2060     char                *spath, temp[17], *p;
2061     char                *supath, *upath;
2062     struct path         *path;
2063     int                 err;
2064     struct adouble      ads;
2065     struct adouble      add;
2066     struct adouble      *adsp = NULL;
2067     struct adouble      *addp = NULL;
2068     struct ofork        *s_of = NULL;
2069     struct ofork        *d_of = NULL;
2070     int                 crossdev;
2071     
2072     int                 slen, dlen;
2073     uint32_t            sid, did;
2074     uint16_t            vid;
2075
2076     uid_t              uid;
2077     gid_t              gid;
2078
2079     *rbuflen = 0;
2080     ibuf += 2;
2081
2082     memcpy(&vid, ibuf, sizeof(vid));
2083     ibuf += sizeof(vid);
2084
2085     if (NULL == ( vol = getvolbyvid( vid )) ) {
2086         return( AFPERR_PARAM);
2087     }
2088
2089     if ((vol->v_flags & AFPVOL_RO))
2090         return AFPERR_VLOCK;
2091
2092     /* source and destination dids */
2093     memcpy(&sid, ibuf, sizeof(sid));
2094     ibuf += sizeof(sid);
2095     memcpy(&did, ibuf, sizeof(did));
2096     ibuf += sizeof(did);
2097
2098     /* source file */
2099     if (NULL == (dir = dirlookup( vol, sid )) ) {
2100         return afp_errno; /* was AFPERR_PARAM */
2101     }
2102
2103     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2104         return get_afp_errno(AFPERR_NOOBJ); 
2105     }
2106
2107     if ( path_isadir(path) ) {
2108         return AFPERR_BADTYPE;   /* it's a dir */
2109     }
2110
2111     /* save some stuff */
2112     srcst = path->st;
2113     sdir = curdir;
2114     spath = obj->oldtmp;
2115     supath = obj->newtmp;
2116     strcpy(spath, path->m_name);
2117     strcpy(supath, path->u_name); /* this is for the cnid changing */
2118     p = absupath( vol, sdir, supath);
2119     if (!p) {
2120         /* pathname too long */
2121         return AFPERR_PARAM ;
2122     }
2123     
2124     ad_init(&ads, vol);
2125     if (!(adsp = find_adouble( path, &s_of, &ads))) {
2126         return afp_errno;
2127     }
2128
2129     /* ***** from here we may have resource fork open **** */
2130     
2131     /* look for the source cnid. if it doesn't exist, don't worry about
2132      * it. */
2133     sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2134
2135     if (NULL == ( dir = dirlookup( vol, did )) ) {
2136         err = afp_errno; /* was AFPERR_PARAM */
2137         goto err_exchangefile;
2138     }
2139
2140     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2141         err = get_afp_errno(AFPERR_NOOBJ); 
2142         goto err_exchangefile;
2143     }
2144
2145     if ( path_isadir(path) ) {
2146         err = AFPERR_BADTYPE;
2147         goto err_exchangefile;
2148     }
2149
2150     /* FPExchangeFiles is the only call that can return the SameObj
2151      * error */
2152     if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2153         err = AFPERR_SAMEOBJ;
2154         goto err_exchangefile;
2155     }
2156
2157     ad_init(&add, vol);
2158     if (!(addp = find_adouble( path, &d_of, &add))) {
2159         err = afp_errno;
2160         goto err_exchangefile;
2161     }
2162     destst = path->st;
2163
2164     /* they are not on the same device and at least one is open
2165      * FIXME broken for for crossdev and adouble v2
2166      * return an error 
2167     */
2168     crossdev = (srcst.st_dev != destst.st_dev);
2169     if (/* (d_of || s_of)  && */ crossdev) {
2170         err = AFPERR_MISC;
2171         goto err_exchangefile;
2172     }
2173
2174     /* look for destination id. */
2175     upath = path->u_name;
2176     did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2177
2178     /* construct a temp name.
2179      * NOTE: the temp file will be in the dest file's directory. it
2180      * will also be inaccessible from AFP. */
2181     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2182     if (!mktemp(temp)) {
2183         err = AFPERR_MISC;
2184         goto err_exchangefile;
2185     }
2186     
2187     if (crossdev) {
2188         /* FIXME we need to close fork for copy, both s_of and d_of are null */
2189        ad_close(adsp, ADFLAGS_HF);
2190        ad_close(addp, ADFLAGS_HF);
2191     }
2192
2193     /* now, quickly rename the file. we error if we can't. */
2194     if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2195         goto err_exchangefile;
2196     of_rename(vol, s_of, sdir, spath, curdir, temp);
2197
2198     /* rename destination to source */
2199     if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2200         goto err_src_to_tmp;
2201     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2202
2203     /* rename temp to destination */
2204     if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2205         goto err_dest_to_src;
2206     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2207
2208     /* id's need switching. src -> dest and dest -> src. 
2209      * we need to re-stat() if it was a cross device copy.
2210     */
2211     if (sid)
2212         cnid_delete(vol->v_cdb, sid);
2213     if (did)
2214         cnid_delete(vol->v_cdb, did);
2215
2216     if ((did && ( (crossdev && lstat( upath, &srcst) < 0) || 
2217                 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2218        ||
2219        (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2220                 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2221     ) {
2222         switch (errno) {
2223         case EPERM:
2224         case EACCES:
2225             err = AFPERR_ACCESS;
2226             break;
2227         default:
2228             err = AFPERR_PARAM;
2229         }
2230         goto err_temp_to_dest;
2231     }
2232     
2233     /* here we need to reopen if crossdev */
2234     if (sid && ad_setid(addp, destst.st_dev, destst.st_ino,  sid, sdir->d_did, vol->v_stamp)) 
2235     {
2236        ad_flush( addp );
2237     }
2238         
2239     if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino,  did, curdir->d_did, vol->v_stamp)) 
2240     {
2241        ad_flush( adsp );
2242     }
2243
2244     /* change perms, src gets dest perm and vice versa */
2245
2246     uid = geteuid();
2247     gid = getegid();
2248     if (seteuid(0)) {
2249         LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2250         err = AFP_OK; /* ignore error */
2251         goto err_temp_to_dest;
2252     }
2253
2254     /*
2255      * we need to exchange ACL entries as well
2256      */
2257     /* exchange_acls(vol, p, upath); */
2258
2259     path->st = srcst;
2260     path->st_valid = 1;
2261     path->st_errno = 0;
2262     path->m_name = NULL;
2263     path->u_name = upath;
2264
2265     setfilunixmode(vol, path, destst.st_mode);
2266     setfilowner(vol, destst.st_uid, destst.st_gid, path);
2267
2268     path->st = destst;
2269     path->st_valid = 1;
2270     path->st_errno = 0;
2271     path->u_name = p;
2272
2273     setfilunixmode(vol, path, srcst.st_mode);
2274     setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2275
2276     if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2277         LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2278         exit(EXITERR_SYS);
2279     }
2280
2281     err = AFP_OK;
2282     goto err_exchangefile;
2283
2284     /* all this stuff is so that we can unwind a failed operation
2285      * properly. */
2286 err_temp_to_dest:
2287     /* rename dest to temp */
2288     renamefile(vol, -1, upath, temp, temp, adsp);
2289     of_rename(vol, s_of, curdir, upath, curdir, temp);
2290
2291 err_dest_to_src:
2292     /* rename source back to dest */
2293     renamefile(vol, -1, p, upath, path->m_name, addp);
2294     of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2295
2296 err_src_to_tmp:
2297     /* rename temp back to source */
2298     renamefile(vol, -1, temp, p, spath, adsp);
2299     of_rename(vol, s_of, curdir, temp, sdir, spath);
2300
2301 err_exchangefile:
2302     if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2303        ad_close(adsp, ADFLAGS_HF);
2304     }
2305     if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2306        ad_close(addp, ADFLAGS_HF);
2307     }
2308
2309     struct dir *cached;
2310     if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2311         (void)dir_remove(vol, cached);
2312     if ((cached = dircache_search_by_did(vol, did)) != NULL)
2313         (void)dir_remove(vol, cached);
2314
2315     return err;
2316 }