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