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