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