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