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