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