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