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