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