]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/file.c
Merge remote-tracking branch 'origin/branch-netatalk-3-0' into develop
[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 #include <atalk/spotlight.h>
29
30 #include "directory.h"
31 #include "dircache.h"
32 #include "desktop.h"
33 #include "volume.h"
34 #include "fork.h"
35 #include "file.h"
36 #include "filedir.h"
37 #include "unix.h"
38
39 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
40  * field         bytes        subfield    bytes
41  * 
42  * files:
43  * ioFlFndrInfo  16      ->       type    4  type field
44  *                             creator    4  creator field
45  *                               flags    2  finder flags:
46  *                                           alias, bundle, etc.
47  *                            location    4  location in window
48  *                              folder    2  window that contains file
49  * 
50  * ioFlXFndrInfo 16      ->     iconID    2  icon id
51  *                              unused    6  reserved 
52  *                              script    1  script system
53  *                              xflags    1  reserved
54  *                           commentID    2  comment id
55  *                           putawayID    4  home directory id
56  */
57
58 const u_char ufinderi[ADEDLEN_FINDERI] = {
59                               0, 0, 0, 0, 0, 0, 0, 0,
60                               1, 0, 0, 0, 0, 0, 0, 0,
61                               0, 0, 0, 0, 0, 0, 0, 0,
62                               0, 0, 0, 0, 0, 0, 0, 0
63                           };
64
65 static const u_char old_ufinderi[] = {
66                               'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
67                           };
68
69 /* ---------------------- 
70 */
71 static int default_type(void *finder) 
72 {
73     if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
74         return 1;
75     return 0;
76 }
77
78 /* FIXME path : unix or mac name ? (for now it's unix name ) */
79 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
80 {
81     struct extmap       *em;
82     void                *ad_finder = NULL;
83     int                 chk_ext = 0;
84
85     if (adp)
86         ad_finder = ad_entry(adp, ADEID_FINDERI);
87
88     if (ad_finder) {
89         memcpy(data, ad_finder, ADEDLEN_FINDERI);
90         /* default type ? */
91         if (default_type(ad_finder)) 
92             chk_ext = 1;
93     }
94     else {
95         memcpy(data, ufinderi, ADEDLEN_FINDERI);
96         chk_ext = 1;
97         if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
98             uint16_t ashort;
99             
100             ashort = htons(FINDERINFO_INVISIBLE);
101             memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
102         }
103     }
104
105     if (islink && !vol_syml_opt(vol)) {
106         uint16_t linkflag;
107         memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
108         linkflag |= htons(FINDERINFO_ISALIAS);
109         memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
110         memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4); 
111         memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4); 
112     }
113
114     /** Only enter if no appledouble information and no finder information found. */
115     if (chk_ext && (em = getextmap( upath ))) {
116         memcpy(data, em->em_type, sizeof( em->em_type ));
117         memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
118     }
119
120     return data;
121 }
122
123 /* ---------------------
124 */
125 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8) 
126 {
127     uint32_t   aint;
128     char        *tp = NULL;
129     char        *src = name;
130     aint = strlen( name );
131
132     if (!utf8) {
133         /* want mac name */
134         if (utf8_encoding(vol->v_obj)) {
135             /* but name is an utf8 mac name */
136             char *u, *m;
137            
138             /* global static variable... */
139             tp = strdup(name);
140             if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
141                aint = 0;
142             }
143             else {
144                 aint = strlen(m);
145                 src = m;
146             }
147             
148         }
149         if (aint > MACFILELEN)
150             aint = MACFILELEN;
151         *data++ = aint;
152     }
153     else {
154         uint16_t temp;
155
156         if (aint > UTF8FILELEN_EARLY)  /* FIXME safeguard, anyway if no ascii char it's game over*/
157            aint = UTF8FILELEN_EARLY;
158
159         utf8 = vol->v_kTextEncoding;
160         memcpy(data, &utf8, sizeof(utf8));
161         data += sizeof(utf8);
162         
163         temp = htons(aint);
164         memcpy(data, &temp, sizeof(temp));
165         data += sizeof(temp);
166     }
167
168     memcpy( data, src, aint );
169     data += aint;
170     if (tp) {
171         strcpy(name, tp);
172         free(tp);
173     }
174     return data;
175 }
176
177 /*
178  * FIXME: PDINFO is UTF8 and doesn't need adp
179 */
180 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR)  |\
181                                   (1 << FILPBIT_CDATE) |\
182                                   (1 << FILPBIT_MDATE) |\
183                                   (1 << FILPBIT_BDATE) |\
184                                   (1 << FILPBIT_FINFO) |\
185                                   (1 << FILPBIT_RFLEN) |\
186                                   (1 << FILPBIT_EXTRFLEN) |\
187                                   (1 << FILPBIT_PDINFO) |\
188                                   (1 << FILPBIT_FNUM) |\
189                                   (1 << FILPBIT_UNIXPR)))
190
191 /*!
192  * @brief Get CNID for did/upath args both from database and adouble file
193  *
194  * 1. Get the objects CNID as stored in its adouble file
195  * 2. Get the objects CNID from the database
196  * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
197  * 4. In case 2 and 3 differ, store 3 in the adouble file
198  *
199  * @param vol    (rw) volume
200  * @param adp    (rw) adouble struct of object upath, might be NULL
201  * @param st     (r) stat of upath, must NOT be NULL
202  * @param did    (r) parent CNID of upath
203  * @param upath  (r) name of object
204  * @param len    (r) strlen of upath
205  */
206 uint32_t get_id(struct vol *vol,
207                 struct adouble *adp, 
208                 const struct stat *st,
209                 const cnid_t did,
210                 const char *upath,
211                 const int len) 
212 {
213     static int first = 1;       /* mark if this func is called the first time */
214     uint32_t adcnid;
215     uint32_t dbcnid = CNID_INVALID;
216
217 restart:
218     if (vol->v_cdb != NULL) {
219         /* prime aint with what we think is the cnid, set did to zero for
220            catching moved files */
221         adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
222
223         AFP_CNID_START("cnid_add");
224             dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
225         AFP_CNID_DONE();
226
227             /* Throw errors if cnid_add fails. */
228             if (dbcnid == CNID_INVALID) {
229             switch (errno) {
230             case CNID_ERR_CLOSE: /* the db is closed */
231                 break;
232             case CNID_ERR_PARAM:
233                 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
234                 afp_errno = AFPERR_PARAM;
235                 goto exit;
236             case CNID_ERR_PATH:
237                 afp_errno = AFPERR_PARAM;
238                 goto exit;
239             default:
240                 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
241                 /* we have to do it here for "dbd" because it uses "lazy opening" */
242                 /* In order to not end in a loop somehow with goto restart below  */
243                 /*  */
244                 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
245                     cnid_close(vol->v_cdb);
246                     free(vol->v_cnidscheme);
247                     vol->v_cnidscheme = strdup("tdb");
248
249                     int flags = CNID_FLAG_MEMORY;
250                     if ((vol->v_flags & AFPVOL_NODEV)) {
251                         flags |= CNID_FLAG_NODEV;
252                     }
253                     LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
254                         vol->v_path);
255                     vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
256                     if (vol->v_cdb) {
257                         if (!(vol->v_flags & AFPVOL_TM)) {
258                             vol->v_flags |= AFPVOL_RO;
259                             setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
260                                        "Check server messages for details. Switching to read-only mode.");
261                             kill(getpid(), SIGUSR2);
262                         }
263                         goto restart; /* now try again with the temp CNID db */
264                     } else {
265                         setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
266                                    "Check server messages for details, can't recover from this state!");
267                     }
268                 }
269                 afp_errno = AFPERR_MISC;
270                 goto exit;
271             }
272         }
273         else if (adp && adcnid && (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                 if (ad_flush(adp) != 0)
279                     LOG(log_error, logtype_afpd, "get_id(\"%s\"): can't flush", fullpathname(upath));
280             }
281         }
282     }
283
284 exit:
285     first = 0;
286     return dbcnid;
287 }
288              
289 /* -------------------------- */
290 int getmetadata(const AFPObj *obj,
291                 struct vol *vol,
292                 uint16_t bitmap,
293                 struct path *path, struct dir *dir, 
294                 char *buf, size_t *buflen, struct adouble *adp)
295 {
296     char                *data, *l_nameoff = NULL, *upath;
297     char                *utf_nameoff = NULL;
298     int                 bit = 0;
299     uint32_t            aint;
300     cnid_t              id = 0;
301     uint16_t            ashort;
302     u_char              achar, fdType[4];
303     uint32_t           utf8 = 0;
304     struct stat         *st;
305     struct maccess      ma;
306
307     LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
308
309     upath = path->u_name;
310     st = &path->st;
311     data = buf;
312
313     if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
314          || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding(obj)) /* FIXME should be m_name utf8 filename */
315          || (bitmap & (1 << FILPBIT_FNUM))) {
316         if (!path->id) {
317             bstring fullpath;
318             struct dir *cachedfile;
319             int len = strlen(upath);
320             if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
321                 id = cachedfile->d_did;
322             else {
323                 id = get_id(vol, adp, st, dir->d_did, upath, len);
324
325                 /* Add it to the cache */
326                 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
327                     ntohl(dir->d_did), upath, ntohl(id));
328
329                 /* Get macname from unixname first */
330                 if (path->m_name == NULL) {
331                     if ((path->m_name = utompath(vol, upath, id, utf8_encoding(obj))) == NULL) {
332                         LOG(log_error, logtype_afpd, "getmetadata: utompath error");
333                         return AFPERR_MISC;
334                     }
335                 }
336                 
337                 /* Build fullpath */
338                 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
339                     || (bconchar(fullpath, '/') != BSTR_OK)
340                     || (bcatcstr(fullpath, upath)) != BSTR_OK) {
341                     LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
342                     bdestroy(fullpath);
343                     return AFPERR_MISC;
344                 }
345
346                 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
347                     LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
348                     return AFPERR_MISC;
349                 }
350
351                 if ((dircache_add(vol, cachedfile)) != 0) {
352                     LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
353                     return AFPERR_MISC;
354                 }
355             }
356         } else {
357             id = path->id;
358         }
359
360         if (id == CNID_INVALID)
361             return afp_errno;
362
363         if (!path->m_name) {
364             path->m_name = utompath(vol, upath, id, utf8_encoding(vol->v_obj));
365         }
366     }
367     while ( bitmap != 0 ) {
368         while (( bitmap & 1 ) == 0 ) {
369             bitmap = bitmap>>1;
370             bit++;
371         }
372
373         switch ( bit ) {
374         case FILPBIT_ATTR :
375             if ( adp ) {
376                 ad_getattr(adp, &ashort);
377             } else if (vol_inv_dots(vol) && *upath == '.') {
378                 ashort = htons(ATTRBIT_INVISIBLE);
379             } else
380                 ashort = 0;
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     sl_index_file(path);
768
769     curdir->d_offcnt++;
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                 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1027             } else {
1028                 bshort &= ~ashort;
1029             }
1030             if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1031                 change_parent_mdate = 1;
1032             ad_setattr(adp, bshort);
1033             break;
1034         case FILPBIT_CDATE :
1035             ad_setdate(adp, AD_DATE_CREATE, cdate);
1036             break;
1037         case FILPBIT_MDATE :
1038             break;
1039         case FILPBIT_BDATE :
1040             ad_setdate(adp, AD_DATE_BACKUP, bdate);
1041             break;
1042         case FILPBIT_FINFO :
1043             if (default_type( ad_entry( adp, ADEID_FINDERI ))
1044                     && ( 
1045                      ((em = getextmap( path->m_name )) &&
1046                       !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1047                       !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1048                      || ((em = getdefextmap()) &&
1049                       !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1050                       !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1051             )) {
1052                 memcpy(finder_buf, ufinderi, 8 );
1053             }
1054             memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1055             break;
1056         case FILPBIT_UNIXPR :
1057             if (upriv_bit) {
1058                 setfilunixmode(vol, path, upriv);
1059             }
1060             break;
1061         case FILPBIT_PDINFO :
1062             if (obj->afp_version < 30) { /* else it's UTF8 name */
1063                 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1064                 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1065                 break;
1066             }
1067             /* fallthrough */
1068         default :
1069             err = AFPERR_BITMAP;
1070             goto setfilparam_done;
1071         }
1072         bitmap = bitmap>>1;
1073         bit++;
1074     }
1075
1076 setfilparam_done:
1077     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1078        newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1079     }
1080     if (newdate) {
1081        if (isad)
1082           ad_setdate(adp, AD_DATE_MODIFY, newdate);
1083        ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1084        utime(upath, &ut);
1085     }
1086
1087     if (isad) {
1088         ad_flush(adp);
1089         ad_close(adp, ADFLAGS_HF);
1090     }
1091
1092     if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1093         newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1094         bitmap = 1<<FILPBIT_MDATE;
1095         setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1096     }
1097
1098 #ifdef DEBUG
1099     LOG(log_debug9, logtype_afpd, "end setfilparams:");
1100 #endif /* DEBUG */
1101     return err;
1102 }
1103
1104 /*
1105  * renamefile and copyfile take the old and new unix pathnames
1106  * and the new mac name.
1107  *
1108  * sdir_fd     source dir fd to which src path is relative (for openat et al semantics)
1109  *             passing -1 means this is not used, src path is a full path
1110  * src         the source path 
1111  * dst         the dest filename in current dir
1112  * newname     the dest mac name
1113  * adp         adouble struct of src file, if open, or & zeroed one
1114  *
1115  */
1116 int renamefile(struct vol *vol, struct dir *ddir, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1117 {
1118     int         rc;
1119
1120     LOG(log_debug, logtype_afpd,
1121         "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1122
1123     if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1124         switch ( errno ) {
1125         case ENOENT :
1126             return( AFPERR_NOOBJ );
1127         case EPERM:
1128         case EACCES :
1129             return( AFPERR_ACCESS );
1130         case EROFS:
1131             return AFPERR_VLOCK;
1132         case EXDEV :                    /* Cross device move -- try copy */
1133            /* NOTE: with open file it's an error because after the copy we will 
1134             * get two files, it's fixable for our process (eg reopen the new file, get the
1135             * locks, and so on. But it doesn't solve the case with a second process
1136             */
1137             if (adp->ad_open_forks) {
1138                 /* FIXME  warning in syslog so admin'd know there's a conflict ?*/
1139                 return AFPERR_OLOCK; /* little lie */
1140             }
1141             if (AFP_OK != ( rc = copyfile(vol, vol, ddir, sdir_fd, src, dst, newname, NULL )) ) {
1142                 /* on error copyfile delete dest */
1143                 return( rc );
1144             }
1145             return deletefile(vol, sdir_fd, src, 0);
1146         default :
1147             return( AFPERR_PARAM );
1148         }
1149     }
1150
1151     if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1152         int err;
1153         
1154         err = errno;        
1155         /* try to undo the data fork rename,
1156          * we know we are on the same device 
1157         */
1158         if (err) {
1159         unix_rename(-1, dst, sdir_fd, src ); 
1160             /* return the first error */
1161             switch ( err) {
1162             case ENOENT :
1163                 return AFPERR_NOOBJ;
1164             case EPERM:
1165             case EACCES :
1166                 return AFPERR_ACCESS ;
1167             case EROFS:
1168                 return AFPERR_VLOCK;
1169             default :
1170                 return AFPERR_PARAM ;
1171             }
1172         }
1173     }
1174
1175     /* don't care if we can't open the newly renamed ressource fork */
1176     if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1177         ad_setname(adp, newname);
1178         ad_flush( adp );
1179         ad_close( adp, ADFLAGS_HF );
1180     }
1181
1182     return( AFP_OK );
1183 }
1184
1185 /* ---------------- 
1186    convert a Mac long name to an utf8 name,
1187 */
1188 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1189 {
1190 size_t    outlen;
1191
1192     if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1193         return -1;
1194     }
1195     return outlen;
1196 }
1197
1198 /* ---------------- */
1199 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1200 {
1201 char        type = *ibuf;
1202 size_t      plen = 0;
1203 uint16_t   len16;
1204 uint32_t   hint;
1205
1206     if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1207         return -1;
1208     }
1209     ibuf++;
1210     switch (type) {
1211     case 2:
1212         if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1213             if (vol->v_obj->afp_version >= 30) {
1214                 /* convert it to UTF8 
1215                 */
1216                 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1217                    return -1;
1218             }
1219             else {
1220                 strncpy( newname, ibuf, plen );
1221                 newname[ plen ] = '\0';
1222             }
1223             if (strlen(newname) != plen) {
1224                 /* there's \0 in newname, e.g. it's a pathname not
1225                  * only a filename. 
1226                 */
1227                 return -1;
1228             }
1229         }
1230         break;
1231     case 3:
1232         memcpy(&hint, ibuf, sizeof(hint));
1233         ibuf += sizeof(hint);
1234            
1235         memcpy(&len16, ibuf, sizeof(len16));
1236         ibuf += sizeof(len16);
1237         plen = ntohs(len16);
1238         
1239         if (plen) {
1240             if (plen > AFPOBJ_TMPSIZ) {
1241                 return -1;
1242             }
1243             strncpy( newname, ibuf, plen );
1244             newname[ plen ] = '\0';
1245             if (strlen(newname) != plen) {
1246                 return -1;
1247             }
1248         }
1249         break;
1250     }
1251     return plen;
1252 }
1253
1254 /* -----------------------------------
1255 */
1256 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1257 {
1258     struct vol  *s_vol, *d_vol;
1259     struct dir  *dir;
1260     char        *newname, *p, *upath;
1261     struct path *s_path;
1262     uint32_t    sdid, ddid;
1263     int         err, retvalue = AFP_OK;
1264     uint16_t    svid, dvid;
1265
1266     struct adouble ad, *adp;
1267     int denyreadset;
1268     
1269     *rbuflen = 0;
1270     ibuf += 2;
1271
1272     memcpy(&svid, ibuf, sizeof( svid ));
1273     ibuf += sizeof( svid );
1274     if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1275         return( AFPERR_PARAM );
1276     }
1277
1278     memcpy(&sdid, ibuf, sizeof( sdid ));
1279     ibuf += sizeof( sdid );
1280     if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1281         return afp_errno;
1282     }
1283
1284     memcpy(&dvid, ibuf, sizeof( dvid ));
1285     ibuf += sizeof( dvid );
1286     memcpy(&ddid, ibuf, sizeof( ddid ));
1287     ibuf += sizeof( ddid );
1288
1289     if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1290         return get_afp_errno(AFPERR_PARAM);
1291     }
1292     if ( path_isadir(s_path) ) {
1293         return( AFPERR_BADTYPE );
1294     }
1295
1296     /* don't allow copies when the file is open.
1297      * XXX: the spec only calls for read/deny write access.
1298      *      however, copyfile doesn't have any of that info,
1299      *      and locks need to stay coherent. as a result,
1300      *      we just balk if the file is opened already. */
1301
1302     adp = of_ad(s_vol, s_path, &ad);
1303
1304     if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1305         return AFPERR_DENYCONF;
1306     }
1307 #ifdef HAVE_FSHARE_T
1308     fshare_t shmd;
1309     shmd.f_access = F_RDACC;
1310     shmd.f_deny = F_NODNY;
1311     if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1312         retvalue = AFPERR_DENYCONF;
1313         goto copy_exit;
1314     }
1315     if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1316         retvalue = AFPERR_DENYCONF;
1317         goto copy_exit;
1318     }
1319 #endif
1320     denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 || 
1321                   ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1322
1323     if (denyreadset) {
1324         retvalue = AFPERR_DENYCONF;
1325         goto copy_exit;
1326     }
1327
1328     newname = obj->newtmp;
1329     strcpy( newname, s_path->m_name );
1330
1331     p = ctoupath( s_vol, curdir, newname );
1332     if (!p) {
1333         retvalue = AFPERR_PARAM;
1334         goto copy_exit;
1335     }
1336
1337     if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1338         retvalue = AFPERR_PARAM;
1339         goto copy_exit;
1340     }
1341
1342     if (d_vol->v_flags & AFPVOL_RO) {
1343         retvalue = AFPERR_VLOCK;
1344         goto copy_exit;
1345     }
1346
1347     if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1348         retvalue = afp_errno;
1349         goto copy_exit;
1350     }
1351
1352     if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1353         retvalue = get_afp_errno(AFPERR_NOOBJ);
1354         goto copy_exit;
1355     }
1356     
1357     if ( *s_path->m_name != '\0' ) {
1358         retvalue =path_error(s_path, AFPERR_NOOBJ);
1359         goto copy_exit;
1360     }
1361
1362     /* one of the handful of places that knows about the path type */
1363     if (copy_path_name(d_vol, newname, ibuf) < 0) {
1364         retvalue = AFPERR_PARAM;
1365         goto copy_exit;
1366     }
1367     /* newname is always only a filename so curdir *is* its
1368      * parent folder
1369     */
1370     if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1371         retvalue =AFPERR_PARAM;
1372         goto copy_exit;
1373     }
1374
1375     if ( (err = copyfile(s_vol, d_vol, curdir, -1, p, upath , newname, adp)) < 0 ) {
1376         retvalue = err;
1377         goto copy_exit;
1378     }
1379     curdir->d_offcnt++;
1380
1381     setvoltime(obj, d_vol );
1382
1383 copy_exit:
1384     ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1385     return( retvalue );
1386 }
1387
1388 /* ----------------------------------
1389  * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1390  * because we are doing it elsewhere.
1391  * currently if newname is NULL then adp is NULL. 
1392  */
1393 int copyfile(struct vol *s_vol,
1394              struct vol *d_vol, 
1395              struct dir *d_dir, 
1396              int sfd,
1397              char *src,
1398              char *dst,
1399              char *newname,
1400              struct adouble *adp)
1401 {
1402     struct adouble      ads, add;
1403     int                 err = 0;
1404     int                 ret_err = 0;
1405     int                 adflags;
1406     int                 stat_result;
1407     struct stat         st;
1408     
1409     LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1410         sfd, src, dst, newname);
1411
1412     if (adp == NULL) {
1413         ad_init(&ads, s_vol);
1414         adp = &ads;
1415     }
1416
1417     adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RF | ADFLAGS_NORF;
1418
1419     if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1420         ret_err = errno;
1421         goto done;
1422     }
1423
1424     if (!AD_META_OPEN(adp))
1425         /* no resource fork, don't create one for dst file */
1426         adflags &= ~ADFLAGS_HF;
1427
1428     if (!AD_RSRC_OPEN(adp))
1429         /* no resource fork, don't create one for dst file */
1430         adflags &= ~ADFLAGS_RF;
1431
1432     stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1433
1434     if (stat_result < 0) {           
1435       /* unlikely but if fstat fails, the default file mode will be 0666. */
1436       st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1437     }
1438
1439     ad_init(&add, d_vol);
1440     if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1441         ret_err = errno;
1442         ad_close( adp, adflags );
1443         if (EEXIST != ret_err) {
1444             deletefile(d_vol, -1, dst, 0);
1445             goto done;
1446         }
1447         return AFPERR_EXIST;
1448     }
1449
1450     if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1451         LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1452
1453     if (err == 0)
1454         if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1455             LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1456
1457     if (err < 0)
1458        ret_err = errno;
1459
1460     if (AD_META_OPEN(&add)) {
1461         if (AD_META_OPEN(adp))
1462             ad_copy_header(&add, adp);
1463         ad_setname(&add, dst);
1464         cnid_t id;
1465         struct stat stdest;
1466         if (fstat(ad_meta_fileno(&add), &stdest) != 0) {
1467             ret_err = errno;
1468             goto error;
1469         }
1470         if ((id = get_id(d_vol, &add, &stdest, d_dir->d_did, dst, strlen(dst))) == CNID_INVALID) {
1471             ret_err = EINVAL;
1472             goto error;
1473         }
1474         (void)ad_setid(&add, stdest.st_dev, stdest.st_ino, id, d_dir->d_did, d_vol->v_stamp);
1475         ad_flush(&add);
1476     }
1477
1478 error:
1479     ad_close( adp, adflags );
1480
1481     if (ad_close( &add, adflags ) <0) {
1482        ret_err = errno;
1483     } 
1484
1485     if (ret_err) {
1486         deletefile(d_vol, -1, dst, 0);
1487     }
1488     else if (stat_result == 0) {
1489         /* set dest modification date to src date */
1490         struct utimbuf  ut;
1491
1492         ut.actime = ut.modtime = st.st_mtime;
1493         utime(dst, &ut);
1494         /* FIXME netatalk doesn't use resource fork file date
1495          * but maybe we should set its modtime too.
1496         */
1497     }
1498
1499 done:
1500     switch ( ret_err ) {
1501     case 0:
1502         return AFP_OK;
1503     case EDQUOT:
1504     case EFBIG:
1505     case ENOSPC:
1506         LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1507         return AFPERR_DFULL;
1508     case ENOENT:
1509         return AFPERR_NOOBJ;
1510     case EACCES:
1511         return AFPERR_ACCESS;
1512     case EROFS:
1513         return AFPERR_VLOCK;
1514     }
1515     return AFPERR_PARAM;
1516 }
1517
1518
1519 /* -----------------------------------
1520    vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1521    checkAttrib:   1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1522
1523    when deletefile is called we don't have lock on it, file is closed (for us)
1524    untrue if called by renamefile
1525    
1526    ad_open always try to open file RDWR first and ad_lock takes care of
1527    WRITE lock on read only file.
1528 */
1529
1530 static int check_attrib(struct adouble *adp)
1531 {
1532 uint16_t   bshort = 0;
1533
1534         ad_getattr(adp, &bshort);
1535     /*
1536      * Does kFPDeleteInhibitBit (bit 8) set?
1537      */
1538         if ((bshort & htons(ATTRBIT_NODELETE))) {
1539                 return AFPERR_OLOCK;
1540         }
1541     if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1542         return AFPERR_BUSY;
1543         }
1544         return 0;
1545 }
1546 /* 
1547  * dirfd can be used for unlinkat semantics
1548  */
1549 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1550 {
1551     struct adouble      ad;
1552     struct adouble      *adp = NULL;
1553     int                 adflags, err = AFP_OK;
1554     int                 meta = 0;
1555
1556     LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1557
1558     ad_init(&ad, vol);
1559     if (checkAttrib) {
1560         /* was EACCESS error try to get only metadata */
1561         /* we never want to create a resource fork here, we are going to delete it 
1562          * moreover sometimes deletefile is called with a no existent file and 
1563          * ad_open would create a 0 byte resource fork
1564         */
1565         if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1566             if ((err = check_attrib(&ad))) {
1567                 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1568                return err;
1569             }
1570             meta = 1;
1571         }
1572     }
1573  
1574     /* try to open both forks at once */
1575     adflags = ADFLAGS_DF;
1576     if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1577         switch (errno) {
1578         case ENOENT:
1579             err = AFPERR_NOOBJ;
1580             goto end;
1581         case EACCES: /* maybe it's a file with no write mode for us */
1582             break;   /* was return AFPERR_ACCESS;*/
1583         case EROFS:
1584             err = AFPERR_VLOCK;
1585             goto end;
1586         default:
1587             err = AFPERR_PARAM;
1588             goto end;
1589         }
1590     }
1591     else {
1592         adp = &ad;
1593     }
1594
1595     if ( adp && AD_RSRC_OPEN(adp) ) { /* there's a resource fork */
1596         adflags |= ADFLAGS_RF;
1597         /* FIXME we have a pb here because we want to know if a file is open 
1598          * there's a 'priority inversion' if you can't open the ressource fork RW
1599          * you can delete it if it's open because you can't get a write lock.
1600          * 
1601          * ADLOCK_FILELOCK means the whole ressource fork, not only after the 
1602          * metadatas
1603          *
1604          * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1605          */
1606         if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1607             err = AFPERR_BUSY;
1608             goto end;
1609         }
1610     }
1611
1612     if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1613         LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1614         err = AFPERR_BUSY;
1615     } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1616         cnid_t id;
1617         if (checkAttrib) {
1618             AFP_CNID_START("cnid_get");
1619             id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file));
1620             AFP_CNID_DONE();
1621             if (id) {
1622                 AFP_CNID_START("cnid_delete");
1623                 cnid_delete(vol->v_cdb, id);
1624                 AFP_CNID_DONE();
1625             }
1626         }
1627     }
1628
1629 end:
1630     if (meta)
1631         ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1632
1633     if (adp)
1634         ad_close( &ad, adflags );  /* ad_close removes locks if any */
1635
1636     return err;
1637 }
1638
1639 /* ------------------------------------ */
1640 /* return a file id */
1641 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1642 {
1643     struct stat         *st;
1644     struct vol          *vol;
1645     struct dir          *dir;
1646     char                *upath;
1647     int                 len;
1648     cnid_t              did, id;
1649     u_short             vid;
1650     struct path         *s_path;
1651
1652     *rbuflen = 0;
1653
1654     ibuf += 2;
1655
1656     memcpy(&vid, ibuf, sizeof(vid));
1657     ibuf += sizeof(vid);
1658
1659     if (NULL == ( vol = getvolbyvid( vid )) ) {
1660         return( AFPERR_PARAM);
1661     }
1662
1663     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1664         return AFPERR_NOOP;
1665     }
1666
1667     if (vol->v_flags & AFPVOL_RO)
1668         return AFPERR_VLOCK;
1669
1670     memcpy(&did, ibuf, sizeof( did ));
1671     ibuf += sizeof(did);
1672
1673     if (NULL == ( dir = dirlookup( vol, did )) ) {
1674         return afp_errno; /* was AFPERR_PARAM */
1675     }
1676
1677     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1678         return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1679     }
1680
1681     if ( path_isadir(s_path) ) {
1682         return( AFPERR_BADTYPE );
1683     }
1684
1685     upath = s_path->u_name;
1686     switch (s_path->st_errno) {
1687         case 0:
1688              break; /* success */
1689         case EPERM:
1690         case EACCES:
1691             return AFPERR_ACCESS;
1692         case ENOENT:
1693             return AFPERR_NOOBJ;
1694         default:
1695             return AFPERR_PARAM;
1696     }
1697     st = &s_path->st;
1698     AFP_CNID_START("cnid_lookup");
1699     id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath));
1700     AFP_CNID_DONE();
1701     if (id) {
1702         memcpy(rbuf, &id, sizeof(id));
1703         *rbuflen = sizeof(id);
1704         return AFPERR_EXISTID;
1705     }
1706
1707     if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1708         memcpy(rbuf, &id, sizeof(id));
1709         *rbuflen = sizeof(id);
1710         return AFP_OK;
1711     }
1712
1713     return afp_errno;
1714 }
1715
1716 /* ------------------------------- */
1717 struct reenum {
1718     struct vol *vol;
1719     cnid_t     did;
1720 };
1721
1722 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1723 {
1724     struct path   path;
1725     struct reenum *param = data;
1726     struct vol    *vol = param->vol;  
1727     cnid_t        did  = param->did;
1728     cnid_t        aint;
1729     
1730     if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
1731         return 0;
1732     
1733     /* update or add to cnid */
1734     AFP_CNID_START("cnid_add");
1735     aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1736     AFP_CNID_DONE();
1737
1738     return 0;
1739 }
1740
1741 /* --------------------
1742  * Ok the db is out of synch with the dir.
1743  * but if it's a deleted file we don't want to do it again and again.
1744 */
1745 static int
1746 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1747 {
1748     int             ret;
1749     struct reenum   data;
1750     struct stat     st;
1751     
1752     if (vol->v_cdb == NULL) {
1753         return -1;
1754     }
1755     
1756     /* FIXME use of_statdir ? */
1757     if (ostat(name, &st, vol_syml_opt(vol))) {
1758         return -1;
1759     }
1760
1761     if (dirreenumerate(dir, &st)) {
1762         /* we already did it once and the dir haven't been modified */
1763         return dir->d_offcnt;
1764     }
1765     
1766     data.vol = vol;
1767     data.did = dir->d_did;
1768     if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1769         setdiroffcnt(curdir, &st,  ret);
1770         dir->d_flags |= DIRF_CNID;
1771     }
1772
1773     return ret;
1774 }
1775
1776 /* ------------------------------
1777    resolve a file id */
1778 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1779 {
1780     struct vol          *vol;
1781     struct dir          *dir;
1782     char                *upath;
1783     struct path         path;
1784     int                 err, retry=0;
1785     size_t              buflen;
1786     cnid_t              id, cnid;
1787     uint16_t            vid, bitmap;
1788
1789     static char buffer[12 + MAXPATHLEN + 1];
1790     int len = 12 + MAXPATHLEN + 1;
1791
1792     *rbuflen = 0;
1793     ibuf += 2;
1794
1795     memcpy(&vid, ibuf, sizeof(vid));
1796     ibuf += sizeof(vid);
1797
1798     if (NULL == ( vol = getvolbyvid( vid )) ) {
1799         return( AFPERR_PARAM);
1800     }
1801
1802     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1803         return AFPERR_NOOP;
1804     }
1805
1806     memcpy(&id, ibuf, sizeof( id ));
1807     ibuf += sizeof(id);
1808     cnid = id;
1809     
1810     if (!id) {
1811         /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1812         return AFPERR_NOID;
1813     }
1814 retry:
1815     AFP_CNID_START("cnid_resolve");
1816     upath = cnid_resolve(vol->v_cdb, &id, buffer, len);
1817     AFP_CNID_DONE();
1818     if (upath == NULL) {
1819         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1820     }
1821
1822     if (NULL == ( dir = dirlookup( vol, id )) ) {
1823         return AFPERR_NOID; /* idem AFPERR_PARAM */
1824     }
1825     if (movecwd(vol, dir) < 0) {
1826         switch (errno) {
1827         case EACCES:
1828         case EPERM:
1829             return AFPERR_ACCESS;
1830         case ENOENT:
1831             return AFPERR_NOID;
1832         default:
1833             return AFPERR_PARAM;
1834         }
1835     }
1836
1837     memset(&path, 0, sizeof(path));
1838     path.u_name = upath;
1839     if (of_stat(vol, &path) < 0 ) {
1840 #ifdef ESTALE
1841         /* with nfs and our working directory is deleted */
1842         if (errno == ESTALE) {
1843             errno = ENOENT;
1844         }
1845 #endif  
1846         if ( errno == ENOENT && !retry) {
1847             /* cnid db is out of sync, reenumerate the directory and update ids */
1848             reenumerate_id(vol, ".", dir);
1849             id = cnid;
1850             retry = 1;
1851             goto retry;
1852         }
1853         switch (errno) {
1854         case EACCES:
1855         case EPERM:
1856             return AFPERR_ACCESS;
1857         case ENOENT:
1858             return AFPERR_NOID;
1859         default:
1860             return AFPERR_PARAM;
1861         }
1862     }
1863
1864     /* directories are bad */
1865     if (S_ISDIR(path.st.st_mode)) {
1866         /* OS9 and OSX don't return the same error code  */
1867         return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1868     }
1869
1870     memcpy(&bitmap, ibuf, sizeof(bitmap));
1871     bitmap = ntohs( bitmap );
1872     if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1873         return AFPERR_NOID;
1874     }
1875     path.id = cnid;
1876     if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir, 
1877                                       rbuf + sizeof(bitmap), &buflen, 0))) {
1878         return err;
1879     }
1880     *rbuflen = buflen + sizeof(bitmap);
1881     memcpy(rbuf, ibuf, sizeof(bitmap));
1882
1883     return AFP_OK;
1884 }
1885
1886 /* ------------------------------ */
1887 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1888 {
1889     struct stat         st;
1890     struct vol          *vol;
1891     struct dir          *dir;
1892     char                *upath;
1893     int                 err;
1894     cnid_t              id;
1895     cnid_t              fileid;
1896     u_short             vid;
1897     static char buffer[12 + MAXPATHLEN + 1];
1898     int len = 12 + MAXPATHLEN + 1;
1899
1900     *rbuflen = 0;
1901     ibuf += 2;
1902
1903     memcpy(&vid, ibuf, sizeof(vid));
1904     ibuf += sizeof(vid);
1905
1906     if (NULL == ( vol = getvolbyvid( vid )) ) {
1907         return( AFPERR_PARAM);
1908     }
1909
1910     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1911         return AFPERR_NOOP;
1912     }
1913
1914     if (vol->v_flags & AFPVOL_RO)
1915         return AFPERR_VLOCK;
1916
1917     memcpy(&id, ibuf, sizeof( id ));
1918     ibuf += sizeof(id);
1919     fileid = id;
1920
1921     AFP_CNID_START("cnid_resolve");
1922     upath = cnid_resolve(vol->v_cdb, &id, buffer, len);
1923     AFP_CNID_DONE();
1924         if (upath == NULL) {
1925         return AFPERR_NOID;
1926     }
1927
1928     if (NULL == ( dir = dirlookup( vol, id )) ) {
1929         if (afp_errno == AFPERR_NOOBJ) {
1930             err = AFPERR_NOOBJ;
1931             goto delete;
1932         }
1933         return( AFPERR_PARAM );
1934     }
1935
1936     err = AFP_OK;
1937     if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
1938         switch (errno) {
1939         case EACCES:
1940         case EPERM:
1941             return AFPERR_ACCESS;
1942 #ifdef ESTALE
1943         case ESTALE:
1944 #endif  
1945         case ENOENT:
1946             /* still try to delete the id */
1947             err = AFPERR_NOOBJ;
1948             break;
1949         default:
1950             return AFPERR_PARAM;
1951         }
1952     }
1953     else if (S_ISDIR(st.st_mode)) /* directories are bad */
1954         return AFPERR_BADTYPE;
1955
1956 delete:
1957     AFP_CNID_START("cnid_delete");
1958     if (cnid_delete(vol->v_cdb, fileid)) {
1959         AFP_CNID_DONE();
1960         switch (errno) {
1961         case EROFS:
1962             return AFPERR_VLOCK;
1963         case EPERM:
1964         case EACCES:
1965             return AFPERR_ACCESS;
1966         default:
1967             return AFPERR_PARAM;
1968         }
1969     }
1970     AFP_CNID_DONE();
1971     return err;
1972 }
1973
1974 /* ------------------------------ */
1975 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1976 {
1977     int             ret;
1978
1979     if (path->st_errno) {
1980         switch (path->st_errno) {
1981         case ENOENT:
1982             afp_errno = AFPERR_NOID;
1983             break;
1984         case EPERM:
1985         case EACCES:
1986             afp_errno = AFPERR_ACCESS;
1987             break;
1988         default:
1989             afp_errno = AFPERR_PARAM;
1990             break;
1991         }
1992         return NULL;
1993     }
1994     /* we use file_access both for legacy Mac perm and
1995      * for unix privilege, rename will take care of folder perms
1996     */
1997     if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1998         afp_errno = AFPERR_ACCESS;
1999         return NULL;
2000     }
2001     
2002     if ((*of = of_findname(vol, path))) {
2003         /* reuse struct adouble so it won't break locks */
2004         adp = (*of)->of_ad;
2005     }
2006     else {
2007         ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
2008         /* META and HF */
2009         if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2010             /* from AFP spec.
2011              * The user must have the Read & Write privilege for both files in order to use this command.
2012              */
2013             ad_close(adp, ADFLAGS_HF);
2014             afp_errno = AFPERR_ACCESS;
2015             return NULL;
2016         }
2017     }
2018     return adp;
2019 }
2020
2021 #define APPLETEMP ".AppleTempXXXXXX"
2022
2023 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2024 {
2025     struct stat         srcst, destst;
2026     struct vol          *vol;
2027     struct dir          *dir, *sdir;
2028     char                *spath, temp[17], *p;
2029     char                *supath, *upath;
2030     struct path         *path;
2031     int                 err;
2032     struct adouble      ads;
2033     struct adouble      add;
2034     struct adouble      *adsp = NULL;
2035     struct adouble      *addp = NULL;
2036     struct ofork        *s_of = NULL;
2037     struct ofork        *d_of = NULL;
2038     int                 crossdev;
2039     
2040     int                 slen, dlen;
2041     uint32_t            sid, did;
2042     uint16_t            vid;
2043
2044     *rbuflen = 0;
2045     ibuf += 2;
2046
2047     memcpy(&vid, ibuf, sizeof(vid));
2048     ibuf += sizeof(vid);
2049
2050     if (NULL == ( vol = getvolbyvid( vid )) ) {
2051         return( AFPERR_PARAM);
2052     }
2053
2054     if ((vol->v_flags & AFPVOL_RO))
2055         return AFPERR_VLOCK;
2056
2057     /* source and destination dids */
2058     memcpy(&sid, ibuf, sizeof(sid));
2059     ibuf += sizeof(sid);
2060     memcpy(&did, ibuf, sizeof(did));
2061     ibuf += sizeof(did);
2062
2063     /* source file */
2064     if (NULL == (dir = dirlookup( vol, sid )) ) {
2065         return afp_errno; /* was AFPERR_PARAM */
2066     }
2067
2068     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2069         return get_afp_errno(AFPERR_NOOBJ); 
2070     }
2071
2072     if ( path_isadir(path) ) {
2073         return AFPERR_BADTYPE;   /* it's a dir */
2074     }
2075
2076     /* save some stuff */
2077     srcst = path->st;
2078     sdir = curdir;
2079     spath = obj->oldtmp;
2080     supath = obj->newtmp;
2081     strcpy(spath, path->m_name);
2082     strcpy(supath, path->u_name); /* this is for the cnid changing */
2083     p = absupath( vol, sdir, supath);
2084     if (!p) {
2085         /* pathname too long */
2086         return AFPERR_PARAM ;
2087     }
2088     
2089     ad_init(&ads, vol);
2090     if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2091         return afp_errno;
2092     }
2093
2094     /* ***** from here we may have resource fork open **** */
2095     
2096     /* look for the source cnid. if it doesn't exist, don't worry about
2097      * it. */
2098     AFP_CNID_START("cnid_lookup");
2099     sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2100     AFP_CNID_DONE();
2101
2102     if (NULL == ( dir = dirlookup( vol, did )) ) {
2103         err = afp_errno; /* was AFPERR_PARAM */
2104         goto err_exchangefile;
2105     }
2106
2107     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2108         err = get_afp_errno(AFPERR_NOOBJ); 
2109         goto err_exchangefile;
2110     }
2111
2112     if ( path_isadir(path) ) {
2113         err = AFPERR_BADTYPE;
2114         goto err_exchangefile;
2115     }
2116
2117     /* FPExchangeFiles is the only call that can return the SameObj
2118      * error */
2119     if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2120         err = AFPERR_SAMEOBJ;
2121         goto err_exchangefile;
2122     }
2123
2124     ad_init(&add, vol);
2125     if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2126         err = afp_errno;
2127         goto err_exchangefile;
2128     }
2129     destst = path->st;
2130
2131     /* they are not on the same device and at least one is open
2132      * FIXME broken for for crossdev and adouble v2
2133      * return an error 
2134     */
2135     crossdev = (srcst.st_dev != destst.st_dev);
2136     if (/* (d_of || s_of)  && */ crossdev) {
2137         err = AFPERR_MISC;
2138         goto err_exchangefile;
2139     }
2140
2141     /* look for destination id. */
2142     upath = path->u_name;
2143     AFP_CNID_START("cnid_lookup");
2144     did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2145     AFP_CNID_DONE();
2146
2147     /* construct a temp name.
2148      * NOTE: the temp file will be in the dest file's directory. it
2149      * will also be inaccessible from AFP. */
2150     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2151     int fd;
2152     if ((fd = mkstemp(temp)) == -1) {
2153         err = AFPERR_MISC;
2154         goto err_exchangefile;
2155     }
2156     close(fd);
2157
2158     if (crossdev) {
2159         /* FIXME we need to close fork for copy, both s_of and d_of are null */
2160        ad_close(adsp, ADFLAGS_HF);
2161        ad_close(addp, ADFLAGS_HF);
2162     }
2163
2164     /* now, quickly rename the file. we error if we can't. */
2165     if ((err = renamefile(vol, curdir, -1, p, temp, temp, adsp)) != AFP_OK)
2166         goto err_exchangefile;
2167     of_rename(vol, s_of, sdir, spath, curdir, temp);
2168
2169     /* rename destination to source */
2170     if ((err = renamefile(vol, curdir, -1, upath, p, spath, addp)) != AFP_OK)
2171         goto err_src_to_tmp;
2172     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2173
2174     /* rename temp to destination */
2175     if ((err = renamefile(vol, curdir, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2176         goto err_dest_to_src;
2177     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2178
2179     /* 
2180      * id's need switching. src -> dest and dest -> src. 
2181      * we need to re-stat() if it was a cross device copy.
2182      */
2183     if (sid) {
2184         AFP_CNID_START("cnid_delete");        
2185         cnid_delete(vol->v_cdb, sid);
2186         AFP_CNID_DONE();
2187     }
2188     if (did) {
2189         AFP_CNID_START("cnid_delete");
2190         cnid_delete(vol->v_cdb, did);
2191         AFP_CNID_DONE();
2192     }
2193
2194     if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) || 
2195                 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2196        ||
2197         (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
2198                 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2199     ) {
2200         switch (errno) {
2201         case EPERM:
2202         case EACCES:
2203             err = AFPERR_ACCESS;
2204             break;
2205         default:
2206             err = AFPERR_PARAM;
2207         }
2208         goto err_temp_to_dest;
2209     }
2210     
2211     /* here we need to reopen if crossdev */
2212     if (sid && ad_setid(addp, destst.st_dev, destst.st_ino,  sid, sdir->d_did, vol->v_stamp)) 
2213     {
2214        ad_flush( addp );
2215     }
2216         
2217     if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino,  did, curdir->d_did, vol->v_stamp)) 
2218     {
2219        ad_flush( adsp );
2220     }
2221
2222     /* change perms, src gets dest perm and vice versa */
2223
2224     become_root();
2225
2226     /*
2227      * we need to exchange ACL entries as well
2228      */
2229     /* exchange_acls(vol, p, upath); */
2230
2231     path->st = srcst;
2232     path->st_valid = 1;
2233     path->st_errno = 0;
2234     path->m_name = NULL;
2235     path->u_name = upath;
2236
2237     setfilunixmode(vol, path, destst.st_mode);
2238     setfilowner(vol, destst.st_uid, destst.st_gid, path);
2239
2240     path->st = destst;
2241     path->st_valid = 1;
2242     path->st_errno = 0;
2243     path->u_name = p;
2244
2245     setfilunixmode(vol, path, srcst.st_mode);
2246     setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2247
2248     unbecome_root();
2249
2250     err = AFP_OK;
2251     goto err_exchangefile;
2252
2253     /* all this stuff is so that we can unwind a failed operation
2254      * properly. */
2255 err_temp_to_dest:
2256     /* rename dest to temp */
2257     renamefile(vol, curdir, -1, upath, temp, temp, adsp);
2258     of_rename(vol, s_of, curdir, upath, curdir, temp);
2259
2260 err_dest_to_src:
2261     /* rename source back to dest */
2262     renamefile(vol, curdir, -1, p, upath, path->m_name, addp);
2263     of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2264
2265 err_src_to_tmp:
2266     /* rename temp back to source */
2267     renamefile(vol, curdir, -1, temp, p, spath, adsp);
2268     of_rename(vol, s_of, curdir, temp, sdir, spath);
2269
2270 err_exchangefile:
2271     if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2272        ad_close(adsp, ADFLAGS_HF);
2273     }
2274     if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2275        ad_close(addp, ADFLAGS_HF);
2276     }
2277
2278     struct dir *cached;
2279     if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2280         (void)dir_remove(vol, cached);
2281     if ((cached = dircache_search_by_did(vol, did)) != NULL)
2282         (void)dir_remove(vol, cached);
2283
2284     return err;
2285 }