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