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