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