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