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