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