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