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