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