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