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