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