]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/file.c
Fix build
[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     void                *ad_finder = NULL;
80     int                 chk_ext = 0;
81
82         ad_finder = ad_entry(adp, ADEID_FINDERI);
83
84     if (ad_finder) {
85         memcpy(data, ad_finder, ADEDLEN_FINDERI);
86     }
87     else {
88         memcpy(data, ufinderi, ADEDLEN_FINDERI);
89         if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
90             uint16_t ashort;
91             
92             ashort = htons(FINDERINFO_INVISIBLE);
93             memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
94         }
95     }
96
97     if (islink){
98         uint16_t linkflag;
99         memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
100         linkflag |= htons(FINDERINFO_ISALIAS);
101         memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
102         memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4); 
103         memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4); 
104     }
105
106     return data;
107 }
108
109 /* ---------------------
110 */
111 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8) 
112 {
113     uint32_t   aint;
114     char        *tp = NULL;
115     char        *src = name;
116     aint = strlen( name );
117
118     if (!utf8) {
119         /* want mac name */
120         if (utf8_encoding()) {
121             /* but name is an utf8 mac name */
122             char *u, *m;
123            
124             /* global static variable... */
125             tp = strdup(name);
126             if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
127                aint = 0;
128             }
129             else {
130                 aint = strlen(m);
131                 src = m;
132             }
133             
134         }
135         if (aint > MACFILELEN)
136             aint = MACFILELEN;
137         *data++ = aint;
138     }
139     else {
140         uint16_t temp;
141
142         if (aint > UTF8FILELEN_EARLY)  /* FIXME safeguard, anyway if no ascii char it's game over*/
143            aint = UTF8FILELEN_EARLY;
144
145         utf8 = vol->v_kTextEncoding;
146         memcpy(data, &utf8, sizeof(utf8));
147         data += sizeof(utf8);
148         
149         temp = htons(aint);
150         memcpy(data, &temp, sizeof(temp));
151         data += sizeof(temp);
152     }
153
154     memcpy( data, src, aint );
155     data += aint;
156     if (tp) {
157         strcpy(name, tp);
158         free(tp);
159     }
160     return data;
161 }
162
163 /*
164  * FIXME: PDINFO is UTF8 and doesn't need adp
165 */
166 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR)  |\
167                                   (1 << FILPBIT_CDATE) |\
168                                   (1 << FILPBIT_MDATE) |\
169                                   (1 << FILPBIT_BDATE) |\
170                                   (1 << FILPBIT_FINFO) |\
171                                   (1 << FILPBIT_RFLEN) |\
172                                   (1 << FILPBIT_EXTRFLEN) |\
173                                   (1 << FILPBIT_PDINFO) |\
174                                   (1 << FILPBIT_FNUM) |\
175                                   (1 << FILPBIT_UNIXPR)))
176
177 /*!
178  * @brief Get CNID for did/upath args both from database and adouble file
179  *
180  * 1. Get the objects CNID as stored in its adouble file
181  * 2. Get the objects CNID from the database
182  * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
183  * 4. In case 2 and 3 differ, store 3 in the adouble file
184  *
185  * @param vol    (rw) volume
186  * @param adp    (rw) adouble struct of object upath, might be NULL
187  * @param st     (r) stat of upath, must NOT be NULL
188  * @param did    (r) parent CNID of upath
189  * @param upath  (r) name of object
190  * @param len    (r) strlen of upath
191  */
192 uint32_t get_id(struct vol *vol,
193                 struct adouble *adp, 
194                 const struct stat *st,
195                 const cnid_t did,
196                 const char *upath,
197                 const int len) 
198 {
199     static int first = 1;       /* mark if this func is called the first time */
200     uint32_t adcnid;
201     uint32_t dbcnid = CNID_INVALID;
202
203 restart:
204     if (vol->v_cdb != NULL) {
205         /* prime aint with what we think is the cnid, set did to zero for
206            catching moved files */
207         adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
208
209             dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
210             /* Throw errors if cnid_add fails. */
211             if (dbcnid == CNID_INVALID) {
212             switch (errno) {
213             case CNID_ERR_CLOSE: /* the db is closed */
214                 break;
215             case CNID_ERR_PARAM:
216                 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
217                 afp_errno = AFPERR_PARAM;
218                 goto exit;
219             case CNID_ERR_PATH:
220                 afp_errno = AFPERR_PARAM;
221                 goto exit;
222             default:
223                 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
224                 /* we have to do it here for "dbd" because it uses "lazy opening" */
225                 /* In order to not end in a loop somehow with goto restart below  */
226                 /*  */
227                 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
228                     cnid_close(vol->v_cdb);
229                     free(vol->v_cnidscheme);
230                     vol->v_cnidscheme = strdup("tdb");
231
232                     int flags = CNID_FLAG_MEMORY;
233                     if ((vol->v_flags & AFPVOL_NODEV)) {
234                         flags |= CNID_FLAG_NODEV;
235                     }
236                     LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
237                         vol->v_path);
238                     vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
239                     if (vol->v_cdb) {
240                         /* set ro mode*/
241                         vol->v_flags |= AFPVOL_RO;
242 #ifdef SERVERTEXT
243                         /* kill ourself with SIGUSR2 aka msg pending */
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 #endif
248                         goto restart; /* not try again with the temp CNID db */
249                     } else {
250 #ifdef SERVERTEXT
251                         setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
252                                    "Check server messages for details, can't recover from this state!");
253 #endif
254                     }
255                 }
256                 afp_errno = AFPERR_MISC;
257                 goto exit;
258             }
259         }
260         else if (adp && (adcnid != dbcnid)) { /* 4 */
261             /* Update the ressource fork. For a folder adp is always null */
262             LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
263                 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
264             if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
265                 ad_flush(adp);
266             }
267         }
268     }
269
270 exit:
271     first = 0;
272     return dbcnid;
273 }
274              
275 /* -------------------------- */
276 int getmetadata(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()) /* 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())) == 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());
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 (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(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(struct vol *vol,
592                  uint16_t bitmap,
593                  struct path *path, struct dir *dir, 
594                  char *buf, size_t *buflen )
595 {
596     struct adouble      ad, *adp;
597     int                 opened = 0;
598     int rc;    
599     int flags;
600
601     LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
602
603     opened = PARAM_NEED_ADP(bitmap);
604     adp = NULL;
605
606     if (opened) {
607         char *upath;
608         flags = (bitmap & (1 << FILPBIT_ATTR)) ? ADFLAGS_CHECK_OF : 0;
609
610         adp = of_ad(vol, path, &ad);
611         upath = path->u_name;
612
613         if ( ad_metadata( upath, flags, adp) < 0 ) {
614             switch (errno) {
615             case EACCES:
616                 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
617                 upath, strerror(errno));
618                 return AFPERR_ACCESS;
619             case EIO:
620                 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
621                 /* fall through */
622             case ENOENT:
623             default:
624                 adp = NULL;
625                 break;
626             }
627         }
628     }
629     rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
630     ad_close(adp, ADFLAGS_HF | flags);
631
632     return( rc );
633 }
634
635 /* ----------------------------- */
636 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
637 {
638     struct adouble      ad;
639     struct vol          *vol;
640     struct dir          *dir;
641     struct ofork        *of = NULL;
642     char                *path, *upath;
643     int                 creatf, did, openf, retvalue = AFP_OK;
644     uint16_t            vid;
645     struct path         *s_path;
646     
647     *rbuflen = 0;
648     ibuf++;
649     creatf = (unsigned char) *ibuf++;
650
651     memcpy(&vid, ibuf, sizeof( vid ));
652     ibuf += sizeof( vid );
653
654     if (NULL == ( vol = getvolbyvid( vid )) )
655         return( AFPERR_PARAM );
656
657     if (vol->v_flags & AFPVOL_RO)
658         return AFPERR_VLOCK;
659
660     memcpy(&did, ibuf, sizeof( did));
661     ibuf += sizeof( did );
662
663     if (NULL == ( dir = dirlookup( vol, did )) )
664         return afp_errno;
665
666     if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
667         return get_afp_errno(AFPERR_PARAM);
668     if ( *s_path->m_name == '\0' )
669         return( AFPERR_BADTYPE );
670
671     upath = s_path->u_name;
672     ad_init(&ad, vol);
673     
674     /* if upath is deleted we already in trouble anyway */
675     if ((of = of_findname(s_path))) {
676         if (creatf)
677             return AFPERR_BUSY;
678         else
679             return AFPERR_EXIST;
680     }
681
682     if (creatf)
683         openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
684     else
685         /* on a soft create, if the file is open then ad_open won't fail
686            because open syscall is not called */
687         openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
688
689     if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
690         switch ( errno ) {
691         case EROFS:
692             return AFPERR_VLOCK;
693         case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
694             return ( AFPERR_NOOBJ );
695         case EEXIST :
696             return( AFPERR_EXIST );
697         case EACCES :
698             return( AFPERR_ACCESS );
699         case EDQUOT:
700         case ENOSPC :
701             return( AFPERR_DFULL );
702         default :
703             return( AFPERR_PARAM );
704         }
705     }
706     if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
707          /* FIXME with hard create on an existing file, we already
708           * corrupted the data file.
709           */
710          netatalk_unlink( upath );
711          ad_close( &ad, ADFLAGS_DF );
712          return AFPERR_ACCESS;
713     }
714
715     path = s_path->m_name;
716     ad_setname(&ad, path);
717
718     struct stat st;
719     if (lstat(upath, &st) != 0) {
720         LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
721             upath, strerror(errno));
722         ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
723         return AFPERR_MISC;
724     }
725
726     (void)get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath));
727
728     ad_flush(&ad);
729     ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
730     fce_register_new_file(s_path);
731
732 createfile_done:
733     curdir->d_offcnt++;
734
735     setvoltime(obj, vol );
736
737     return (retvalue);
738 }
739
740 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
741 {
742     struct vol  *vol;
743     struct dir  *dir;
744     struct path *s_path;
745     int         did, rc;
746     uint16_t    vid, bitmap;
747
748     *rbuflen = 0;
749     ibuf += 2;
750
751     memcpy(&vid, ibuf, sizeof( vid ));
752     ibuf += sizeof( vid );
753     if (NULL == ( vol = getvolbyvid( vid )) ) {
754         return( AFPERR_PARAM );
755     }
756
757     if (vol->v_flags & AFPVOL_RO)
758         return AFPERR_VLOCK;
759
760     memcpy(&did, ibuf, sizeof( did ));
761     ibuf += sizeof( did );
762     if (NULL == ( dir = dirlookup( vol, did )) ) {
763         return afp_errno; /* was AFPERR_NOOBJ */
764     }
765
766     memcpy(&bitmap, ibuf, sizeof( bitmap ));
767     bitmap = ntohs( bitmap );
768     ibuf += sizeof( bitmap );
769
770     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
771         return get_afp_errno(AFPERR_PARAM);
772     }
773
774     if (path_isadir(s_path)) {
775         return( AFPERR_BADTYPE ); /* it's a directory */
776     }
777
778     if ( s_path->st_errno != 0 ) {
779         return( AFPERR_NOOBJ );
780     }
781
782     if ((u_long)ibuf & 1 ) {
783         ibuf++;
784     }
785
786     if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
787         setvoltime(obj, vol );
788     }
789
790     return( rc );
791 }
792
793 /*
794  * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic  
795  * 
796 */
797 extern struct path Cur_Path;
798
799 int setfilparams(struct vol *vol,
800                  struct path *path, uint16_t f_bitmap, char *buf )
801 {
802     struct adouble      ad, *adp;
803     struct extmap       *em;
804     int                 bit, isad = 1, err = AFP_OK;
805     char                *upath;
806     u_char              achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
807     uint16_t            ashort, bshort, oshort;
808     uint32_t            aint;
809     uint32_t            upriv;
810     uint16_t           upriv_bit = 0;
811         struct utimbuf  ut;
812     int                 change_mdate = 0;
813     int                 change_parent_mdate = 0;
814     int                 newdate = 0;
815     struct timeval      tv;
816     uid_t               f_uid;
817     gid_t               f_gid;
818     uint16_t           bitmap = f_bitmap;
819     uint32_t           cdate,bdate;
820     u_char              finder_buf[32];
821     int symlinked = 0;
822
823 #ifdef DEBUG
824     LOG(log_debug9, logtype_afpd, "begin setfilparams:");
825 #endif /* DEBUG */
826
827     adp = of_ad(vol, path, &ad);
828     upath = path->u_name;
829
830     if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
831         return AFPERR_ACCESS;
832     }
833
834     /* with unix priv maybe we have to change adouble file priv first */
835     bit = 0;
836     while ( bitmap != 0 ) {
837         while (( bitmap & 1 ) == 0 ) {
838             bitmap = bitmap>>1;
839             bit++;
840         }
841         switch(  bit ) {
842         case FILPBIT_ATTR :
843             change_mdate = 1;
844             memcpy(&ashort, buf, sizeof( ashort ));
845             buf += sizeof( ashort );
846             break;
847         case FILPBIT_CDATE :
848             change_mdate = 1;
849             memcpy(&cdate, buf, sizeof(cdate));
850             buf += sizeof( cdate );
851             break;
852         case FILPBIT_MDATE :
853             memcpy(&newdate, buf, sizeof( newdate ));
854             buf += sizeof( newdate );
855             break;
856         case FILPBIT_BDATE :
857             change_mdate = 1;
858             memcpy(&bdate, buf, sizeof( bdate));
859             buf += sizeof( bdate );
860             break;
861         case FILPBIT_FINFO :
862             change_mdate = 1;
863             memcpy(finder_buf, buf, 32 );
864             if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
865                 int fp;
866                 ssize_t len;
867                 int erc=1;
868                 char buf[PATH_MAX+1];
869                 if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
870                     if ((len = read(fp, buf, PATH_MAX+1))) {
871                         if (unlink(path->u_name) == 0) {
872                             buf[len] = 0;
873                             erc = symlink(buf, path->u_name);
874                             if (!erc)
875                                 of_stat(path);
876                         }
877                     }
878                     close(fp);
879                 }
880                 if (erc != 0) {
881                     err=AFPERR_BITMAP;
882                     goto setfilparam_done;
883                 }
884                 symlinked = 1;
885             }
886             buf += 32;
887             break;
888         case FILPBIT_UNIXPR :
889             if (!vol_unix_priv(vol)) {
890                 /* this volume doesn't use unix priv */
891                 err = AFPERR_BITMAP;
892                 bitmap = 0;
893                 break;
894             }
895             change_mdate = 1;
896             change_parent_mdate = 1;
897
898             memcpy( &aint, buf, sizeof( aint ));
899             f_uid = ntohl (aint);
900             buf += sizeof( aint );
901             memcpy( &aint, buf, sizeof( aint ));
902             f_gid = ntohl (aint);
903             buf += sizeof( aint );
904             setfilowner(vol, f_uid, f_gid, path);
905
906             memcpy( &upriv, buf, sizeof( upriv ));
907             buf += sizeof( upriv );
908             upriv = ntohl (upriv);
909             if ((upriv & S_IWUSR)) {
910                 setfilunixmode(vol, path, upriv);
911             }
912             else {
913                 /* do it later */
914                 upriv_bit = 1;
915             }
916             break;
917         case FILPBIT_PDINFO :
918             if (afp_version < 30) { /* else it's UTF8 name */
919                 achar = *buf;
920                 buf += 2;
921                 /* Keep special case to support crlf translations */
922                 if ((unsigned int) achar == 0x04) {
923                     fdType = (u_char *)"TEXT";
924                     buf += 2;
925                 } else {
926                     xyy[0] = ( u_char ) 'p';
927                     xyy[1] = achar;
928                     xyy[3] = *buf++;
929                     xyy[2] = *buf++;
930                     fdType = xyy;
931                 }
932                 break;
933             }
934             /* fallthrough */
935         default :
936             err = AFPERR_BITMAP;
937             /* break while loop */
938             bitmap = 0;
939             break;
940         }
941
942         bitmap = bitmap>>1;
943         bit++;
944     }
945
946     /* second try with adouble open 
947     */
948     if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
949         LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
950         /*
951          * For some things, we don't need an adouble header:
952          * - change of modification date
953          * - UNIX privs (Bug-ID #2863424)
954          */
955         if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
956             LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
957             return AFPERR_ACCESS;
958         }
959         LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
960         isad = 0;
961     } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
962         ad_setname(adp, path->m_name);
963     }
964     
965     bit = 0;
966     bitmap = f_bitmap;
967     while ( bitmap != 0 ) {
968         while (( bitmap & 1 ) == 0 ) {
969             bitmap = bitmap>>1;
970             bit++;
971         }
972
973         switch(  bit ) {
974         case FILPBIT_ATTR :
975             ad_getattr(adp, &bshort);
976             oshort = bshort;
977             if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
978                 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
979             } else {
980                 bshort &= ~ashort;
981             }
982             if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
983                 change_parent_mdate = 1;
984             ad_setattr(adp, bshort);
985             break;
986         case FILPBIT_CDATE :
987             ad_setdate(adp, AD_DATE_CREATE, cdate);
988             break;
989         case FILPBIT_MDATE :
990             break;
991         case FILPBIT_BDATE :
992             ad_setdate(adp, AD_DATE_BACKUP, bdate);
993             break;
994         case FILPBIT_FINFO :
995             memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
996             break;
997         case FILPBIT_UNIXPR :
998             if (upriv_bit) {
999                 setfilunixmode(vol, path, upriv);
1000             }
1001             break;
1002         case FILPBIT_PDINFO :
1003             if (afp_version < 30) { /* else it's UTF8 name */
1004                 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1005                 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1006                 break;
1007             }
1008             /* fallthrough */
1009         default :
1010             err = AFPERR_BITMAP;
1011             goto setfilparam_done;
1012         }
1013         bitmap = bitmap>>1;
1014         bit++;
1015     }
1016
1017 setfilparam_done:
1018     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1019        newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1020     }
1021     if (newdate) {
1022        if (isad)
1023           ad_setdate(adp, AD_DATE_MODIFY, newdate);
1024        ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1025        utime(upath, &ut);
1026     }
1027
1028     if (isad) {
1029         ad_flush(adp);
1030         ad_close(adp, ADFLAGS_HF);
1031     }
1032
1033     if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1034         newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1035         bitmap = 1<<FILPBIT_MDATE;
1036         setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1037     }
1038
1039 #ifdef DEBUG
1040     LOG(log_debug9, logtype_afpd, "end setfilparams:");
1041 #endif /* DEBUG */
1042     return err;
1043 }
1044
1045 /*
1046  * renamefile and copyfile take the old and new unix pathnames
1047  * and the new mac name.
1048  *
1049  * sdir_fd     source dir fd to which src path is relative (for openat et al semantics)
1050  *             passing -1 means this is not used, src path is a full path
1051  * src         the source path 
1052  * dst         the dest filename in current dir
1053  * newname     the dest mac name
1054  * adp         adouble struct of src file, if open, or & zeroed one
1055  *
1056  */
1057 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1058 {
1059     int         rc;
1060
1061     LOG(log_debug, logtype_afpd,
1062         "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1063
1064     if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1065         switch ( errno ) {
1066         case ENOENT :
1067             return( AFPERR_NOOBJ );
1068         case EPERM:
1069         case EACCES :
1070             return( AFPERR_ACCESS );
1071         case EROFS:
1072             return AFPERR_VLOCK;
1073         case EXDEV :                    /* Cross device move -- try copy */
1074            /* NOTE: with open file it's an error because after the copy we will 
1075             * get two files, it's fixable for our process (eg reopen the new file, get the
1076             * locks, and so on. But it doesn't solve the case with a second process
1077             */
1078             if (adp->ad_open_forks) {
1079                 /* FIXME  warning in syslog so admin'd know there's a conflict ?*/
1080                 return AFPERR_OLOCK; /* little lie */
1081             }
1082             if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1083                 /* on error copyfile delete dest */
1084                 return( rc );
1085             }
1086             return deletefile(vol, sdir_fd, src, 0);
1087         default :
1088             return( AFPERR_PARAM );
1089         }
1090     }
1091
1092     if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1093         int err;
1094         
1095         err = errno;        
1096         /* try to undo the data fork rename,
1097          * we know we are on the same device 
1098         */
1099         if (err) {
1100         unix_rename(-1, dst, sdir_fd, src ); 
1101             /* return the first error */
1102             switch ( err) {
1103             case ENOENT :
1104                 return AFPERR_NOOBJ;
1105             case EPERM:
1106             case EACCES :
1107                 return AFPERR_ACCESS ;
1108             case EROFS:
1109                 return AFPERR_VLOCK;
1110             default :
1111                 return AFPERR_PARAM ;
1112             }
1113         }
1114     }
1115
1116     /* don't care if we can't open the newly renamed ressource fork */
1117     if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1118         ad_setname(adp, newname);
1119         ad_flush( adp );
1120         ad_close( adp, ADFLAGS_HF );
1121     }
1122
1123     return( AFP_OK );
1124 }
1125
1126 /* ---------------- 
1127    convert a Mac long name to an utf8 name,
1128 */
1129 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1130 {
1131 size_t    outlen;
1132
1133     if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1134         return -1;
1135     }
1136     return outlen;
1137 }
1138
1139 /* ---------------- */
1140 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1141 {
1142 char        type = *ibuf;
1143 size_t      plen = 0;
1144 uint16_t   len16;
1145 uint32_t   hint;
1146
1147     if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1148         return -1;
1149     }
1150     ibuf++;
1151     switch (type) {
1152     case 2:
1153         if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1154             if (afp_version >= 30) {
1155                 /* convert it to UTF8 
1156                 */
1157                 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1158                    return -1;
1159             }
1160             else {
1161                 strncpy( newname, ibuf, plen );
1162                 newname[ plen ] = '\0';
1163             }
1164             if (strlen(newname) != plen) {
1165                 /* there's \0 in newname, e.g. it's a pathname not
1166                  * only a filename. 
1167                 */
1168                 return -1;
1169             }
1170         }
1171         break;
1172     case 3:
1173         memcpy(&hint, ibuf, sizeof(hint));
1174         ibuf += sizeof(hint);
1175            
1176         memcpy(&len16, ibuf, sizeof(len16));
1177         ibuf += sizeof(len16);
1178         plen = ntohs(len16);
1179         
1180         if (plen) {
1181             if (plen > AFPOBJ_TMPSIZ) {
1182                 return -1;
1183             }
1184             strncpy( newname, ibuf, plen );
1185             newname[ plen ] = '\0';
1186             if (strlen(newname) != plen) {
1187                 return -1;
1188             }
1189         }
1190         break;
1191     }
1192     return plen;
1193 }
1194
1195 /* -----------------------------------
1196 */
1197 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1198 {
1199     struct vol  *s_vol, *d_vol;
1200     struct dir  *dir;
1201     char        *newname, *p, *upath;
1202     struct path *s_path;
1203     uint32_t    sdid, ddid;
1204     int         err, retvalue = AFP_OK;
1205     uint16_t    svid, dvid;
1206
1207     struct adouble ad, *adp;
1208     int denyreadset;
1209     
1210     *rbuflen = 0;
1211     ibuf += 2;
1212
1213     memcpy(&svid, ibuf, sizeof( svid ));
1214     ibuf += sizeof( svid );
1215     if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1216         return( AFPERR_PARAM );
1217     }
1218
1219     memcpy(&sdid, ibuf, sizeof( sdid ));
1220     ibuf += sizeof( sdid );
1221     if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1222         return afp_errno;
1223     }
1224
1225     memcpy(&dvid, ibuf, sizeof( dvid ));
1226     ibuf += sizeof( dvid );
1227     memcpy(&ddid, ibuf, sizeof( ddid ));
1228     ibuf += sizeof( ddid );
1229
1230     if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1231         return get_afp_errno(AFPERR_PARAM);
1232     }
1233     if ( path_isadir(s_path) ) {
1234         return( AFPERR_BADTYPE );
1235     }
1236
1237     /* don't allow copies when the file is open.
1238      * XXX: the spec only calls for read/deny write access.
1239      *      however, copyfile doesn't have any of that info,
1240      *      and locks need to stay coherent. as a result,
1241      *      we just balk if the file is opened already. */
1242
1243     adp = of_ad(s_vol, s_path, &ad);
1244
1245     if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1246         return AFPERR_DENYCONF;
1247     }
1248     denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 || 
1249                   ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1250
1251     if (denyreadset) {
1252         retvalue = AFPERR_DENYCONF;
1253         goto copy_exit;
1254     }
1255
1256     newname = obj->newtmp;
1257     strcpy( newname, s_path->m_name );
1258
1259     p = ctoupath( s_vol, curdir, newname );
1260     if (!p) {
1261         retvalue = AFPERR_PARAM;
1262         goto copy_exit;
1263     }
1264
1265     if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1266         retvalue = AFPERR_PARAM;
1267         goto copy_exit;
1268     }
1269
1270     if (d_vol->v_flags & AFPVOL_RO) {
1271         retvalue = AFPERR_VLOCK;
1272         goto copy_exit;
1273     }
1274
1275     if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1276         retvalue = afp_errno;
1277         goto copy_exit;
1278     }
1279
1280     if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1281         retvalue = get_afp_errno(AFPERR_NOOBJ);
1282         goto copy_exit;
1283     }
1284     
1285     if ( *s_path->m_name != '\0' ) {
1286         retvalue =path_error(s_path, AFPERR_NOOBJ);
1287         goto copy_exit;
1288     }
1289
1290     /* one of the handful of places that knows about the path type */
1291     if (copy_path_name(d_vol, newname, ibuf) < 0) {
1292         retvalue = AFPERR_PARAM;
1293         goto copy_exit;
1294     }
1295     /* newname is always only a filename so curdir *is* its
1296      * parent folder
1297     */
1298     if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1299         retvalue =AFPERR_PARAM;
1300         goto copy_exit;
1301     }
1302
1303     if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1304         retvalue = err;
1305         goto copy_exit;
1306     }
1307     curdir->d_offcnt++;
1308
1309     setvoltime(obj, d_vol );
1310
1311 copy_exit:
1312     ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1313     return( retvalue );
1314 }
1315
1316 /* ----------------------------------
1317  * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1318  * because we are doing it elsewhere.
1319  * currently if newname is NULL then adp is NULL. 
1320  */
1321 int copyfile(const struct vol *s_vol,
1322              const struct vol *d_vol, 
1323              int sfd,
1324              char *src,
1325              char *dst,
1326              char *newname,
1327              struct adouble *adp)
1328 {
1329     struct adouble      ads, add;
1330     int                 err = 0;
1331     int                 ret_err = 0;
1332     int                 adflags;
1333     int                 stat_result;
1334     struct stat         st;
1335     
1336     LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1337         sfd, src, dst, newname);
1338
1339     if (adp == NULL) {
1340         ad_init(&ads, s_vol);
1341         adp = &ads;
1342     }
1343
1344     adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1345
1346     if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1347         ret_err = errno;
1348         goto done;
1349     }
1350
1351     if (!AD_RSRC_OPEN(adp))
1352         /* no resource fork, don't create one for dst file */
1353         adflags &= ~ADFLAGS_RF;
1354
1355     stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1356
1357     if (stat_result < 0) {           
1358       /* unlikely but if fstat fails, the default file mode will be 0666. */
1359       st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1360     }
1361
1362     ad_init(&add, d_vol);
1363     if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1364         ret_err = errno;
1365         ad_close( adp, adflags );
1366         if (EEXIST != ret_err) {
1367             deletefile(d_vol, -1, dst, 0);
1368             goto done;
1369         }
1370         return AFPERR_EXIST;
1371     }
1372
1373     if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1374         LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1375
1376     if (err == 0)
1377         if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1378             LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1379
1380     if (err < 0)
1381        ret_err = errno;
1382
1383     if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1384         /* set the new name in the resource fork */
1385         ad_copy_header(&add, adp);
1386         ad_setname(&add, newname);
1387         ad_flush( &add );
1388     }
1389     ad_close( adp, adflags );
1390
1391     if (ad_close( &add, adflags ) <0) {
1392        ret_err = errno;
1393     } 
1394
1395     if (ret_err) {
1396         deletefile(d_vol, -1, dst, 0);
1397     }
1398     else if (stat_result == 0) {
1399         /* set dest modification date to src date */
1400         struct utimbuf  ut;
1401
1402         ut.actime = ut.modtime = st.st_mtime;
1403         utime(dst, &ut);
1404         /* FIXME netatalk doesn't use resource fork file date
1405          * but maybe we should set its modtime too.
1406         */
1407     }
1408
1409 done:
1410     switch ( ret_err ) {
1411     case 0:
1412         return AFP_OK;
1413     case EDQUOT:
1414     case EFBIG:
1415     case ENOSPC:
1416         return AFPERR_DFULL;
1417     case ENOENT:
1418         return AFPERR_NOOBJ;
1419     case EACCES:
1420         return AFPERR_ACCESS;
1421     case EROFS:
1422         return AFPERR_VLOCK;
1423     }
1424     return AFPERR_PARAM;
1425 }
1426
1427
1428 /* -----------------------------------
1429    vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1430    checkAttrib:   1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1431
1432    when deletefile is called we don't have lock on it, file is closed (for us)
1433    untrue if called by renamefile
1434    
1435    ad_open always try to open file RDWR first and ad_lock takes care of
1436    WRITE lock on read only file.
1437 */
1438
1439 static int check_attrib(struct adouble *adp)
1440 {
1441 uint16_t   bshort = 0;
1442
1443         ad_getattr(adp, &bshort);
1444     /*
1445      * Does kFPDeleteInhibitBit (bit 8) set?
1446      */
1447         if ((bshort & htons(ATTRBIT_NODELETE))) {
1448                 return AFPERR_OLOCK;
1449         }
1450     if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1451         return AFPERR_BUSY;
1452         }
1453         return 0;
1454 }
1455 /* 
1456  * dirfd can be used for unlinkat semantics
1457  */
1458 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1459 {
1460     struct adouble      ad;
1461     struct adouble      *adp = NULL;
1462     int                 adflags, err = AFP_OK;
1463     int                 meta = 0;
1464
1465     LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1466
1467     ad_init(&ad, vol);
1468     if (checkAttrib) {
1469         /* was EACCESS error try to get only metadata */
1470         /* we never want to create a resource fork here, we are going to delete it 
1471          * moreover sometimes deletefile is called with a no existent file and 
1472          * ad_open would create a 0 byte resource fork
1473         */
1474         if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1475             if ((err = check_attrib(&ad))) {
1476                 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1477                return err;
1478             }
1479             meta = 1;
1480         }
1481     }
1482  
1483     /* try to open both forks at once */
1484     adflags = ADFLAGS_DF;
1485     if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1486         switch (errno) {
1487         case ENOENT:
1488             err = AFPERR_NOOBJ;
1489             goto end;
1490         case EACCES: /* maybe it's a file with no write mode for us */
1491             break;   /* was return AFPERR_ACCESS;*/
1492         case EROFS:
1493             err = AFPERR_VLOCK;
1494             goto end;
1495         default:
1496             err = AFPERR_PARAM;
1497             goto end;
1498         }
1499     }
1500     else {
1501         adp = &ad;
1502     }
1503
1504     if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1505         adflags |= ADFLAGS_RF;
1506         /* FIXME we have a pb here because we want to know if a file is open 
1507          * there's a 'priority inversion' if you can't open the ressource fork RW
1508          * you can delete it if it's open because you can't get a write lock.
1509          * 
1510          * ADLOCK_FILELOCK means the whole ressource fork, not only after the 
1511          * metadatas
1512          *
1513          * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1514          */
1515         if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1516             err = AFPERR_BUSY;
1517             goto end;
1518         }
1519     }
1520
1521     if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1522         LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1523         err = AFPERR_BUSY;
1524     } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1525         cnid_t id;
1526         if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1527             cnid_delete(vol->v_cdb, id);
1528         }
1529     }
1530
1531 end:
1532     if (meta)
1533         ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1534
1535     if (adp)
1536         ad_close( &ad, adflags );  /* ad_close removes locks if any */
1537
1538     return err;
1539 }
1540
1541 /* ------------------------------------ */
1542 /* return a file id */
1543 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1544 {
1545     struct stat         *st;
1546     struct vol          *vol;
1547     struct dir          *dir;
1548     char                *upath;
1549     int                 len;
1550     cnid_t              did, id;
1551     u_short             vid;
1552     struct path         *s_path;
1553
1554     *rbuflen = 0;
1555
1556     ibuf += 2;
1557
1558     memcpy(&vid, ibuf, sizeof(vid));
1559     ibuf += sizeof(vid);
1560
1561     if (NULL == ( vol = getvolbyvid( vid )) ) {
1562         return( AFPERR_PARAM);
1563     }
1564
1565     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1566         return AFPERR_NOOP;
1567     }
1568
1569     if (vol->v_flags & AFPVOL_RO)
1570         return AFPERR_VLOCK;
1571
1572     memcpy(&did, ibuf, sizeof( did ));
1573     ibuf += sizeof(did);
1574
1575     if (NULL == ( dir = dirlookup( vol, did )) ) {
1576         return afp_errno; /* was AFPERR_PARAM */
1577     }
1578
1579     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1580         return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1581     }
1582
1583     if ( path_isadir(s_path) ) {
1584         return( AFPERR_BADTYPE );
1585     }
1586
1587     upath = s_path->u_name;
1588     switch (s_path->st_errno) {
1589         case 0:
1590              break; /* success */
1591         case EPERM:
1592         case EACCES:
1593             return AFPERR_ACCESS;
1594         case ENOENT:
1595             return AFPERR_NOOBJ;
1596         default:
1597             return AFPERR_PARAM;
1598     }
1599     st = &s_path->st;
1600     if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1601         memcpy(rbuf, &id, sizeof(id));
1602         *rbuflen = sizeof(id);
1603         return AFPERR_EXISTID;
1604     }
1605
1606     if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1607         memcpy(rbuf, &id, sizeof(id));
1608         *rbuflen = sizeof(id);
1609         return AFP_OK;
1610     }
1611
1612     return afp_errno;
1613 }
1614
1615 /* ------------------------------- */
1616 struct reenum {
1617     struct vol *vol;
1618     cnid_t     did;
1619 };
1620
1621 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1622 {
1623     struct path   path;
1624     struct reenum *param = data;
1625     struct vol    *vol = param->vol;  
1626     cnid_t        did  = param->did;
1627     cnid_t        aint;
1628     
1629     if ( lstat(de->d_name, &path.st) < 0 )
1630         return 0;
1631     
1632     /* update or add to cnid */
1633     aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1634
1635     return 0;
1636 }
1637
1638 /* --------------------
1639  * Ok the db is out of synch with the dir.
1640  * but if it's a deleted file we don't want to do it again and again.
1641 */
1642 static int
1643 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1644 {
1645     int             ret;
1646     struct reenum   data;
1647     struct stat     st;
1648     
1649     if (vol->v_cdb == NULL) {
1650         return -1;
1651     }
1652     
1653     /* FIXME use of_statdir ? */
1654     if (lstat(name, &st)) {
1655         return -1;
1656     }
1657
1658     if (dirreenumerate(dir, &st)) {
1659         /* we already did it once and the dir haven't been modified */
1660         return dir->d_offcnt;
1661     }
1662     
1663     data.vol = vol;
1664     data.did = dir->d_did;
1665     if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1666         setdiroffcnt(curdir, &st,  ret);
1667         dir->d_flags |= DIRF_CNID;
1668     }
1669
1670     return ret;
1671 }
1672
1673 /* ------------------------------
1674    resolve a file id */
1675 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1676 {
1677     struct vol          *vol;
1678     struct dir          *dir;
1679     char                *upath;
1680     struct path         path;
1681     int                 err, retry=0;
1682     size_t              buflen;
1683     cnid_t              id, cnid;
1684     uint16_t            vid, bitmap;
1685
1686     static char buffer[12 + MAXPATHLEN + 1];
1687     int len = 12 + MAXPATHLEN + 1;
1688
1689     *rbuflen = 0;
1690     ibuf += 2;
1691
1692     memcpy(&vid, ibuf, sizeof(vid));
1693     ibuf += sizeof(vid);
1694
1695     if (NULL == ( vol = getvolbyvid( vid )) ) {
1696         return( AFPERR_PARAM);
1697     }
1698
1699     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1700         return AFPERR_NOOP;
1701     }
1702
1703     memcpy(&id, ibuf, sizeof( id ));
1704     ibuf += sizeof(id);
1705     cnid = id;
1706     
1707     if (!id) {
1708         /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1709         return AFPERR_NOID;
1710     }
1711 retry:
1712     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1713         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1714     }
1715
1716     if (NULL == ( dir = dirlookup( vol, id )) ) {
1717         return AFPERR_NOID; /* idem AFPERR_PARAM */
1718     }
1719     if (movecwd(vol, dir) < 0) {
1720         switch (errno) {
1721         case EACCES:
1722         case EPERM:
1723             return AFPERR_ACCESS;
1724         case ENOENT:
1725             return AFPERR_NOID;
1726         default:
1727             return AFPERR_PARAM;
1728         }
1729     }
1730
1731     memset(&path, 0, sizeof(path));
1732     path.u_name = upath;
1733     if ( of_stat(&path) < 0 ) {
1734 #ifdef ESTALE
1735         /* with nfs and our working directory is deleted */
1736         if (errno == ESTALE) {
1737             errno = ENOENT;
1738         }
1739 #endif  
1740         if ( errno == ENOENT && !retry) {
1741             /* cnid db is out of sync, reenumerate the directory and update ids */
1742             reenumerate_id(vol, ".", dir);
1743             id = cnid;
1744             retry = 1;
1745             goto retry;
1746         }
1747         switch (errno) {
1748         case EACCES:
1749         case EPERM:
1750             return AFPERR_ACCESS;
1751         case ENOENT:
1752             return AFPERR_NOID;
1753         default:
1754             return AFPERR_PARAM;
1755         }
1756     }
1757
1758     /* directories are bad */
1759     if (S_ISDIR(path.st.st_mode)) {
1760         /* OS9 and OSX don't return the same error code  */
1761         return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1762     }
1763
1764     memcpy(&bitmap, ibuf, sizeof(bitmap));
1765     bitmap = ntohs( bitmap );
1766     if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1767         return AFPERR_NOID;
1768     }
1769     path.id = cnid;
1770     if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir, 
1771                             rbuf + sizeof(bitmap), &buflen))) {
1772         return err;
1773     }
1774     *rbuflen = buflen + sizeof(bitmap);
1775     memcpy(rbuf, ibuf, sizeof(bitmap));
1776
1777     return AFP_OK;
1778 }
1779
1780 /* ------------------------------ */
1781 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1782 {
1783     struct stat         st;
1784     struct vol          *vol;
1785     struct dir          *dir;
1786     char                *upath;
1787     int                 err;
1788     cnid_t              id;
1789     cnid_t              fileid;
1790     u_short             vid;
1791     static char buffer[12 + MAXPATHLEN + 1];
1792     int len = 12 + MAXPATHLEN + 1;
1793
1794     *rbuflen = 0;
1795     ibuf += 2;
1796
1797     memcpy(&vid, ibuf, sizeof(vid));
1798     ibuf += sizeof(vid);
1799
1800     if (NULL == ( vol = getvolbyvid( vid )) ) {
1801         return( AFPERR_PARAM);
1802     }
1803
1804     if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1805         return AFPERR_NOOP;
1806     }
1807
1808     if (vol->v_flags & AFPVOL_RO)
1809         return AFPERR_VLOCK;
1810
1811     memcpy(&id, ibuf, sizeof( id ));
1812     ibuf += sizeof(id);
1813     fileid = id;
1814
1815     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1816         return AFPERR_NOID;
1817     }
1818
1819     if (NULL == ( dir = dirlookup( vol, id )) ) {
1820         if (afp_errno == AFPERR_NOOBJ) {
1821             err = AFPERR_NOOBJ;
1822             goto delete;
1823         }
1824         return( AFPERR_PARAM );
1825     }
1826
1827     err = AFP_OK;
1828     if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1829         switch (errno) {
1830         case EACCES:
1831         case EPERM:
1832             return AFPERR_ACCESS;
1833 #ifdef ESTALE
1834         case ESTALE:
1835 #endif  
1836         case ENOENT:
1837             /* still try to delete the id */
1838             err = AFPERR_NOOBJ;
1839             break;
1840         default:
1841             return AFPERR_PARAM;
1842         }
1843     }
1844     else if (S_ISDIR(st.st_mode)) /* directories are bad */
1845         return AFPERR_BADTYPE;
1846
1847 delete:
1848     if (cnid_delete(vol->v_cdb, fileid)) {
1849         switch (errno) {
1850         case EROFS:
1851             return AFPERR_VLOCK;
1852         case EPERM:
1853         case EACCES:
1854             return AFPERR_ACCESS;
1855         default:
1856             return AFPERR_PARAM;
1857         }
1858     }
1859
1860     return err;
1861 }
1862
1863 /* ------------------------------ */
1864 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1865 {
1866     int             ret;
1867
1868     if (path->st_errno) {
1869         switch (path->st_errno) {
1870         case ENOENT:
1871             afp_errno = AFPERR_NOID;
1872             break;
1873         case EPERM:
1874         case EACCES:
1875             afp_errno = AFPERR_ACCESS;
1876             break;
1877         default:
1878             afp_errno = AFPERR_PARAM;
1879             break;
1880         }
1881         return NULL;
1882     }
1883     /* we use file_access both for legacy Mac perm and
1884      * for unix privilege, rename will take care of folder perms
1885     */
1886     if (file_access(path, OPENACC_WR ) < 0) {
1887         afp_errno = AFPERR_ACCESS;
1888         return NULL;
1889     }
1890     
1891     if ((*of = of_findname(path))) {
1892         /* reuse struct adouble so it won't break locks */
1893         adp = (*of)->of_ad;
1894     }
1895     else {
1896         ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1897         /* META and HF */
1898         if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1899             /* from AFP spec.
1900              * The user must have the Read & Write privilege for both files in order to use this command.
1901              */
1902             ad_close(adp, ADFLAGS_HF);
1903             afp_errno = AFPERR_ACCESS;
1904             return NULL;
1905         }
1906     }
1907     return adp;
1908 }
1909
1910 #define APPLETEMP ".AppleTempXXXXXX"
1911
1912 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1913 {
1914     struct stat         srcst, destst;
1915     struct vol          *vol;
1916     struct dir          *dir, *sdir;
1917     char                *spath, temp[17], *p;
1918     char                *supath, *upath;
1919     struct path         *path;
1920     int                 err;
1921     struct adouble      ads;
1922     struct adouble      add;
1923     struct adouble      *adsp = NULL;
1924     struct adouble      *addp = NULL;
1925     struct ofork        *s_of = NULL;
1926     struct ofork        *d_of = NULL;
1927     int                 crossdev;
1928     
1929     int                 slen, dlen;
1930     uint32_t            sid, did;
1931     uint16_t            vid;
1932
1933     uid_t              uid;
1934     gid_t              gid;
1935
1936     *rbuflen = 0;
1937     ibuf += 2;
1938
1939     memcpy(&vid, ibuf, sizeof(vid));
1940     ibuf += sizeof(vid);
1941
1942     if (NULL == ( vol = getvolbyvid( vid )) ) {
1943         return( AFPERR_PARAM);
1944     }
1945
1946     if ((vol->v_flags & AFPVOL_RO))
1947         return AFPERR_VLOCK;
1948
1949     /* source and destination dids */
1950     memcpy(&sid, ibuf, sizeof(sid));
1951     ibuf += sizeof(sid);
1952     memcpy(&did, ibuf, sizeof(did));
1953     ibuf += sizeof(did);
1954
1955     /* source file */
1956     if (NULL == (dir = dirlookup( vol, sid )) ) {
1957         return afp_errno; /* was AFPERR_PARAM */
1958     }
1959
1960     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1961         return get_afp_errno(AFPERR_NOOBJ); 
1962     }
1963
1964     if ( path_isadir(path) ) {
1965         return AFPERR_BADTYPE;   /* it's a dir */
1966     }
1967
1968     /* save some stuff */
1969     srcst = path->st;
1970     sdir = curdir;
1971     spath = obj->oldtmp;
1972     supath = obj->newtmp;
1973     strcpy(spath, path->m_name);
1974     strcpy(supath, path->u_name); /* this is for the cnid changing */
1975     p = absupath( vol, sdir, supath);
1976     if (!p) {
1977         /* pathname too long */
1978         return AFPERR_PARAM ;
1979     }
1980     
1981     ad_init(&ads, vol);
1982     if (!(adsp = find_adouble( path, &s_of, &ads))) {
1983         return afp_errno;
1984     }
1985
1986     /* ***** from here we may have resource fork open **** */
1987     
1988     /* look for the source cnid. if it doesn't exist, don't worry about
1989      * it. */
1990     sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
1991
1992     if (NULL == ( dir = dirlookup( vol, did )) ) {
1993         err = afp_errno; /* was AFPERR_PARAM */
1994         goto err_exchangefile;
1995     }
1996
1997     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1998         err = get_afp_errno(AFPERR_NOOBJ); 
1999         goto err_exchangefile;
2000     }
2001
2002     if ( path_isadir(path) ) {
2003         err = AFPERR_BADTYPE;
2004         goto err_exchangefile;
2005     }
2006
2007     /* FPExchangeFiles is the only call that can return the SameObj
2008      * error */
2009     if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2010         err = AFPERR_SAMEOBJ;
2011         goto err_exchangefile;
2012     }
2013
2014     ad_init(&add, vol);
2015     if (!(addp = find_adouble( path, &d_of, &add))) {
2016         err = afp_errno;
2017         goto err_exchangefile;
2018     }
2019     destst = path->st;
2020
2021     /* they are not on the same device and at least one is open
2022      * FIXME broken for for crossdev and adouble v2
2023      * return an error 
2024     */
2025     crossdev = (srcst.st_dev != destst.st_dev);
2026     if (/* (d_of || s_of)  && */ crossdev) {
2027         err = AFPERR_MISC;
2028         goto err_exchangefile;
2029     }
2030
2031     /* look for destination id. */
2032     upath = path->u_name;
2033     did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2034
2035     /* construct a temp name.
2036      * NOTE: the temp file will be in the dest file's directory. it
2037      * will also be inaccessible from AFP. */
2038     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2039     if (!mktemp(temp)) {
2040         err = AFPERR_MISC;
2041         goto err_exchangefile;
2042     }
2043     
2044     if (crossdev) {
2045         /* FIXME we need to close fork for copy, both s_of and d_of are null */
2046        ad_close(adsp, ADFLAGS_HF);
2047        ad_close(addp, ADFLAGS_HF);
2048     }
2049
2050     /* now, quickly rename the file. we error if we can't. */
2051     if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2052         goto err_exchangefile;
2053     of_rename(vol, s_of, sdir, spath, curdir, temp);
2054
2055     /* rename destination to source */
2056     if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2057         goto err_src_to_tmp;
2058     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2059
2060     /* rename temp to destination */
2061     if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2062         goto err_dest_to_src;
2063     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2064
2065     /* id's need switching. src -> dest and dest -> src. 
2066      * we need to re-stat() if it was a cross device copy.
2067     */
2068     if (sid)
2069         cnid_delete(vol->v_cdb, sid);
2070     if (did)
2071         cnid_delete(vol->v_cdb, did);
2072
2073     if ((did && ( (crossdev && lstat( upath, &srcst) < 0) || 
2074                 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2075        ||
2076        (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2077                 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2078     ) {
2079         switch (errno) {
2080         case EPERM:
2081         case EACCES:
2082             err = AFPERR_ACCESS;
2083             break;
2084         default:
2085             err = AFPERR_PARAM;
2086         }
2087         goto err_temp_to_dest;
2088     }
2089     
2090     /* here we need to reopen if crossdev */
2091     if (sid && ad_setid(addp, destst.st_dev, destst.st_ino,  sid, sdir->d_did, vol->v_stamp)) 
2092     {
2093        ad_flush( addp );
2094     }
2095         
2096     if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino,  did, curdir->d_did, vol->v_stamp)) 
2097     {
2098        ad_flush( adsp );
2099     }
2100
2101     /* change perms, src gets dest perm and vice versa */
2102
2103     uid = geteuid();
2104     gid = getegid();
2105     if (seteuid(0)) {
2106         LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2107         err = AFP_OK; /* ignore error */
2108         goto err_temp_to_dest;
2109     }
2110
2111     /*
2112      * we need to exchange ACL entries as well
2113      */
2114     /* exchange_acls(vol, p, upath); */
2115
2116     path->st = srcst;
2117     path->st_valid = 1;
2118     path->st_errno = 0;
2119     path->m_name = NULL;
2120     path->u_name = upath;
2121
2122     setfilunixmode(vol, path, destst.st_mode);
2123     setfilowner(vol, destst.st_uid, destst.st_gid, path);
2124
2125     path->st = destst;
2126     path->st_valid = 1;
2127     path->st_errno = 0;
2128     path->u_name = p;
2129
2130     setfilunixmode(vol, path, srcst.st_mode);
2131     setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2132
2133     if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2134         LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2135         exit(EXITERR_SYS);
2136     }
2137
2138     err = AFP_OK;
2139     goto err_exchangefile;
2140
2141     /* all this stuff is so that we can unwind a failed operation
2142      * properly. */
2143 err_temp_to_dest:
2144     /* rename dest to temp */
2145     renamefile(vol, -1, upath, temp, temp, adsp);
2146     of_rename(vol, s_of, curdir, upath, curdir, temp);
2147
2148 err_dest_to_src:
2149     /* rename source back to dest */
2150     renamefile(vol, -1, p, upath, path->m_name, addp);
2151     of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2152
2153 err_src_to_tmp:
2154     /* rename temp back to source */
2155     renamefile(vol, -1, temp, p, spath, adsp);
2156     of_rename(vol, s_of, curdir, temp, sdir, spath);
2157
2158 err_exchangefile:
2159     if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2160        ad_close(adsp, ADFLAGS_HF);
2161     }
2162     if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2163        ad_close(addp, ADFLAGS_HF);
2164     }
2165
2166     struct dir *cached;
2167     if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2168         (void)dir_remove(vol, cached);
2169     if ((cached = dircache_search_by_did(vol, did)) != NULL)
2170         (void)dir_remove(vol, cached);
2171
2172     return err;
2173 }