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