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