]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/file.c
Spotlight: use async Tracker SPARQL API
[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, "tdb", flags);
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             ashort &= ~htons(vol->v_ignattr);
382 #if 0
383             /* FIXME do we want a visual clue if the file is read only
384              */
385             struct maccess      ma;
386             accessmode(vol, ".", &ma, dir , NULL);
387             if ((ma.ma_user & AR_UWRITE)) {
388                 accessmode(vol, upath, &ma, dir , st);
389                 if (!(ma.ma_user & AR_UWRITE)) {
390                         ashort |= htons(ATTRBIT_NOWRITE);
391                 }
392             }
393 #endif
394             memcpy(data, &ashort, sizeof( ashort ));
395             data += sizeof( ashort );
396             LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
397                 path->u_name, ntohs(ashort));
398             break;
399
400         case FILPBIT_PDID :
401             memcpy(data, &dir->d_did, sizeof( uint32_t ));
402             data += sizeof( uint32_t );
403             LOG(log_debug, logtype_afpd, "metadata('%s'):     Parent DID: %u",
404                 path->u_name, ntohl(dir->d_did));
405             break;
406
407         case FILPBIT_CDATE :
408             if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
409                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
410             memcpy(data, &aint, sizeof( aint ));
411             data += sizeof( aint );
412             break;
413
414         case FILPBIT_MDATE :
415             if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
416                 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
417                    aint = AD_DATE_FROM_UNIX(st->st_mtime);
418                 }
419             } else {
420                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
421             }
422             memcpy(data, &aint, sizeof( int ));
423             data += sizeof( int );
424             break;
425
426         case FILPBIT_BDATE :
427             if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
428                 aint = AD_DATE_START;
429             memcpy(data, &aint, sizeof( int ));
430             data += sizeof( int );
431             break;
432
433         case FILPBIT_FINFO :
434                 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
435             data += ADEDLEN_FINDERI;
436             break;
437
438         case FILPBIT_LNAME :
439             l_nameoff = data;
440             data += sizeof( uint16_t );
441             break;
442
443         case FILPBIT_SNAME :
444             memset(data, 0, sizeof(uint16_t));
445             data += sizeof( uint16_t );
446             break;
447
448         case FILPBIT_FNUM :
449             memcpy(data, &id, sizeof( id ));
450             data += sizeof( id );
451             LOG(log_debug, logtype_afpd, "metadata('%s'):           CNID: %u",
452                 path->u_name, ntohl(id));
453             break;
454
455         case FILPBIT_DFLEN :
456             if  (st->st_size > 0xffffffff)
457                aint = 0xffffffff;
458             else
459                aint = htonl( st->st_size );
460             memcpy(data, &aint, sizeof( aint ));
461             data += sizeof( aint );
462             break;
463
464         case FILPBIT_RFLEN: {
465             off_t rlen;
466             if (adp) {
467                 if (adp->ad_rlen > 0xffffffff)
468                     aint = 0xffffffff;
469                 else
470                     aint = htonl( adp->ad_rlen);
471             } else {
472                 rlen = ad_reso_size(path->u_name, 0, NULL);
473                 if (rlen > 0xffffffff)
474                     rlen = 0xffffffff;
475                 aint = htonl(rlen);
476             }
477             memcpy(data, &aint, sizeof( aint ));
478             data += sizeof( aint );
479             break;
480         }
481
482             /* Current client needs ProDOS info block for this file.
483                Use simple heuristic and let the Mac "type" string tell
484                us what the PD file code should be.  Everything gets a
485                subtype of 0x0000 unless the original value was hashed
486                to "pXYZ" when we created it.  See IA, Ver 2.
487                <shirsch@adelphia.net> */
488         case FILPBIT_PDINFO :
489             if (obj->afp_version >= 30) { /* UTF8 name */
490                 utf8 = kTextEncodingUTF8;
491                 utf_nameoff = data;
492                 data += sizeof( uint16_t );
493                 aint = 0;
494                 memcpy(data, &aint, sizeof( aint ));
495                 data += sizeof( aint );
496             }
497             else {
498                 if ( adp ) {
499                     memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
500
501                     if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
502                         achar = '\x04';
503                         ashort = 0x0000;
504                     }
505                     else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
506                         achar = '\xff';
507                         ashort = 0x0000;
508                     }
509                     else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
510                         achar = '\xb3';
511                         ashort = 0x0000;
512                     }
513                     else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
514                         achar = '\x00';
515                         ashort = 0x0000;
516                     }
517                     else if ( fdType[0] == 'p' ) {
518                         achar = fdType[1];
519                         ashort = (fdType[2] * 256) + fdType[3];
520                     }
521                     else {
522                         achar = '\x00';
523                         ashort = 0x0000;
524                     }
525                 }
526                 else {
527                     achar = '\x00';
528                     ashort = 0x0000;
529                 }
530
531                 *data++ = achar;
532                 *data++ = 0;
533                 memcpy(data, &ashort, sizeof( ashort ));
534                 data += sizeof( ashort );
535                 memset(data, 0, sizeof( ashort ));
536                 data += sizeof( ashort );
537             }
538             break;
539         case FILPBIT_EXTDFLEN:
540             aint = htonl(st->st_size >> 32);
541             memcpy(data, &aint, sizeof( aint ));
542             data += sizeof( aint );
543             aint = htonl(st->st_size);
544             memcpy(data, &aint, sizeof( aint ));
545             data += sizeof( aint );
546             break;
547         case FILPBIT_EXTRFLEN:
548             if (adp) {
549                 aint = htonl(adp->ad_rlen >> 32);
550                 memcpy(data, &aint, sizeof( aint ));
551                 data += sizeof( aint );
552                 aint = htonl(adp->ad_rlen);
553                 memcpy(data, &aint, sizeof( aint ));
554                 data += sizeof( aint );
555             } else {
556                 int64_t rlen = hton64(ad_reso_size(path->u_name, 0, NULL));
557                 memcpy(data, &rlen, sizeof(rlen));
558                 data += sizeof(rlen);
559             }
560             break;
561         case FILPBIT_UNIXPR :
562             /* accessmode may change st_mode with ACLs */
563             accessmode(obj, vol, upath, &ma, dir , st);
564
565             aint = htonl(st->st_uid);
566             memcpy( data, &aint, sizeof( aint ));
567             data += sizeof( aint );
568             aint = htonl(st->st_gid);
569             memcpy( data, &aint, sizeof( aint ));
570             data += sizeof( aint );
571
572             /* FIXME: ugly hack
573                type == slnk indicates an OSX style symlink, 
574                we have to add S_IFLNK to the mode, otherwise
575                10.3 clients freak out. */
576
577             aint = st->st_mode;
578             if (adp) {
579                 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
580                 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
581                     aint |= S_IFLNK;
582                 }
583             }
584             aint = htonl(aint);
585
586             memcpy( data, &aint, sizeof( aint ));
587             data += sizeof( aint );
588
589             *data++ = ma.ma_user;
590             *data++ = ma.ma_world;
591             *data++ = ma.ma_group;
592             *data++ = ma.ma_owner;
593             break;
594             
595         default :
596             return( AFPERR_BITMAP );
597         }
598         bitmap = bitmap>>1;
599         bit++;
600     }
601     if ( l_nameoff ) {
602         ashort = htons( data - buf );
603         memcpy(l_nameoff, &ashort, sizeof( ashort ));
604         data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
605     }
606     if ( utf_nameoff ) {
607         ashort = htons( data - buf );
608         memcpy(utf_nameoff, &ashort, sizeof( ashort ));
609         data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
610     }
611     *buflen = data - buf;
612     return (AFP_OK);
613 }
614                 
615 /* ----------------------- */
616 int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct path *path,
617                  struct dir *dir, char *buf, size_t *buflen, int in_enumerate)
618 {
619     struct adouble      ad, *adp;
620     int                 opened = 0;
621     int rc;    
622     int flags; /* uninitialized ok */
623
624     LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
625
626     opened = PARAM_NEED_ADP(bitmap);
627     adp = NULL;
628
629     if (opened) {
630         char *upath;
631         /*
632          * Dont check for and resturn open fork attributes when enumerating
633          * This saves a lot of syscalls, the client will hopefully only use the result
634          * in FPGetFileParms where we return the correct value
635          */
636         flags = (!in_enumerate &&(bitmap & (1 << FILPBIT_ATTR))) ? ADFLAGS_CHECK_OF : 0;
637
638         adp = of_ad(vol, path, &ad);
639         upath = path->u_name;
640
641         if ( ad_metadata( upath, flags, adp) < 0 ) {
642             switch (errno) {
643             case EACCES:
644                 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
645                 upath, strerror(errno));
646                 return AFPERR_ACCESS;
647             case EIO:
648                 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
649                 /* fall through */
650             case ENOENT:
651             default:
652                 adp = NULL;
653                 break;
654             }
655         }
656     }
657     rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
658
659     if (opened)
660         ad_close(adp, ADFLAGS_HF | flags);
661
662     return( rc );
663 }
664
665 /* ----------------------------- */
666 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
667 {
668     struct adouble      ad;
669     struct vol          *vol;
670     struct dir          *dir;
671     struct ofork        *of = NULL;
672     char                *path, *upath;
673     int                 creatf, did, openf, retvalue = AFP_OK;
674     uint16_t            vid;
675     struct path         *s_path;
676     
677     *rbuflen = 0;
678     ibuf++;
679     creatf = (unsigned char) *ibuf++;
680
681     memcpy(&vid, ibuf, sizeof( vid ));
682     ibuf += sizeof( vid );
683
684     if (NULL == ( vol = getvolbyvid( vid )) )
685         return( AFPERR_PARAM );
686
687     if (vol->v_flags & AFPVOL_RO)
688         return AFPERR_VLOCK;
689
690     memcpy(&did, ibuf, sizeof( did));
691     ibuf += sizeof( did );
692
693     if (NULL == ( dir = dirlookup( vol, did )) )
694         return afp_errno;
695
696     if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
697         return get_afp_errno(AFPERR_PARAM);
698     if ( *s_path->m_name == '\0' )
699         return( AFPERR_BADTYPE );
700
701     upath = s_path->u_name;
702     ad_init(&ad, vol);
703     
704     /* if upath is deleted we already in trouble anyway */
705     if ((of = of_findname(vol, s_path))) {
706         if (creatf)
707             return AFPERR_BUSY;
708         else
709             return AFPERR_EXIST;
710     }
711
712     if (creatf)
713         openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
714     else
715         /* on a soft create, if the file is open then ad_open won't fail
716            because open syscall is not called */
717         openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
718
719     if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
720         switch ( errno ) {
721         case EROFS:
722             return AFPERR_VLOCK;
723         case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
724             return ( AFPERR_NOOBJ );
725         case EEXIST :
726             return( AFPERR_EXIST );
727         case EACCES :
728             return( AFPERR_ACCESS );
729         case EDQUOT:
730         case ENOSPC :
731             LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
732             return( AFPERR_DFULL );
733         default :
734             return( AFPERR_PARAM );
735         }
736     }
737     if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
738          /* FIXME with hard create on an existing file, we already
739           * corrupted the data file.
740           */
741          netatalk_unlink( upath );
742          ad_close( &ad, ADFLAGS_DF );
743          return AFPERR_ACCESS;
744     }
745
746     path = s_path->m_name;
747     ad_setname(&ad, path);
748
749     struct stat st;
750     if (lstat(upath, &st) != 0) {
751         LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
752             upath, strerror(errno));
753         ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
754         return AFPERR_MISC;
755     }
756
757     cnid_t id;
758     if ((id = get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
759         LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
760         goto createfile_iderr;
761     }
762     (void)ad_setid(&ad, st.st_dev, st.st_ino, id, dir->d_did, vol->v_stamp);
763
764 createfile_iderr:
765     ad_flush(&ad);
766     ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
767     fce_register(obj, FCE_FILE_CREATE, fullpathname(upath), NULL);
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                 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->cnid_db_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->cnid_db_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->cnid_db_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 }