]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/file.c
Enhance logging
[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             LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
739             return( AFPERR_DFULL );
740         default :
741             return( AFPERR_PARAM );
742         }
743     }
744     if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
745          /* on noadouble volumes, just creating the data fork is ok */
746          if (vol_noadouble(vol)) {
747              ad_close( adp, ADFLAGS_DF );
748              goto createfile_done;
749          }
750          /* FIXME with hard create on an existing file, we already
751           * corrupted the data file.
752           */
753          netatalk_unlink( upath );
754          ad_close( adp, ADFLAGS_DF );
755          return AFPERR_ACCESS;
756     }
757
758     path = s_path->m_name;
759     ad_setname(adp, path);
760
761     struct stat st;
762     if (lstat(upath, &st) != 0) {
763         LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
764             upath, strerror(errno));
765         ad_close( adp, ADFLAGS_DF|ADFLAGS_HF);
766         return AFPERR_MISC;
767     }
768
769     (void)get_id(vol, adp, &st, dir->d_did, upath, strlen(upath));
770
771     ad_flush( adp);
772
773     fce_register_new_file(s_path);
774
775     ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
776
777 createfile_done:
778     curdir->d_offcnt++;
779
780 #ifdef DROPKLUDGE
781     if (vol->v_flags & AFPVOL_DROPBOX) {
782         retvalue = matchfile2dirperms(upath, vol, did);
783     }
784 #endif /* DROPKLUDGE */
785
786     setvoltime(obj, vol );
787
788     return (retvalue);
789 }
790
791 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
792 {
793     struct vol  *vol;
794     struct dir  *dir;
795     struct path *s_path;
796     int         did, rc;
797     u_int16_t   vid, bitmap;
798
799     *rbuflen = 0;
800     ibuf += 2;
801
802     memcpy(&vid, ibuf, sizeof( vid ));
803     ibuf += sizeof( vid );
804     if (NULL == ( vol = getvolbyvid( vid )) ) {
805         return( AFPERR_PARAM );
806     }
807
808     if (vol->v_flags & AFPVOL_RO)
809         return AFPERR_VLOCK;
810
811     memcpy(&did, ibuf, sizeof( did ));
812     ibuf += sizeof( did );
813     if (NULL == ( dir = dirlookup( vol, did )) ) {
814         return afp_errno; /* was AFPERR_NOOBJ */
815     }
816
817     memcpy(&bitmap, ibuf, sizeof( bitmap ));
818     bitmap = ntohs( bitmap );
819     ibuf += sizeof( bitmap );
820
821     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
822         return get_afp_errno(AFPERR_PARAM);
823     }
824
825     if (path_isadir(s_path)) {
826         return( AFPERR_BADTYPE ); /* it's a directory */
827     }
828
829     if ( s_path->st_errno != 0 ) {
830         return( AFPERR_NOOBJ );
831     }
832
833     if ((u_long)ibuf & 1 ) {
834         ibuf++;
835     }
836
837     if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
838         setvoltime(obj, vol );
839     }
840
841     return( rc );
842 }
843
844 /*
845  * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic  
846  * 
847 */
848 extern struct path Cur_Path;
849
850 int setfilparams(struct vol *vol,
851                  struct path *path, u_int16_t f_bitmap, char *buf )
852 {
853     struct adouble      ad, *adp;
854     struct extmap       *em;
855     int                 bit, isad = 1, err = AFP_OK;
856     char                *upath;
857     u_char              achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
858     u_int16_t           ashort, bshort, oshort;
859     u_int32_t           aint;
860     u_int32_t           upriv;
861     u_int16_t           upriv_bit = 0;
862     
863     struct utimbuf      ut;
864
865     int                 change_mdate = 0;
866     int                 change_parent_mdate = 0;
867     int                 newdate = 0;
868     struct timeval      tv;
869     uid_t               f_uid;
870     gid_t               f_gid;
871     u_int16_t           bitmap = f_bitmap;
872     u_int32_t           cdate,bdate;
873     u_char              finder_buf[32];
874
875 #ifdef DEBUG
876     LOG(log_debug9, logtype_afpd, "begin setfilparams:");
877 #endif /* DEBUG */
878
879     adp = of_ad(vol, path, &ad);
880     upath = path->u_name;
881
882     if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
883         return AFPERR_ACCESS;
884     }
885
886     /* with unix priv maybe we have to change adouble file priv first */
887     bit = 0;
888     while ( bitmap != 0 ) {
889         while (( bitmap & 1 ) == 0 ) {
890             bitmap = bitmap>>1;
891             bit++;
892         }
893         switch(  bit ) {
894         case FILPBIT_ATTR :
895             change_mdate = 1;
896             memcpy(&ashort, buf, sizeof( ashort ));
897             buf += sizeof( ashort );
898             break;
899         case FILPBIT_CDATE :
900             change_mdate = 1;
901             memcpy(&cdate, buf, sizeof(cdate));
902             buf += sizeof( cdate );
903             break;
904         case FILPBIT_MDATE :
905             memcpy(&newdate, buf, sizeof( newdate ));
906             buf += sizeof( newdate );
907             break;
908         case FILPBIT_BDATE :
909             change_mdate = 1;
910             memcpy(&bdate, buf, sizeof( bdate));
911             buf += sizeof( bdate );
912             break;
913         case FILPBIT_FINFO :
914             change_mdate = 1;
915             memcpy(finder_buf, buf, 32 );
916             if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
917             // SLFINFO
918                 int fp;
919                 ssize_t len;
920                 int erc=1;
921                 char buf[PATH_MAX+1];
922                 if ((fp=open(path->u_name,O_RDONLY))>=0){
923                     if ((len=read(fp,buf,PATH_MAX+1))){
924                         if (unlink(path->u_name)==0){
925                             buf[len]=0;
926                             erc = symlink(buf, path->u_name);
927                             if (!erc)
928                                 of_stat(path);
929                         }
930                     }
931                     close(fp);
932                 }
933                 if (erc!=0){
934                     err=AFPERR_BITMAP;
935                     goto setfilparam_done;
936                 }
937             }
938             buf += 32;
939             break;
940         case FILPBIT_UNIXPR :
941             if (!vol_unix_priv(vol)) {
942                 /* this volume doesn't use unix priv */
943                 err = AFPERR_BITMAP;
944                 bitmap = 0;
945                 break;
946             }
947             change_mdate = 1;
948             change_parent_mdate = 1;
949
950             memcpy( &aint, buf, sizeof( aint ));
951             f_uid = ntohl (aint);
952             buf += sizeof( aint );
953             memcpy( &aint, buf, sizeof( aint ));
954             f_gid = ntohl (aint);
955             buf += sizeof( aint );
956             setfilowner(vol, f_uid, f_gid, path);
957
958             memcpy( &upriv, buf, sizeof( upriv ));
959             buf += sizeof( upriv );
960             upriv = ntohl (upriv);
961             if ((upriv & S_IWUSR)) {
962                 setfilunixmode(vol, path, upriv);
963             }
964             else {
965                 /* do it later */
966                 upriv_bit = 1;
967             }
968             break;
969         case FILPBIT_PDINFO :
970             if (afp_version < 30) { /* else it's UTF8 name */
971                 achar = *buf;
972                 buf += 2;
973                 /* Keep special case to support crlf translations */
974                 if ((unsigned int) achar == 0x04) {
975                     fdType = (u_char *)"TEXT";
976                     buf += 2;
977                 } else {
978                     xyy[0] = ( u_char ) 'p';
979                     xyy[1] = achar;
980                     xyy[3] = *buf++;
981                     xyy[2] = *buf++;
982                     fdType = xyy;
983                 }
984                 break;
985             }
986             /* fallthrough */
987         default :
988             err = AFPERR_BITMAP;
989             /* break while loop */
990             bitmap = 0;
991             break;
992         }
993
994         bitmap = bitmap>>1;
995         bit++;
996     }
997
998     /* second try with adouble open 
999     */
1000     if ( ad_open_metadata( upath, 0, O_CREAT, adp) < 0) {
1001         LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
1002         /*
1003          * For some things, we don't need an adouble header:
1004          * - change of modification date
1005          * - UNIX privs (Bug-ID #2863424)
1006          */
1007         if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
1008             LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
1009             return AFPERR_ACCESS;
1010         }
1011         LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
1012         isad = 0;
1013     } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
1014         ad_setname(adp, path->m_name);
1015     }
1016     
1017     bit = 0;
1018     bitmap = f_bitmap;
1019     while ( bitmap != 0 ) {
1020         while (( bitmap & 1 ) == 0 ) {
1021             bitmap = bitmap>>1;
1022             bit++;
1023         }
1024
1025         switch(  bit ) {
1026         case FILPBIT_ATTR :
1027             ad_getattr(adp, &bshort);
1028             oshort = bshort;
1029             if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1030                 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1031             } else {
1032                 bshort &= ~ashort;
1033             }
1034             if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1035                 change_parent_mdate = 1;
1036             ad_setattr(adp, bshort);
1037             break;
1038         case FILPBIT_CDATE :
1039             ad_setdate(adp, AD_DATE_CREATE, cdate);
1040             break;
1041         case FILPBIT_MDATE :
1042             break;
1043         case FILPBIT_BDATE :
1044             ad_setdate(adp, AD_DATE_BACKUP, bdate);
1045             break;
1046         case FILPBIT_FINFO :
1047             if (default_type( ad_entry( adp, ADEID_FINDERI ))
1048                     && ( 
1049                      ((em = getextmap( path->m_name )) &&
1050                       !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1051                       !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1052                      || ((em = getdefextmap()) &&
1053                       !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1054                       !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1055             )) {
1056                 memcpy(finder_buf, ufinderi, 8 );
1057             }
1058             memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1059             break;
1060         case FILPBIT_UNIXPR :
1061             if (upriv_bit) {
1062                 setfilunixmode(vol, path, upriv);
1063             }
1064             break;
1065         case FILPBIT_PDINFO :
1066             if (afp_version < 30) { /* else it's UTF8 name */
1067                 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1068                 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1069                 break;
1070             }
1071             /* fallthrough */
1072         default :
1073             err = AFPERR_BITMAP;
1074             goto setfilparam_done;
1075         }
1076         bitmap = bitmap>>1;
1077         bit++;
1078     }
1079
1080 setfilparam_done:
1081     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1082        newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1083     }
1084     if (newdate) {
1085        if (isad)
1086           ad_setdate(adp, AD_DATE_MODIFY, newdate);
1087        ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1088        utime(upath, &ut);
1089     }
1090
1091     if (isad) {
1092         ad_flush( adp);
1093         ad_close_metadata( adp);
1094
1095     }
1096
1097     if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1098         newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1099         bitmap = 1<<FILPBIT_MDATE;
1100         setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1101     }
1102
1103 #ifdef DEBUG
1104     LOG(log_debug9, logtype_afpd, "end setfilparams:");
1105 #endif /* DEBUG */
1106     return err;
1107 }
1108
1109 /*
1110  * renamefile and copyfile take the old and new unix pathnames
1111  * and the new mac name.
1112  *
1113  * sdir_fd     source dir fd to which src path is relative (for openat et al semantics)
1114  *             passing -1 means this is not used, src path is a full path
1115  * src         the source path 
1116  * dst         the dest filename in current dir
1117  * newname     the dest mac name
1118  * adp         adouble struct of src file, if open, or & zeroed one
1119  *
1120  */
1121 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1122 {
1123     int         rc;
1124
1125     LOG(log_debug, logtype_afpd,
1126         "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1127
1128     if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1129         switch ( errno ) {
1130         case ENOENT :
1131             return( AFPERR_NOOBJ );
1132         case EPERM:
1133         case EACCES :
1134             return( AFPERR_ACCESS );
1135         case EROFS:
1136             return AFPERR_VLOCK;
1137         case EXDEV :                    /* Cross device move -- try copy */
1138            /* NOTE: with open file it's an error because after the copy we will 
1139             * get two files, it's fixable for our process (eg reopen the new file, get the
1140             * locks, and so on. But it doesn't solve the case with a second process
1141             */
1142             if (adp->ad_open_forks) {
1143                 /* FIXME  warning in syslog so admin'd know there's a conflict ?*/
1144                 return AFPERR_OLOCK; /* little lie */
1145             }
1146             if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1147                 /* on error copyfile delete dest */
1148                 return( rc );
1149             }
1150             return deletefile(vol, sdir_fd, src, 0);
1151         default :
1152             return( AFPERR_PARAM );
1153         }
1154     }
1155
1156     if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1157         int err;
1158         
1159         err = errno;        
1160         /* try to undo the data fork rename,
1161          * we know we are on the same device 
1162         */
1163         if (err) {
1164         unix_rename(-1, dst, sdir_fd, src ); 
1165             /* return the first error */
1166             switch ( err) {
1167             case ENOENT :
1168                 return AFPERR_NOOBJ;
1169             case EPERM:
1170             case EACCES :
1171                 return AFPERR_ACCESS ;
1172             case EROFS:
1173                 return AFPERR_VLOCK;
1174             default :
1175                 return AFPERR_PARAM ;
1176             }
1177         }
1178     }
1179
1180     /* don't care if we can't open the newly renamed ressource fork
1181      */
1182     if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1183         ad_setname(adp, newname);
1184         ad_flush( adp );
1185         ad_close( adp, ADFLAGS_HF );
1186     }
1187
1188     return( AFP_OK );
1189 }
1190
1191 /* ---------------- 
1192    convert a Mac long name to an utf8 name,
1193 */
1194 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1195 {
1196 size_t    outlen;
1197
1198     if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1199         return -1;
1200     }
1201     return outlen;
1202 }
1203
1204 /* ---------------- */
1205 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1206 {
1207 char        type = *ibuf;
1208 size_t      plen = 0;
1209 u_int16_t   len16;
1210 u_int32_t   hint;
1211
1212     if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1213         return -1;
1214     }
1215     ibuf++;
1216     switch (type) {
1217     case 2:
1218         if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1219             if (afp_version >= 30) {
1220                 /* convert it to UTF8 
1221                 */
1222                 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1223                    return -1;
1224             }
1225             else {
1226                 strncpy( newname, ibuf, plen );
1227                 newname[ plen ] = '\0';
1228             }
1229             if (strlen(newname) != plen) {
1230                 /* there's \0 in newname, e.g. it's a pathname not
1231                  * only a filename. 
1232                 */
1233                 return -1;
1234             }
1235         }
1236         break;
1237     case 3:
1238         memcpy(&hint, ibuf, sizeof(hint));
1239         ibuf += sizeof(hint);
1240            
1241         memcpy(&len16, ibuf, sizeof(len16));
1242         ibuf += sizeof(len16);
1243         plen = ntohs(len16);
1244         
1245         if (plen) {
1246             if (plen > AFPOBJ_TMPSIZ) {
1247                 return -1;
1248             }
1249             strncpy( newname, ibuf, plen );
1250             newname[ plen ] = '\0';
1251             if (strlen(newname) != plen) {
1252                 return -1;
1253             }
1254         }
1255         break;
1256     }
1257     return plen;
1258 }
1259
1260 /* -----------------------------------
1261 */
1262 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1263 {
1264     struct vol  *s_vol, *d_vol;
1265     struct dir  *dir;
1266     char        *newname, *p, *upath;
1267     struct path *s_path;
1268     u_int32_t   sdid, ddid;
1269     int         err, retvalue = AFP_OK;
1270     u_int16_t   svid, dvid;
1271
1272     struct adouble ad, *adp;
1273     int denyreadset;
1274     
1275     *rbuflen = 0;
1276     ibuf += 2;
1277
1278     memcpy(&svid, ibuf, sizeof( svid ));
1279     ibuf += sizeof( svid );
1280     if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1281         return( AFPERR_PARAM );
1282     }
1283
1284     memcpy(&sdid, ibuf, sizeof( sdid ));
1285     ibuf += sizeof( sdid );
1286     if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1287         return afp_errno;
1288     }
1289
1290     memcpy(&dvid, ibuf, sizeof( dvid ));
1291     ibuf += sizeof( dvid );
1292     memcpy(&ddid, ibuf, sizeof( ddid ));
1293     ibuf += sizeof( ddid );
1294
1295     if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1296         return get_afp_errno(AFPERR_PARAM);
1297     }
1298     if ( path_isadir(s_path) ) {
1299         return( AFPERR_BADTYPE );
1300     }
1301
1302     /* don't allow copies when the file is open.
1303      * XXX: the spec only calls for read/deny write access.
1304      *      however, copyfile doesn't have any of that info,
1305      *      and locks need to stay coherent. as a result,
1306      *      we just balk if the file is opened already. */
1307
1308     adp = of_ad(s_vol, s_path, &ad);
1309
1310     if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1311         return AFPERR_DENYCONF;
1312     }
1313     denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 || 
1314                   getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1315
1316     if (denyreadset) {
1317         retvalue = AFPERR_DENYCONF;
1318         goto copy_exit;
1319     }
1320
1321     newname = obj->newtmp;
1322     strcpy( newname, s_path->m_name );
1323
1324     p = ctoupath( s_vol, curdir, newname );
1325     if (!p) {
1326         retvalue = AFPERR_PARAM;
1327         goto copy_exit;
1328     }
1329
1330 #ifdef FORCE_UIDGID
1331     /* FIXME svid != dvid && dvid's user can't read svid */
1332 #endif
1333     if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1334         retvalue = AFPERR_PARAM;
1335         goto copy_exit;
1336     }
1337
1338     if (d_vol->v_flags & AFPVOL_RO) {
1339         retvalue = AFPERR_VLOCK;
1340         goto copy_exit;
1341     }
1342
1343     if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1344         retvalue = afp_errno;
1345         goto copy_exit;
1346     }
1347
1348     if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1349         retvalue = get_afp_errno(AFPERR_NOOBJ);
1350         goto copy_exit;
1351     }
1352     
1353     if ( *s_path->m_name != '\0' ) {
1354         retvalue =path_error(s_path, AFPERR_NOOBJ);
1355         goto copy_exit;
1356     }
1357
1358     /* one of the handful of places that knows about the path type */
1359     if (copy_path_name(d_vol, newname, ibuf) < 0) {
1360         retvalue = AFPERR_PARAM;
1361         goto copy_exit;
1362     }
1363     /* newname is always only a filename so curdir *is* its
1364      * parent folder
1365     */
1366     if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1367         retvalue =AFPERR_PARAM;
1368         goto copy_exit;
1369     }
1370
1371     if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1372         retvalue = err;
1373         goto copy_exit;
1374     }
1375     curdir->d_offcnt++;
1376
1377 #ifdef DROPKLUDGE
1378     if (vol->v_flags & AFPVOL_DROPBOX) {
1379         retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1380     }
1381 #endif /* DROPKLUDGE */
1382
1383     setvoltime(obj, d_vol );
1384
1385 copy_exit:
1386     ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1387     return( retvalue );
1388 }
1389
1390 /* ----------------------- */
1391 static int copy_all(const int dfd, const void *buf,
1392                                size_t buflen)
1393 {
1394     ssize_t cc;
1395
1396 #ifdef DEBUG
1397     LOG(log_debug9, logtype_afpd, "begin copy_all:");
1398 #endif /* DEBUG */
1399
1400     while (buflen > 0) {
1401         if ((cc = write(dfd, buf, buflen)) < 0) {
1402             switch (errno) {
1403             case EINTR:
1404                 continue;
1405             default:
1406                 return -1;
1407             }
1408         }
1409         buflen -= cc;
1410     }
1411
1412 #ifdef DEBUG
1413     LOG(log_debug9, logtype_afpd, "end copy_all:");
1414 #endif /* DEBUG */
1415
1416     return 0;
1417 }
1418
1419 /* -------------------------- 
1420  * copy only the fork data stream
1421 */
1422 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1423 {
1424     ssize_t cc;
1425     int     err = 0;
1426     char    filebuf[8192];
1427     int     sfd, dfd;
1428     
1429     if (eid == ADEID_DFORK) {
1430         sfd = ad_data_fileno(ads);
1431         dfd = ad_data_fileno(add);
1432     }
1433     else {
1434         sfd = ad_reso_fileno(ads);
1435         dfd = ad_reso_fileno(add);
1436     }        
1437
1438     if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1439         return -1;
1440
1441     if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1442         return -1;
1443         
1444 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1445     /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1446     off_t   offset = 0;
1447     size_t  size;
1448     struct stat         st;
1449     #define BUF 128*1024*1024
1450
1451     if (fstat(sfd, &st) == 0) {
1452         
1453         while (1) {
1454             if ( offset >= st.st_size) {
1455                return 0;
1456             }
1457             size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1458             if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1459                 switch (errno) {
1460                 case ENOSYS:
1461                 case EINVAL:  /* there's no guarantee that all fs support sendfile */
1462                     goto no_sendfile;
1463                 default:
1464                     return -1;
1465                 }
1466             }
1467         }
1468     }
1469     no_sendfile:
1470     lseek(sfd, offset, SEEK_SET);
1471 #endif 
1472
1473     while (1) {
1474         if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1475             if (errno == EINTR)
1476                 continue;
1477             err = -1;
1478             break;
1479         }
1480
1481         if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1482             break;
1483         }
1484     }
1485     return err;
1486 }
1487
1488 /* ----------------------------------
1489  * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1490  * because we are doing it elsewhere.
1491  * currently if newname is NULL then adp is NULL. 
1492  */
1493 int copyfile(const struct vol *s_vol,
1494              const struct vol *d_vol, 
1495              int sfd,
1496              char *src,
1497              char *dst,
1498              char *newname,
1499              struct adouble *adp)
1500 {
1501     struct adouble      ads, add;
1502     int                 err = 0;
1503     int                 ret_err = 0;
1504     int                 adflags;
1505     int                 stat_result;
1506     struct stat         st;
1507     
1508     LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1509         sfd, src, dst, newname);
1510
1511     if (adp == NULL) {
1512         ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options); 
1513         adp = &ads;
1514     }
1515
1516     adflags = ADFLAGS_DF;
1517     if (newname) {
1518         adflags |= ADFLAGS_HF;
1519     }
1520
1521     if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1522         ret_err = errno;
1523         goto done;
1524     }
1525
1526     if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1527         /* no resource fork, don't create one for dst file */
1528         adflags &= ~ADFLAGS_HF;
1529     }
1530
1531     stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1532
1533     if (stat_result < 0) {           
1534       /* unlikely but if fstat fails, the default file mode will be 0666. */
1535       st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1536     }
1537
1538     ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1539     if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1540         ret_err = errno;
1541         ad_close( adp, adflags );
1542         if (EEXIST != ret_err) {
1543             deletefile(d_vol, -1, dst, 0);
1544             goto done;
1545         }
1546         return AFPERR_EXIST;
1547     }
1548     
1549     /*
1550      * XXX if the source and the dest don't use the same resource type it's broken
1551      */
1552     if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1553         /* copy the data fork */
1554         if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1555             if (ad_meta_fileno(adp) != -1)
1556                 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1557         }
1558     }
1559
1560     if (err < 0) {
1561        ret_err = errno;
1562     }
1563
1564     if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1565         /* set the new name in the resource fork */
1566         ad_copy_header(&add, adp);
1567         ad_setname(&add, newname);
1568         ad_flush( &add );
1569     }
1570     ad_close( adp, adflags );
1571
1572     if (ad_close( &add, adflags ) <0) {
1573        ret_err = errno;
1574     } 
1575
1576     if (ret_err) {
1577         deletefile(d_vol, -1, dst, 0);
1578     }
1579     else if (stat_result == 0) {
1580         /* set dest modification date to src date */
1581         struct utimbuf  ut;
1582
1583         ut.actime = ut.modtime = st.st_mtime;
1584         utime(dst, &ut);
1585         /* FIXME netatalk doesn't use resource fork file date
1586          * but maybe we should set its modtime too.
1587         */
1588     }
1589
1590 done:
1591     switch ( ret_err ) {
1592     case 0:
1593         return AFP_OK;
1594     case EDQUOT:
1595     case EFBIG:
1596     case ENOSPC:
1597         LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1598         return AFPERR_DFULL;
1599     case ENOENT:
1600         return AFPERR_NOOBJ;
1601     case EACCES:
1602         return AFPERR_ACCESS;
1603     case EROFS:
1604         return AFPERR_VLOCK;
1605     }
1606     return AFPERR_PARAM;
1607 }
1608
1609
1610 /* -----------------------------------
1611    vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1612    checkAttrib:   1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1613
1614    when deletefile is called we don't have lock on it, file is closed (for us)
1615    untrue if called by renamefile
1616    
1617    ad_open always try to open file RDWR first and ad_lock takes care of
1618    WRITE lock on read only file.
1619 */
1620
1621 static int check_attrib(struct adouble *adp)
1622 {
1623 u_int16_t   bshort = 0;
1624
1625         ad_getattr(adp, &bshort);
1626     /*
1627      * Does kFPDeleteInhibitBit (bit 8) set?
1628      */
1629         if ((bshort & htons(ATTRBIT_NODELETE))) {
1630                 return AFPERR_OLOCK;
1631         }
1632     if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1633         return AFPERR_BUSY;
1634         }
1635         return 0;
1636 }
1637 /* 
1638  * dirfd can be used for unlinkat semantics
1639  */
1640 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1641 {
1642     struct adouble      ad;
1643     struct adouble      *adp = NULL;
1644     int                 adflags, err = AFP_OK;
1645     int                 meta = 0;
1646
1647     LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1648
1649     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1650     if (checkAttrib) {
1651         /* was EACCESS error try to get only metadata */
1652         /* we never want to create a resource fork here, we are going to delete it 
1653          * moreover sometimes deletefile is called with a no existent file and 
1654          * ad_open would create a 0 byte resource fork
1655         */
1656         if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1657             if ((err = check_attrib(&ad))) {
1658                ad_close_metadata(&ad);
1659                return err;
1660             }
1661             meta = 1;
1662         }
1663     }
1664  
1665     /* try to open both forks at once */
1666     adflags = ADFLAGS_DF;
1667     if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1668         switch (errno) {
1669         case ENOENT:
1670             err = AFPERR_NOOBJ;
1671             goto end;
1672         case EACCES: /* maybe it's a file with no write mode for us */
1673             break;   /* was return AFPERR_ACCESS;*/
1674         case EROFS:
1675             err = AFPERR_VLOCK;
1676             goto end;
1677         default:
1678             err = AFPERR_PARAM;
1679             goto end;
1680         }
1681     }
1682     else {
1683         adp = &ad;
1684     }
1685
1686     if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1687         adflags |= ADFLAGS_HF;
1688         /* FIXME we have a pb here because we want to know if a file is open 
1689          * there's a 'priority inversion' if you can't open the ressource fork RW
1690          * you can delete it if it's open because you can't get a write lock.
1691          * 
1692          * ADLOCK_FILELOCK means the whole ressource fork, not only after the 
1693          * metadatas
1694          *
1695          * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1696          */
1697         if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1698             err = AFPERR_BUSY;
1699             goto end;
1700         }
1701     }
1702
1703     if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1704         err = AFPERR_BUSY;
1705     } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1706         cnid_t id;
1707         if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1708             cnid_delete(vol->v_cdb, id);
1709         }
1710     }
1711
1712 end:
1713     if (meta)
1714         ad_close_metadata(&ad);
1715
1716     if (adp)
1717         ad_close( &ad, adflags );  /* ad_close removes locks if any */
1718
1719     return err;
1720 }
1721
1722 /* ------------------------------------ */
1723 /* return a file id */
1724 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1725 {
1726     struct stat         *st;
1727     struct vol          *vol;
1728     struct dir          *dir;
1729     char                *upath;
1730     int                 len;
1731     cnid_t              did, id;
1732     u_short             vid;
1733     struct path         *s_path;
1734
1735     *rbuflen = 0;
1736
1737     ibuf += 2;
1738
1739     memcpy(&vid, ibuf, sizeof(vid));
1740     ibuf += sizeof(vid);
1741
1742     if (NULL == ( vol = getvolbyvid( vid )) ) {
1743         return( AFPERR_PARAM);
1744     }
1745
1746     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1747         return AFPERR_NOOP;
1748     }
1749
1750     if (vol->v_flags & AFPVOL_RO)
1751         return AFPERR_VLOCK;
1752
1753     memcpy(&did, ibuf, sizeof( did ));
1754     ibuf += sizeof(did);
1755
1756     if (NULL == ( dir = dirlookup( vol, did )) ) {
1757         return afp_errno; /* was AFPERR_PARAM */
1758     }
1759
1760     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1761         return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1762     }
1763
1764     if ( path_isadir(s_path) ) {
1765         return( AFPERR_BADTYPE );
1766     }
1767
1768     upath = s_path->u_name;
1769     switch (s_path->st_errno) {
1770         case 0:
1771              break; /* success */
1772         case EPERM:
1773         case EACCES:
1774             return AFPERR_ACCESS;
1775         case ENOENT:
1776             return AFPERR_NOOBJ;
1777         default:
1778             return AFPERR_PARAM;
1779     }
1780     st = &s_path->st;
1781     if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1782         memcpy(rbuf, &id, sizeof(id));
1783         *rbuflen = sizeof(id);
1784         return AFPERR_EXISTID;
1785     }
1786
1787     if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1788         memcpy(rbuf, &id, sizeof(id));
1789         *rbuflen = sizeof(id);
1790         return AFP_OK;
1791     }
1792
1793     return afp_errno;
1794 }
1795
1796 /* ------------------------------- */
1797 struct reenum {
1798     struct vol *vol;
1799     cnid_t     did;
1800 };
1801
1802 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1803 {
1804     struct path   path;
1805     struct reenum *param = data;
1806     struct vol    *vol = param->vol;  
1807     cnid_t        did  = param->did;
1808     cnid_t        aint;
1809     
1810     if ( lstat(de->d_name, &path.st)<0 )
1811         return 0;
1812     
1813     /* update or add to cnid */
1814     aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1815
1816 #if AD_VERSION > AD_VERSION1
1817     if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1818         struct adouble  ad, *adp;
1819
1820         path.st_errno = 0;
1821         path.st_valid = 1;
1822         path.u_name = de->d_name;
1823             
1824         adp = of_ad(vol, &path, &ad);
1825             
1826         if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1827             return 0;
1828         }
1829         if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1830             ad_flush(adp);
1831         }
1832         ad_close_metadata(adp);
1833     }
1834 #endif /* AD_VERSION > AD_VERSION1 */
1835
1836     return 0;
1837 }
1838
1839 /* --------------------
1840  * Ok the db is out of synch with the dir.
1841  * but if it's a deleted file we don't want to do it again and again.
1842 */
1843 static int
1844 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1845 {
1846     int             ret;
1847     struct reenum   data;
1848     struct stat     st;
1849     
1850     if (vol->v_cdb == NULL) {
1851         return -1;
1852     }
1853     
1854     /* FIXME use of_statdir ? */
1855     if (lstat(name, &st)) {
1856         return -1;
1857     }
1858
1859     if (dirreenumerate(dir, &st)) {
1860         /* we already did it once and the dir haven't been modified */
1861         return dir->d_offcnt;
1862     }
1863     
1864     data.vol = vol;
1865     data.did = dir->d_did;
1866     if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1867         setdiroffcnt(curdir, &st,  ret);
1868         dir->d_flags |= DIRF_CNID;
1869     }
1870
1871     return ret;
1872 }
1873
1874 /* ------------------------------
1875    resolve a file id */
1876 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1877 {
1878     struct vol          *vol;
1879     struct dir          *dir;
1880     char                *upath;
1881     struct path         path;
1882     int                 err, retry=0;
1883     size_t              buflen;
1884     cnid_t              id, cnid;
1885     u_int16_t           vid, bitmap;
1886
1887     static char buffer[12 + MAXPATHLEN + 1];
1888     int len = 12 + MAXPATHLEN + 1;
1889
1890     *rbuflen = 0;
1891     ibuf += 2;
1892
1893     memcpy(&vid, ibuf, sizeof(vid));
1894     ibuf += sizeof(vid);
1895
1896     if (NULL == ( vol = getvolbyvid( vid )) ) {
1897         return( AFPERR_PARAM);
1898     }
1899
1900     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1901         return AFPERR_NOOP;
1902     }
1903
1904     memcpy(&id, ibuf, sizeof( id ));
1905     ibuf += sizeof(id);
1906     cnid = id;
1907     
1908     if (!id) {
1909         /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1910         return AFPERR_NOID;
1911     }
1912 retry:
1913     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1914         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1915     }
1916
1917     if (NULL == ( dir = dirlookup( vol, id )) ) {
1918         return AFPERR_NOID; /* idem AFPERR_PARAM */
1919     }
1920     if (movecwd(vol, dir) < 0) {
1921         switch (errno) {
1922         case EACCES:
1923         case EPERM:
1924             return AFPERR_ACCESS;
1925         case ENOENT:
1926             return AFPERR_NOID;
1927         default:
1928             return AFPERR_PARAM;
1929         }
1930     }
1931
1932     memset(&path, 0, sizeof(path));
1933     path.u_name = upath;
1934     if ( of_stat(&path) < 0 ) {
1935 #ifdef ESTALE
1936         /* with nfs and our working directory is deleted */
1937         if (errno == ESTALE) {
1938             errno = ENOENT;
1939         }
1940 #endif  
1941         if ( errno == ENOENT && !retry) {
1942             /* cnid db is out of sync, reenumerate the directory and update ids */
1943             reenumerate_id(vol, ".", dir);
1944             id = cnid;
1945             retry = 1;
1946             goto retry;
1947         }
1948         switch (errno) {
1949         case EACCES:
1950         case EPERM:
1951             return AFPERR_ACCESS;
1952         case ENOENT:
1953             return AFPERR_NOID;
1954         default:
1955             return AFPERR_PARAM;
1956         }
1957     }
1958
1959     /* directories are bad */
1960     if (S_ISDIR(path.st.st_mode)) {
1961         /* OS9 and OSX don't return the same error code  */
1962         return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1963     }
1964
1965     memcpy(&bitmap, ibuf, sizeof(bitmap));
1966     bitmap = ntohs( bitmap );
1967     if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1968         return AFPERR_NOID;
1969     }
1970     path.id = cnid;
1971     if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir, 
1972                             rbuf + sizeof(bitmap), &buflen))) {
1973         return err;
1974     }
1975     *rbuflen = buflen + sizeof(bitmap);
1976     memcpy(rbuf, ibuf, sizeof(bitmap));
1977
1978     return AFP_OK;
1979 }
1980
1981 /* ------------------------------ */
1982 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1983 {
1984     struct stat         st;
1985     struct vol          *vol;
1986     struct dir          *dir;
1987     char                *upath;
1988     int                 err;
1989     cnid_t              id;
1990     cnid_t              fileid;
1991     u_short             vid;
1992     static char buffer[12 + MAXPATHLEN + 1];
1993     int len = 12 + MAXPATHLEN + 1;
1994
1995     *rbuflen = 0;
1996     ibuf += 2;
1997
1998     memcpy(&vid, ibuf, sizeof(vid));
1999     ibuf += sizeof(vid);
2000
2001     if (NULL == ( vol = getvolbyvid( vid )) ) {
2002         return( AFPERR_PARAM);
2003     }
2004
2005     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
2006         return AFPERR_NOOP;
2007     }
2008
2009     if (vol->v_flags & AFPVOL_RO)
2010         return AFPERR_VLOCK;
2011
2012     memcpy(&id, ibuf, sizeof( id ));
2013     ibuf += sizeof(id);
2014     fileid = id;
2015
2016     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
2017         return AFPERR_NOID;
2018     }
2019
2020     if (NULL == ( dir = dirlookup( vol, id )) ) {
2021         if (afp_errno == AFPERR_NOOBJ) {
2022             err = AFPERR_NOOBJ;
2023             goto delete;
2024         }
2025         return( AFPERR_PARAM );
2026     }
2027
2028     err = AFP_OK;
2029     if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
2030         switch (errno) {
2031         case EACCES:
2032         case EPERM:
2033             return AFPERR_ACCESS;
2034 #ifdef ESTALE
2035         case ESTALE:
2036 #endif  
2037         case ENOENT:
2038             /* still try to delete the id */
2039             err = AFPERR_NOOBJ;
2040             break;
2041         default:
2042             return AFPERR_PARAM;
2043         }
2044     }
2045     else if (S_ISDIR(st.st_mode)) /* directories are bad */
2046         return AFPERR_BADTYPE;
2047
2048 delete:
2049     if (cnid_delete(vol->v_cdb, fileid)) {
2050         switch (errno) {
2051         case EROFS:
2052             return AFPERR_VLOCK;
2053         case EPERM:
2054         case EACCES:
2055             return AFPERR_ACCESS;
2056         default:
2057             return AFPERR_PARAM;
2058         }
2059     }
2060
2061     return err;
2062 }
2063
2064 /* ------------------------------ */
2065 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2066 {
2067     int             ret;
2068
2069     if (path->st_errno) {
2070         switch (path->st_errno) {
2071         case ENOENT:
2072             afp_errno = AFPERR_NOID;
2073             break;
2074         case EPERM:
2075         case EACCES:
2076             afp_errno = AFPERR_ACCESS;
2077             break;
2078         default:
2079             afp_errno = AFPERR_PARAM;
2080             break;
2081         }
2082         return NULL;
2083     }
2084     /* we use file_access both for legacy Mac perm and
2085      * for unix privilege, rename will take care of folder perms
2086     */
2087     if (file_access(path, OPENACC_WR ) < 0) {
2088         afp_errno = AFPERR_ACCESS;
2089         return NULL;
2090     }
2091     
2092     if ((*of = of_findname(path))) {
2093         /* reuse struct adouble so it won't break locks */
2094         adp = (*of)->of_ad;
2095     }
2096     else {
2097         ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2098         /* META and HF */
2099         if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2100             /* from AFP spec.
2101              * The user must have the Read & Write privilege for both files in order to use this command.
2102              */
2103             ad_close(adp, ADFLAGS_HF);
2104             afp_errno = AFPERR_ACCESS;
2105             return NULL;
2106         }
2107     }
2108     return adp;
2109 }
2110
2111 #define APPLETEMP ".AppleTempXXXXXX"
2112
2113 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2114 {
2115     struct stat         srcst, destst;
2116     struct vol          *vol;
2117     struct dir          *dir, *sdir;
2118     char                *spath, temp[17], *p;
2119     char                *supath, *upath;
2120     struct path         *path;
2121     int                 err;
2122     struct adouble      ads;
2123     struct adouble      add;
2124     struct adouble      *adsp = NULL;
2125     struct adouble      *addp = NULL;
2126     struct ofork        *s_of = NULL;
2127     struct ofork        *d_of = NULL;
2128     int                 crossdev;
2129     
2130     int                 slen, dlen;
2131     u_int32_t           sid, did;
2132     u_int16_t           vid;
2133
2134     uid_t              uid;
2135     gid_t              gid;
2136
2137     *rbuflen = 0;
2138     ibuf += 2;
2139
2140     memcpy(&vid, ibuf, sizeof(vid));
2141     ibuf += sizeof(vid);
2142
2143     if (NULL == ( vol = getvolbyvid( vid )) ) {
2144         return( AFPERR_PARAM);
2145     }
2146
2147     if ((vol->v_flags & AFPVOL_RO))
2148         return AFPERR_VLOCK;
2149
2150     /* source and destination dids */
2151     memcpy(&sid, ibuf, sizeof(sid));
2152     ibuf += sizeof(sid);
2153     memcpy(&did, ibuf, sizeof(did));
2154     ibuf += sizeof(did);
2155
2156     /* source file */
2157     if (NULL == (dir = dirlookup( vol, sid )) ) {
2158         return afp_errno; /* was AFPERR_PARAM */
2159     }
2160
2161     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2162         return get_afp_errno(AFPERR_NOOBJ); 
2163     }
2164
2165     if ( path_isadir(path) ) {
2166         return AFPERR_BADTYPE;   /* it's a dir */
2167     }
2168
2169     /* save some stuff */
2170     srcst = path->st;
2171     sdir = curdir;
2172     spath = obj->oldtmp;
2173     supath = obj->newtmp;
2174     strcpy(spath, path->m_name);
2175     strcpy(supath, path->u_name); /* this is for the cnid changing */
2176     p = absupath( vol, sdir, supath);
2177     if (!p) {
2178         /* pathname too long */
2179         return AFPERR_PARAM ;
2180     }
2181     
2182     ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2183     if (!(adsp = find_adouble( path, &s_of, &ads))) {
2184         return afp_errno;
2185     }
2186
2187     /* ***** from here we may have resource fork open **** */
2188     
2189     /* look for the source cnid. if it doesn't exist, don't worry about
2190      * it. */
2191     sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2192
2193     if (NULL == ( dir = dirlookup( vol, did )) ) {
2194         err = afp_errno; /* was AFPERR_PARAM */
2195         goto err_exchangefile;
2196     }
2197
2198     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2199         err = get_afp_errno(AFPERR_NOOBJ); 
2200         goto err_exchangefile;
2201     }
2202
2203     if ( path_isadir(path) ) {
2204         err = AFPERR_BADTYPE;
2205         goto err_exchangefile;
2206     }
2207
2208     /* FPExchangeFiles is the only call that can return the SameObj
2209      * error */
2210     if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2211         err = AFPERR_SAMEOBJ;
2212         goto err_exchangefile;
2213     }
2214
2215     ad_init(&add, vol->v_adouble, vol->v_ad_options);
2216     if (!(addp = find_adouble( path, &d_of, &add))) {
2217         err = afp_errno;
2218         goto err_exchangefile;
2219     }
2220     destst = path->st;
2221
2222     /* they are not on the same device and at least one is open
2223      * FIXME broken for for crossdev and adouble v2
2224      * return an error 
2225     */
2226     crossdev = (srcst.st_dev != destst.st_dev);
2227     if (/* (d_of || s_of)  && */ crossdev) {
2228         err = AFPERR_MISC;
2229         goto err_exchangefile;
2230     }
2231
2232     /* look for destination id. */
2233     upath = path->u_name;
2234     did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2235
2236     /* construct a temp name.
2237      * NOTE: the temp file will be in the dest file's directory. it
2238      * will also be inaccessible from AFP. */
2239     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2240     if (!mktemp(temp)) {
2241         err = AFPERR_MISC;
2242         goto err_exchangefile;
2243     }
2244     
2245     if (crossdev) {
2246         /* FIXME we need to close fork for copy, both s_of and d_of are null */
2247        ad_close(adsp, ADFLAGS_HF);
2248        ad_close(addp, ADFLAGS_HF);
2249     }
2250
2251     /* now, quickly rename the file. we error if we can't. */
2252     if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2253         goto err_exchangefile;
2254     of_rename(vol, s_of, sdir, spath, curdir, temp);
2255
2256     /* rename destination to source */
2257     if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2258         goto err_src_to_tmp;
2259     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2260
2261     /* rename temp to destination */
2262     if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2263         goto err_dest_to_src;
2264     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2265
2266     /* id's need switching. src -> dest and dest -> src. 
2267      * we need to re-stat() if it was a cross device copy.
2268     */
2269     if (sid)
2270         cnid_delete(vol->v_cdb, sid);
2271     if (did)
2272         cnid_delete(vol->v_cdb, did);
2273
2274     if ((did && ( (crossdev && lstat( upath, &srcst) < 0) || 
2275                 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2276        ||
2277        (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2278                 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2279     ) {
2280         switch (errno) {
2281         case EPERM:
2282         case EACCES:
2283             err = AFPERR_ACCESS;
2284             break;
2285         default:
2286             err = AFPERR_PARAM;
2287         }
2288         goto err_temp_to_dest;
2289     }
2290     
2291     /* here we need to reopen if crossdev */
2292     if (sid && ad_setid(addp, destst.st_dev, destst.st_ino,  sid, sdir->d_did, vol->v_stamp)) 
2293     {
2294        ad_flush( addp );
2295     }
2296         
2297     if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino,  did, curdir->d_did, vol->v_stamp)) 
2298     {
2299        ad_flush( adsp );
2300     }
2301
2302     /* change perms, src gets dest perm and vice versa */
2303
2304     uid = geteuid();
2305     gid = getegid();
2306     if (seteuid(0)) {
2307         LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2308         err = AFP_OK; /* ignore error */
2309         goto err_temp_to_dest;
2310     }
2311
2312     /*
2313      * we need to exchange ACL entries as well
2314      */
2315     /* exchange_acls(vol, p, upath); */
2316
2317     path->st = srcst;
2318     path->st_valid = 1;
2319     path->st_errno = 0;
2320     path->m_name = NULL;
2321     path->u_name = upath;
2322
2323     setfilunixmode(vol, path, destst.st_mode);
2324     setfilowner(vol, destst.st_uid, destst.st_gid, path);
2325
2326     path->st = destst;
2327     path->st_valid = 1;
2328     path->st_errno = 0;
2329     path->u_name = p;
2330
2331     setfilunixmode(vol, path, srcst.st_mode);
2332     setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2333
2334     if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2335         LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2336         exit(EXITERR_SYS);
2337     }
2338
2339     err = AFP_OK;
2340     goto err_exchangefile;
2341
2342     /* all this stuff is so that we can unwind a failed operation
2343      * properly. */
2344 err_temp_to_dest:
2345     /* rename dest to temp */
2346     renamefile(vol, -1, upath, temp, temp, adsp);
2347     of_rename(vol, s_of, curdir, upath, curdir, temp);
2348
2349 err_dest_to_src:
2350     /* rename source back to dest */
2351     renamefile(vol, -1, p, upath, path->m_name, addp);
2352     of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2353
2354 err_src_to_tmp:
2355     /* rename temp back to source */
2356     renamefile(vol, -1, temp, p, spath, adsp);
2357     of_rename(vol, s_of, curdir, temp, sdir, spath);
2358
2359 err_exchangefile:
2360     if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2361        ad_close(adsp, ADFLAGS_HF);
2362     }
2363     if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2364        ad_close(addp, ADFLAGS_HF);
2365     }
2366
2367     struct dir *cached;
2368     if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2369         (void)dir_remove(vol, cached);
2370     if ((cached = dircache_search_by_did(vol, did)) != NULL)
2371         (void)dir_remove(vol, cached);
2372
2373     return err;
2374 }