1 #include <limits.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include "afpfs-ng/afp.h"
7 #include "resource.h"
8 #include "lowlevel.h"
9 #include "did.h"
10 #include "afpfs-ng/midlevel.h"
11 
12 #define appledouble ".AppleDouble"
13 #define finderinfo_string ".finderinfo"
14 #define comment_string ".comment"
15 #define servericon_string "/.servericon"
16 
17 #define min(a,b) (((a)<(b)) ? (a) : (b))
18 
extra_translate(struct afp_volume * volume,const char * path,char ** newpath_p)19 static unsigned int extra_translate(
20 	struct afp_volume * volume, const char * path,
21 	char ** newpath_p)
22 {
23 
24 	char * p;
25 	char * newpath;
26 	*newpath_p=NULL;
27 	if (~volume->extra_flags & VOLUME_EXTRA_FLAGS_SHOW_APPLEDOUBLE)
28 		return 0;
29 
30 	if (strcmp(path,servericon_string)==0)
31 		return AFP_META_SERVER_ICON;
32 
33 	if (strlen(path)<(strlen(appledouble)+1))
34 		return 0;
35 
36 	if ((p=strrchr(path,'/'))==NULL)
37 		return 0;
38 
39 	p+=1;
40 
41 	if (strcmp(p,appledouble)==0) {
42 
43 		/* Okay, all we have is /foo/.AppleDouble */
44 		/* Make a copy, but not the last .AppleDouble */
45 		newpath = malloc(strlen(path));
46 		memset(newpath,0,strlen(path));
47 		if (strlen(path)==strlen(appledouble)+1)
48 			newpath[0]='/';
49 		else
50 			memcpy(newpath,path,strlen(path)-(strlen(appledouble)+1));
51 		*newpath_p=newpath;
52 
53 		return AFP_META_APPLEDOUBLE;
54 	}
55 
56 	if ((p=strstr(path,appledouble))) {
57 		/* So we have /foo/.AppleDouble/blah.  */
58 		newpath = malloc(strlen(path));
59 		memset(newpath,0,strlen(path));
60 		memcpy(newpath,path,p-path);
61 		p+=strlen(appledouble);
62 		strcat(newpath,p+1);
63 		*newpath_p=newpath;
64 
65 		/* If the ending is .finderinfo, tag it */
66 
67 
68 		if (strlen(newpath)>strlen(finderinfo_string)) {
69 			p=newpath+strlen(newpath)-strlen(finderinfo_string);
70 			if (strcmp(p,finderinfo_string)==0) {
71 				*p='\0';
72 				return AFP_META_FINDERINFO;
73 			}
74 		}
75 
76 		if (strlen(newpath)>strlen(comment_string)) {
77 			p=newpath+strlen(newpath)-strlen(comment_string);
78 			if (strcmp(p,comment_string)==0) {
79 				*p='\0';
80 				return AFP_META_COMMENT;
81 			}
82 		}
83 
84 		return AFP_META_RESOURCE;
85 	}
86 
87 	return 0;
88 }
89 
ensure_dt_opened(struct afp_volume * volume)90 static int ensure_dt_opened(struct afp_volume * volume)
91 {
92 
93 	if (volume->dtrefnum>0) return 0;
94 
95 	return (afp_opendt(volume,&volume->dtrefnum));
96 
97 }
98 
get_comment_size(struct afp_volume * volume,const char * basename,unsigned int did)99 static int get_comment_size(struct afp_volume * volume, const char * basename,
100 	unsigned int did)
101 {
102 	struct afp_comment comment;
103 	int ret=0;
104 	int size=1024;
105 
106 	if ((comment.data = malloc(size))==NULL)
107 		return -1;
108 	comment.maxsize=size;
109 	comment.size=0;
110 	if (volume->dtrefnum==0)
111 		if (afp_opendt(volume,&volume->dtrefnum) <0) {
112 			ret=-EIO;
113 			goto out;
114 		}
115 
116 	ret=(afp_getcomment(volume,did, basename, &comment));
117 	switch(ret) {
118 		case kFPAccessDenied:
119 			ret=-EACCES;
120 			break;
121 		case kFPMiscErr:
122 		case kFPParamErr:
123 			ret=-EIO;
124 			break;
125 		case kFPItemNotFound:
126 		case kFPObjectNotFound:
127 			ret=-ENOENT;
128 			break;
129 		case kFPNoErr:
130 			ret=comment.size;
131 		default:
132 		break;
133 	}
134 out:
135 	free(comment.data);
136 	return ret;
137 }
138 
appledouble_creat(struct afp_volume * volume,const char * path,mode_t mode)139 int appledouble_creat(struct afp_volume * volume, const char * path, mode_t mode)
140 {
141 	int resource;
142 	char * newpath;
143 	resource = extra_translate(volume, path, &newpath);
144 	switch(resource) {
145 		case AFP_META_RESOURCE:
146 			free(newpath);
147 			return 1;
148 		case AFP_META_APPLEDOUBLE:
149 			free(newpath);
150 			return -EBADF;
151 		case AFP_META_SERVER_ICON:
152 			return -EPERM;
153 			return 1;
154 		case AFP_META_FINDERINFO:
155 			free(newpath);
156 			return 1;
157 	}
158 	return 0;
159 }
160 
appledouble_chmod(struct afp_volume * volume,const char * path,mode_t mode)161 int appledouble_chmod(struct afp_volume * volume, const char * path, mode_t mode)
162 {
163 	int resource;
164 	char * newpath;
165 
166 	/* There's no resource you can chmod */
167 	resource = extra_translate(volume, path, &newpath);
168 	free(newpath);
169 	if (resource==0) return 0;
170 	return -EPERM;
171 }
172 
appledouble_unlink(struct afp_volume * volume,const char * path)173 int appledouble_unlink(struct afp_volume * volume, const char *path)
174 {
175 	int resource;
176 	char * newpath;
177 
178 	/* There's no resource you can unlink */
179 	resource = extra_translate(volume, path, &newpath);
180 	free(newpath);
181 	if (resource==0) return 0;
182 	return -EPERM;
183 }
184 
appledouble_close(struct afp_volume * volume,struct afp_file_info * fp)185 int appledouble_close(struct afp_volume * volume, struct afp_file_info * fp)
186 {
187 	switch(fp->resource) {
188 		case AFP_META_RESOURCE:
189 			switch(afp_closefork(volume,fp->forkid)) {
190 			case kFPNoErr:
191 				break;
192 			default:
193 			case kFPParamErr:
194 			case kFPMiscErr:
195 				return -EIO;
196 			}
197 
198 			return 0;
199 		case AFP_META_APPLEDOUBLE:
200 			return -EBADF;
201 		case AFP_META_SERVER_ICON:
202 			return 1;
203 		case AFP_META_FINDERINFO:
204 			return 0;
205 	}
206 	return 0;
207 }
208 
appledouble_write(struct afp_volume * volume,struct afp_file_info * fp,const char * data,size_t size,off_t offset,size_t * totalwritten)209 int appledouble_write(struct afp_volume * volume, struct afp_file_info *fp,
210                 const char *data, size_t size, off_t offset, size_t *totalwritten)
211 {
212 	int ret;
213 	int towrite=size;
214 	unsigned int did;
215 	struct afp_file_info fp2;
216 
217 	switch(fp->resource) {
218 		case AFP_META_RESOURCE:
219 			ret=ll_write(volume,data,size,offset,fp,totalwritten);
220 			return ret;
221 		case AFP_META_APPLEDOUBLE:
222 			return -EBADF;
223 		case AFP_META_FINDERINFO:
224 			if (offset>=32) return -EINVAL;
225 			if (towrite>(32-offset)) towrite=32-offset;
226 
227 			/* Get the previous finderinfo */
228 			ret=ll_get_directory_entry(volume,fp->basename,fp->did,
229 				kFPFinderInfoBit,kFPFinderInfoBit,&fp2);
230 			if (ret<0) return ret;
231 
232 			/* Copy in only the parts we got */
233 			memcpy(fp->finderinfo+offset,data,towrite);
234 
235 			ret=afp_setfiledirparms(volume,
236 					fp->did,fp->basename,
237 					kFPFinderInfoBit, fp);
238 			switch(ret) {
239 				case kFPNoErr:
240 					break;
241 				case kFPAccessDenied:
242 					return -EACCES;
243 				case kFPObjectNotFound:
244 					return -ENOENT;
245 				case kFPBitmapErr:
246 				case kFPMiscErr:
247 				case kFPObjectTypeErr:
248 				case kFPParamErr:
249 				default:
250 					break;
251 
252 			}
253 			*totalwritten=towrite;
254 
255 			return 1;
256 		case AFP_META_COMMENT:
257 			switch(afp_addcomment(volume, fp->did,fp->basename,
258 				(char *)data,(uint64_t *) totalwritten)) {
259 			case kFPAccessDenied:
260 				return -EACCES;
261 			case kFPObjectNotFound:
262 				return -ENOENT;
263 			case kFPNoErr:
264                 		*totalwritten=size;
265 				return 1;
266 			case kFPMiscErr:
267 			default:
268 				return -EIO;
269 			}
270 		case AFP_META_SERVER_ICON:
271 			return -EPERM;
272 
273 	}
274 	return 0;
275 
276 
277 }
278 
appledouble_read(struct afp_volume * volume,struct afp_file_info * fp,char * buf,size_t size,off_t offset,size_t * amount_read,int * eof)279 int appledouble_read(struct afp_volume * volume, struct afp_file_info *fp,
280 	char * buf, size_t size, off_t offset, size_t * amount_read,
281 	int * eof)
282 {
283 	int tocopy;
284 	int ret;
285 	struct afp_comment comment;
286 	*amount_read=0;
287 	*eof=0;
288 
289 	comment.data=malloc(size);
290 	comment.maxsize=size;
291 
292 	switch(fp->resource) {
293 		case AFP_META_RESOURCE:
294 			ret=ll_read(volume,buf,size,offset,fp,eof);
295 			return ret;
296 		case AFP_META_APPLEDOUBLE:
297 			return -EBADF;
298 		case AFP_META_FINDERINFO:
299 			if (offset>32) return -EFAULT;
300 			ret=ll_get_directory_entry(volume,fp->basename,fp->did,
301 				kFPFinderInfoBit,kFPFinderInfoBit,fp);
302 			if (ret<0) return ret;
303 			tocopy=min(size,(32-offset));
304 			memcpy(buf+offset,fp->finderinfo,tocopy);
305 			if (offset+tocopy==32) *eof=1;
306 			*amount_read=tocopy;
307 		case AFP_META_COMMENT:
308 			if (fp->eof)  ret=1;  else
309 			switch(afp_getcomment(volume,fp->did, fp->basename, &comment)) {
310 				case kFPAccessDenied:
311 					ret=-EACCES;
312 					break;
313 				case kFPMiscErr:
314 				case kFPParamErr:
315 					ret=-EIO;
316 					break;
317 				case kFPItemNotFound:
318 				case kFPObjectNotFound:
319 					ret=-ENOENT;
320 					break;
321 				case kFPNoErr:
322 					memcpy(buf,comment.data,comment.size);
323 					*amount_read =comment.size;
324 					ret=1;
325 					*eof=1;
326 					fp->eof=1;
327 				default:
328 				break;
329 			}
330 			free(comment.data);
331 			return ret;
332 		case AFP_META_SERVER_ICON:
333 			if (offset>256) return -EFAULT;
334 			tocopy=min(size,(256-offset));
335 			memcpy(buf+offset,volume->server->icon,tocopy);
336 			*eof=1;
337 			fp->eof=1;
338 			*amount_read=tocopy;
339 			return 1;
340 	}
341 	return 0;
342 }
343 
appledouble_truncate(struct afp_volume * volume,const char * path,int offset)344 int appledouble_truncate(struct afp_volume * volume, const char * path, int offset)
345 {
346 
347 	char * newpath;
348 	int resource = extra_translate(volume, path, &newpath);
349 	struct afp_file_info fp;
350 	int ret;
351 	int dirid;
352 	char basename[AFP_MAX_PATH];
353 
354 	switch(resource) {
355 		case AFP_META_RESOURCE:
356 
357 			get_dirid(volume,newpath,basename,&dirid);
358 
359 			ret=afp_openfork(volume,1,dirid,O_WRONLY,
360 				basename,&fp);
361 
362 			ret=ll_zero_file(volume,fp.forkid,0);
363 			if (ret<0) {
364 				afp_closefork(volume,fp.forkid);
365 				remove_opened_fork(volume,fp);
366 				free(newpath);
367 				return ret;
368 			}
369 			afp_closefork(volume,fp.forkid);
370 			remove_opened_fork(volume,fp);
371 
372 			return 1;
373 		case AFP_META_APPLEDOUBLE:
374 			free(newpath);
375 			return -EISDIR;
376 		case AFP_META_FINDERINFO:
377 			free(newpath);
378 			return 1;
379 		case AFP_META_COMMENT:
380 			free(newpath);
381 			return 1;
382 		case AFP_META_SERVER_ICON:
383 			free(newpath);
384 			return -EPERM;
385 	}
386 	return 0;
387 }
388 
appledouble_open(struct afp_volume * volume,const char * path,int flags,struct afp_file_info * fp)389 int appledouble_open(struct afp_volume * volume, const char * path, int flags,
390 	struct afp_file_info *fp)
391 {
392 	char * newpath;
393 	int ret;
394 
395 	switch((fp->resource = extra_translate(volume, path, &newpath))) {
396 		case AFP_META_RESOURCE:
397 			if (get_dirid(volume,newpath,fp->basename,&fp->did)<0) {
398 				ret=-ENOENT;
399 				goto error;
400 			}
401 			ret=ll_open(volume,newpath,flags,fp);
402 			free(newpath);
403 			if (ret<0) return ret;
404 			return 1;
405 		case AFP_META_APPLEDOUBLE:
406 			free(newpath);
407 			return -EISDIR;
408 		case AFP_META_FINDERINFO:
409 			if (get_dirid(volume,newpath,fp->basename,&fp->did)<0)
410 				return -ENOENT;
411 			free(newpath);
412 			return 1;
413 		case AFP_META_COMMENT:
414 			if (get_dirid(volume,newpath,fp->basename,&fp->did)<0) {
415 				ret=-ENOENT;
416 				goto error;
417 			}
418 			if (volume->dtrefnum==0) {
419 				switch(afp_opendt(volume,&volume->dtrefnum)) {
420 				case kFPParamErr:
421 				case kFPMiscErr:
422 					free(newpath);
423 					return -EIO;
424 				case kFPNoErr:
425 				default:
426 					break;
427 				}
428 			}
429 			free(newpath);
430 			return 1;
431 		case AFP_META_SERVER_ICON:
432 			free(newpath);
433 			return 1;
434 	}
435 	return 0;
436 error:
437 	free(newpath);
438 	return ret;
439 
440 }
441 
appledouble_getattr(struct afp_volume * volume,const char * path,struct stat * stbuf)442 int appledouble_getattr(struct afp_volume * volume,
443 	const char * path, struct stat *stbuf)
444 {
445 	unsigned int resource;
446 	char * newpath;
447 	int ret;
448 
449 	resource = extra_translate(volume, path, &newpath);
450 	switch(resource) {
451 		case AFP_META_RESOURCE:
452 			ll_getattr(volume,newpath,stbuf,1);
453 			goto okay;
454 		case AFP_META_APPLEDOUBLE:
455 			stbuf->st_mode = 0700 | S_IFDIR;
456 			goto okay;
457 		case AFP_META_FINDERINFO:
458 			ll_getattr(volume,newpath,stbuf,0);
459 			stbuf->st_mode |= S_IFREG;
460 			stbuf->st_size=32;
461 			goto okay;
462 		case AFP_META_COMMENT: {
463 			unsigned int did;
464 			char basename[AFP_MAX_PATH];
465 
466 			ret=ll_getattr(volume,newpath,stbuf,0);
467 			if (ret<0)
468 				goto error;
469 
470 			ret=get_dirid(volume,newpath,basename,&did);
471 			if (ret<0)
472 				goto error;
473 
474 			ret=get_comment_size(volume,basename,did);
475 			if (ret<0)
476 				goto error;
477 
478 			stbuf->st_mode |= S_IFREG;
479 			stbuf->st_size=ret;
480 			goto okay;
481 		}
482 		case AFP_META_SERVER_ICON:
483 			stbuf->st_mode = S_IFREG | 0444;
484 			stbuf->st_size=256;
485 			goto okay;
486 	}
487 	return 0;
488 okay:
489 	free(newpath);
490 	return 1;
491 error:
492 	free(newpath);
493 	return ret;
494 
495 }
496 
remove_fp(struct afp_file_info ** base,struct afp_file_info * toremove)497 static void remove_fp(struct afp_file_info **base,struct afp_file_info * toremove)
498 {
499 	struct afp_file_info *fp, *prev=NULL;
500 
501 	for (fp=*base;fp;fp=fp->next)
502 		if (fp==toremove) {
503 			if (prev==NULL) {
504 				*base=fp->next;;
505 				free(fp);
506 				prev=NULL;
507 			} else {
508 				prev->next=fp->next;
509 				prev=fp;
510 				free(fp);
511 			}
512 		}
513 
514 }
515 
add_fp(struct afp_file_info ** newchain,struct afp_file_info * fp,char * suffix,unsigned int size)516 static int add_fp(struct afp_file_info **newchain, struct afp_file_info *fp,
517 		char * suffix, unsigned int size)
518 {
519 	struct afp_file_info * newfp;
520 	newfp=malloc(sizeof(struct afp_file_info));
521 	memcpy(newfp,fp,sizeof(struct afp_file_info));
522 	strcat(newfp->name,suffix);
523 	newfp->resourcesize=size;
524 	newfp->unixprivs.permissions|=S_IFREG;
525 	newfp->next=*newchain;
526 	*newchain=newfp;
527 }
528 
appledouble_readdir(struct afp_volume * volume,const char * path,struct afp_file_info ** base)529 int appledouble_readdir(struct afp_volume * volume,
530 	const char *path, struct afp_file_info **base)
531 {
532 	unsigned int resource;
533 	char * newpath;
534 
535 	resource = extra_translate(volume, path, &newpath);
536 
537 	switch(resource) {
538 		case 0:
539 			return 0;
540 		case AFP_META_APPLEDOUBLE: {
541 			struct afp_file_info *fp, *prev=NULL,
542 				*newfp=NULL, *newchain=NULL, *last=NULL;
543 			ll_readdir(volume, newpath,base,1);
544 
545 			/* Add .finderinfo files */
546 			for (fp=*base;fp;fp=fp->next) {
547 				add_fp(&newchain,fp,finderinfo_string,32);
548 
549 				/* Add comments if it has a size > 0 */
550 				if (ensure_dt_opened(volume)==0) {
551 					int size=get_comment_size(volume,
552 						fp->name,fp->did);
553 
554 					if (size>0)
555 					add_fp(&newchain,fp,comment_string,32);
556 				}
557 
558 				if (fp->unixprivs.permissions & S_IFREG) {
559 					if (fp->resourcesize==0) {
560 						remove_fp(base,fp);
561 					}
562 				} else {
563 					remove_fp(base,fp);
564 				}
565 				last=fp;
566 			}
567 			if ((newchain) && (last)) {
568 				last->next=newchain;
569 			}
570 
571 			free(newpath);
572 			return 1;
573 		case AFP_META_RESOURCE:
574 		case AFP_META_SERVER_ICON:
575 		case AFP_META_COMMENT:
576 			free(newpath);
577 			return -ENOTDIR;
578 		}
579 	}
580 
581 	return 0;
582 }
583 
appledouble_mkdir(struct afp_volume * volume,const char * path,mode_t mode)584 int appledouble_mkdir(struct afp_volume * volume, const char * path, mode_t mode)
585 {
586 	int resource;
587 	char * newpath;
588 
589 	/* You can't mkdir */
590 	resource = extra_translate(volume, path, &newpath);
591 	free(newpath);
592 	if (resource==0) return 0;
593 	return -EPERM;
594 
595 }
596 
appledouble_readlink(struct afp_volume * volume,const char * path,char * buf,size_t size)597 int appledouble_readlink(struct afp_volume * volume, const char * path,
598 	char * buf, size_t size)
599 {
600 	int resource;
601 	char * newpath;
602 
603 	/* You can't readlink*/
604 	resource = extra_translate(volume, path, &newpath);
605 	free(newpath);
606 	if (resource==0) return 0;
607 	return -EPERM;
608 
609 }
610 
appledouble_rmdir(struct afp_volume * volume,const char * path)611 int appledouble_rmdir(struct afp_volume * volume, const char * path)
612 {
613 	int resource;
614 	char * newpath;
615 
616 	/* You can't rmdir*/
617 	resource = extra_translate(volume, path, &newpath);
618 	free(newpath);
619 	if (resource==0) return 0;
620 	return -EPERM;
621 
622 }
623 
appledouble_chown(struct afp_volume * volume,const char * path,uid_t uid,gid_t gid)624 int appledouble_chown(struct afp_volume * volume, const char * path,
625 		uid_t uid, gid_t gid)
626 {
627 	int resource;
628 	char * newpath;
629 
630 	/* You can't chown*/
631 	resource = extra_translate(volume, path, &newpath);
632 	free(newpath);
633 	if (resource==0) return 0;
634 	return -EPERM;
635 
636 }
637 
appledouble_utime(struct afp_volume * volume,const char * path,struct utimbuf * timebuf)638 int appledouble_utime(struct afp_volume * volume, const char * path,
639 	struct utimbuf * timebuf)
640 {
641 	int resource;
642 	char * newpath;
643 
644 	/* You can't utime*/
645 	resource = extra_translate(volume, path, &newpath);
646 	free(newpath);
647 	if (resource==0) return 0;
648 	return -EPERM;
649 }
650 
appledouble_symlink(struct afp_volume * vol,const char * path1,const char * path2)651 int appledouble_symlink(struct afp_volume *vol, const char *path1, const char *path2)
652 {
653 	int resource;
654 	char * newpath;
655 
656 	/* You can't symlink*/
657 	resource = extra_translate(vol, path1, &newpath);
658 	free(newpath);
659 	if (resource==0) return 0;
660 	return -EPERM;
661 }
662 
appledouble_rename(struct afp_volume * volume,const char * path_from,const char * path_to)663 int appledouble_rename(struct afp_volume * volume, const char * path_from,
664 	const char * path_to)
665 {
666 	int resource;
667 	char * newpath;
668 
669 	/* You can't rename*/
670 	resource = extra_translate(volume, path_to, &newpath);
671 	free(newpath);
672 	if (resource==0) return 0;
673 	return -EPERM;
674 }
675 
676