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