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