1 
2 
3 /*
4     midlevel.c: some funtions to abstract some of the common functions
5 
6     Copyright (C) 2006 Alex deVries <alexthepuffin@gmail.com>
7 
8     This program can be distributed under the terms of the GNU GPL.
9     See the file COPYING.
10 */
11 
12 
13 #include "afpfs-ng/afp.h"
14 
15 #include <sys/stat.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <sys/time.h>
20 
21 #ifdef __linux__
22 #include <asm/fcntl.h>
23 #else
24 #include <fcntl.h>
25 #endif
26 
27 
28 #include "users.h"
29 #include "did.h"
30 #include "resource.h"
31 #include "afpfs-ng/utils.h"
32 #include "afpfs-ng/codepage.h"
33 #include "afpfs-ng/midlevel.h"
34 #include "afp_internal.h"
35 #include "forklist.h"
36 #include "uams.h"
37 #include "lowlevel.h"
38 
39 
40 #define min(a,b) (((a)<(b)) ? (a) : (b))
41 
get_unixprivs(struct afp_volume * volume,unsigned int dirid,const char * path,struct afp_file_info * fp)42 static int get_unixprivs(struct afp_volume * volume,
43 	unsigned int dirid,
44 	const char * path, struct afp_file_info * fp)
45 {
46 
47 	switch (ll_get_directory_entry(volume,(char *)path,
48 		dirid, kFPUnixPrivsBit,kFPUnixPrivsBit,fp)) {
49 	case kFPAccessDenied:
50 		return -EACCES;
51 	case kFPObjectNotFound:
52 		return -ENOENT;
53 	case kFPNoErr:
54 		break;
55 	case kFPBitmapErr:
56 	case kFPMiscErr:
57 	case kFPParamErr:
58 	default:
59 		return -EIO;
60 
61 	}
62 	return 0;
63 }
64 
65 
set_unixprivs(struct afp_volume * vol,unsigned int dirid,const char * basename,struct afp_file_info * fp)66 static int set_unixprivs(struct afp_volume * vol,
67 	unsigned int dirid,
68 	const char * basename, struct afp_file_info * fp)
69 
70 {
71 #define TOCHECK_BITS \
72 	(S_IRUSR |S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | \
73 	 S_IROTH | S_IWOTH | S_IXOTH )
74 
75 	int ret=0, rc;
76 	int rc2;
77 	struct afp_file_info fp2;
78 
79 	fp->unixprivs.ua_permissions=0;
80 
81 	if (!vol->extra_flags & VOLUME_EXTRA_FLAGS_VOL_SUPPORTS_UNIX)
82 		return 0;
83 
84 	if (fp->isdir) {
85 		rc=afp_setdirparms(vol, dirid,basename,
86 			kFPUnixPrivsBit, fp);
87 	} else {
88 
89 		/* For broken netatalk servers, strip out the extra bits. */
90 		if ((fp->unixprivs.permissions&~(AFP_CHMOD_ALLOWED_BITS_22)) &&
91 		(vol->server->server_type==AFPFS_SERVER_TYPE_NETATALK) &&
92 		(vol->extra_flags & VOLUME_EXTRA_FLAGS_VOL_CHMOD_KNOWN) &&
93 		(vol->extra_flags & VOLUME_EXTRA_FLAGS_VOL_CHMOD_BROKEN))
94 			fp->unixprivs.permissions&=AFP_CHMOD_ALLOWED_BITS_22;
95 
96 		rc=afp_setfiledirparms(vol,dirid,basename,
97 			kFPUnixPrivsBit, fp);
98 	}
99 
100 	switch (rc) {
101 	case kFPAccessDenied:
102 		ret=EPERM;
103 		break;
104 	case kFPBitmapErr:
105 		ret=ENOSYS;
106 		break;
107 	case kFPObjectNotFound:
108 		ret=ENOENT;
109 		break;
110 	case 0:
111 		ret=0;
112 		break;
113 	case kFPMiscErr:
114 	case kFPObjectTypeErr:
115 	case kFPParamErr:
116 	default:
117 		ret=EIO;
118 		break;
119 	}
120 
121 	/* If it is netatalk, check to see if that worked.  If not,
122 	*            never try this bitset again. */
123 	if ((fp->unixprivs.permissions & ~(AFP_CHMOD_ALLOWED_BITS_22)) &&
124 		(!(vol->extra_flags & VOLUME_EXTRA_FLAGS_VOL_CHMOD_KNOWN)) &&
125 		(vol->server->server_type==AFPFS_SERVER_TYPE_NETATALK))
126 	{
127 		if ((rc2=get_unixprivs(vol, dirid, basename, &fp2)))
128 			return rc2;
129 		vol->extra_flags|=VOLUME_EXTRA_FLAGS_VOL_CHMOD_KNOWN;
130 
131 		if ((fp2.unixprivs.permissions&TOCHECK_BITS)==
132 			(fp->unixprivs.permissions&TOCHECK_BITS)) {
133 				vol->extra_flags&=~VOLUME_EXTRA_FLAGS_VOL_CHMOD_BROKEN;
134 		} else {
135 			vol->extra_flags|=VOLUME_EXTRA_FLAGS_VOL_CHMOD_BROKEN;
136 			return -EFAULT;
137 		}
138 	}
139 
140 	return -ret;
141 }
142 
add_file_by_name(struct afp_file_info ** base,const char * filename)143 void add_file_by_name(struct afp_file_info ** base, const char *filename)
144 {
145 	struct afp_file_info * t,*new_file;
146 
147 	new_file=malloc(sizeof(*new_file));
148 	memcpy(new_file->name,filename,AFP_MAX_PATH);
149 	new_file->next=NULL;
150 
151 	if (*base==NULL) {
152 		*base=new_file;
153 	} else {
154 		for (t=*base;t->next;t=t->next);
155 		t->next=new_file;
156 	}
157 
158 }
159 
afp_ml_filebase_free(struct afp_file_info ** filebase)160 void afp_ml_filebase_free(struct afp_file_info **filebase)
161 {
162 
163 	struct afp_file_info * p, *next;
164 
165 	if (filebase==NULL) return;
166 
167 	p=*filebase;
168 	while (p) {
169 		next=p->next;
170 		free(p);
171 		p=next;
172 	}
173 	*filebase=NULL;
174 }
175 
176 
177 
178 
179 
180 /*
181  * set_uidgid()
182  *
183  * This sets the userid and groupid in an afp_file_info struct using the
184  * appropriate translation.  You should pass it the host's version of the
185  * uid and gid.
186  *
187  */
188 
set_uidgid(struct afp_volume * volume,struct afp_file_info * fp,uid_t uid,gid_t gid)189 static int set_uidgid(struct afp_volume * volume,
190 	struct afp_file_info * fp, uid_t uid, gid_t gid)
191 {
192 
193 	unsigned int newuid=uid;
194 	unsigned int newgid=gid;
195 
196 	translate_uidgid_to_server(volume,&newuid,&newgid);
197 
198 	fp->unixprivs.uid=newuid;
199 	fp->unixprivs.gid=newgid;
200 
201 	return 0;
202 }
203 
update_time(unsigned int * newtime)204 static void update_time(unsigned int * newtime)
205 {
206 	struct timeval tv;
207 	gettimeofday(&tv,NULL);
208 	*newtime=tv.tv_sec;
209 }
210 
ml_open(struct afp_volume * volume,const char * path,int flags,struct afp_file_info ** newfp)211 int ml_open(struct afp_volume * volume, const char *path, int flags,
212 	struct afp_file_info **newfp)
213 {
214 
215 /* FIXME:  doesn't handle create properly */
216 
217 	struct afp_file_info * fp ;
218 	int ret, dsi_ret,rc;
219 	int create_file=0;
220 	char resource=0;
221 	unsigned int dirid;
222 	char converted_path[AFP_MAX_PATH];
223 	unsigned char aflags = AFP_OPENFORK_ALLOWREAD;
224 
225 	if (convert_path_to_afp(volume->server->path_encoding,
226 		converted_path,(char *) path,AFP_MAX_PATH))
227 		return -EINVAL;
228 
229 	if (invalid_filename(volume->server,converted_path))
230 		return -ENAMETOOLONG;
231 
232 	if (volume_is_readonly(volume) &&
233 		(flags & (O_WRONLY|O_RDWR|O_TRUNC|O_APPEND|O_CREAT)))
234 		return -EACCES;
235 
236 	if ((fp=malloc(sizeof(*fp)))==NULL) {
237 		return -1;
238 	}
239 	*newfp=fp;
240 
241 	memset(fp,0,sizeof(*fp));
242 
243 	ret=appledouble_open(volume,path,flags,fp);
244 	if (ret<0) return ret;
245 	if (ret==1) goto out;
246 
247 
248 	if (get_dirid(volume,converted_path,fp->basename,&dirid)<0)
249 		return -ENOENT;
250 
251 	fp->did=dirid;
252 
253 	ret=ll_open(volume,converted_path,flags,fp);
254 
255 	if (ret<0) goto error;
256 
257 
258 out:
259 	return 0;
260 
261 error:
262 	free(fp);
263 	return ret;
264 }
265 
266 
267 
ml_creat(struct afp_volume * volume,const char * path,mode_t mode)268 int ml_creat(struct afp_volume * volume, const char *path, mode_t mode)
269 {
270 	int ret=0;
271 	char resource;
272 	char basename[AFP_MAX_PATH];
273 	unsigned int dirid;
274 	struct afp_file_info fp;
275 	int rc;
276 	char converted_path[AFP_MAX_PATH];
277 
278 	if (convert_path_to_afp(volume->server->path_encoding,
279 		converted_path,(char *) path,AFP_MAX_PATH))
280 		return -EINVAL;
281 
282 	if (volume_is_readonly(volume))
283 		return -EACCES;
284 
285 	ret=appledouble_creat(volume,path,mode);
286 	if (ret<0) return ret;
287 	if (ret==1) return 0;
288 
289 	if (invalid_filename(volume->server,converted_path))
290 		return -ENAMETOOLONG;
291 
292 	get_dirid(volume, converted_path, basename, &dirid);
293 
294 	rc=afp_createfile(volume,kFPSoftCreate, dirid,basename);
295 	switch(rc) {
296 	case kFPAccessDenied:
297 		ret=EACCES;
298 		break;
299 	case kFPDiskFull:
300 		ret=ENOSPC;
301 		break;
302 	case kFPObjectExists:
303 		ret=EEXIST;
304 		break;
305 	case kFPObjectNotFound:
306 		ret=ENOENT;
307 		break;
308 	case kFPFileBusy:
309 	case kFPVolLocked:
310 		ret=EBUSY;
311 		break;
312 	case kFPNoErr:
313 		ret=0;
314 		break;
315 	default:
316 	case kFPParamErr:
317 	case kFPMiscErr:
318 		ret=EIO;
319 	}
320 
321 	if (ret) return -ret;
322 
323 	/* If we don't support unixprivs, just exit */
324 	if (~ volume->extra_flags & VOLUME_EXTRA_FLAGS_VOL_SUPPORTS_UNIX)
325 		return 0;
326 
327 	/* Figure out the privs of the file we just created */
328 	if ((ret=get_unixprivs(volume,
329 		dirid,basename, &fp)))
330 		return rc;
331 
332 	if (ret) return -ret;
333 
334 	if (fp.unixprivs.permissions==mode)
335 		return 0;
336 
337 
338 	fp.unixprivs.ua_permissions=0;
339 	fp.unixprivs.permissions=mode;
340 	fp.isdir=0;  /* Anything you make with mknod is a file */
341 	/* note that we're not monkeying with the ownership here */
342 
343 	rc=set_unixprivs(volume, dirid, basename, &fp);
344 
345 	switch(rc) {
346 	case kFPAccessDenied:
347 		ret=EPERM;
348 		goto error;
349 	case kFPObjectNotFound:
350 		ret=ENOENT;
351 		goto error;
352 	case 0:
353 		ret=0;
354 		break;
355 	case kFPBitmapErr:
356 	case kFPMiscErr:
357 	case kFPObjectTypeErr:
358 	case kFPParamErr:
359 	default:
360 		ret=EIO;
361 		goto error;
362 	}
363 
364 error:
365 	return -ret;
366 }
367 
368 
369 
ml_readdir(struct afp_volume * volume,const char * path,struct afp_file_info ** fb)370 int ml_readdir(struct afp_volume * volume,
371 	const char *path,
372 	struct afp_file_info **fb)
373 {
374 	int ret=0;
375 	char converted_path[AFP_MAX_PATH];
376 
377 	if (convert_path_to_afp(volume->server->path_encoding,
378 		converted_path,(char *) path,AFP_MAX_PATH)) {
379 		return -EINVAL;
380 	}
381 
382 	ret=appledouble_readdir(volume, converted_path, fb);
383 
384 	if (ret<0) return ret;
385 	if (ret==1) goto done;
386 
387 	return ll_readdir(volume,converted_path,fb,0);
388 done:
389 	return 0;
390 }
391 
ml_read(struct afp_volume * volume,const char * path,char * buf,size_t size,off_t offset,struct afp_file_info * fp,int * eof)392 int ml_read(struct afp_volume * volume, const char *path,
393 	char *buf, size_t size, off_t offset,
394 	struct afp_file_info *fp, int * eof)
395 {
396 	int bytesleft=size;
397 	int totalsize=0;
398 	int ret=0;
399 	int rc;
400 	unsigned int bufsize=min(volume->server->rx_quantum,size);
401 	char converted_path[AFP_MAX_PATH];
402 	struct afp_rx_buffer buffer;
403 	size_t amount_copied=0;
404 
405 	*eof=0;
406 
407 	if (convert_path_to_afp(volume->server->path_encoding,
408 		converted_path,(char *) path,AFP_MAX_PATH)) {
409 		return -EINVAL;
410 	}
411 
412 	if (fp->resource) {
413 		ret=appledouble_read(volume,fp,buf,size,offset,&amount_copied,eof);
414 
415 		if (ret<0) return ret;
416 		if (ret==1) return amount_copied;
417 
418 	}
419 
420 	ret=ll_read(volume,buf,size,offset,fp,eof);
421 
422 	return ret;
423 }
424 
425 
ml_chmod(struct afp_volume * vol,const char * path,mode_t mode)426 int ml_chmod(struct afp_volume * vol, const char * path, mode_t mode)
427 {
428 /*
429 chmod has an interesting story to it.
430 
431 It is known to work with Darwin 10.3.9 (AFP 3.1), 10.4.2 and 10.5.x (AFP 3.2).
432 
433 chmod will not work properly in the following situations:
434 
435 - AFP 2.2, this needs some more verification but I don't see how it is possible
436 
437 - netatalk 2.0.3 and probably earlier:
438 
439   . netatalk will only enable it at all if you have "options=upriv"
440     set for that volume.
441 
442   . netatalk will never be able to chmod the execute bit and some others on
443     files; this is hard coded in unix.c's setfilemode() in 2.0.3.  It's like
444     it has 2.2 behaviour even though it is trying to speak 3.1.
445 
446   . The only bits allowed are
447         S_IRUSR |S_IWUSR | S_IRGRP | S_IWGRP |S_IROTH | S_IWOTH;
448     There's probably a reason for this, I don't know what it is.
449 
450   . afpfs-ng's behaviour's the same as the Darwin client.
451 
452 The right way to see if a volume supports chmod is to check the attributes
453 found with getvolparm or volopen, then to test chmod the first time.
454 
455 */
456 
457 	int ret=0,rc;
458 	struct afp_file_info fp;
459 	unsigned int dirid;
460 	char basename[AFP_MAX_PATH];
461 	char converted_path[AFP_MAX_PATH];
462 	uid_t uid; gid_t gid;
463 
464 	if (invalid_filename(vol->server,path))
465 		return -ENAMETOOLONG;
466 
467 	if (volume_is_readonly(vol))
468 		return -EACCES;
469 
470 
471 	/* There's no way to do this in AFP < 3.0 */
472 	if (~ vol->extra_flags & VOLUME_EXTRA_FLAGS_VOL_SUPPORTS_UNIX) {
473 
474 		if (vol->extra_flags & VOLUME_EXTRA_FLAGS_IGNORE_UNIXPRIVS) {
475 			struct stat stbuf;
476 			/* See if the file exists */
477 			ret=ll_getattr(vol,path,&stbuf,0);
478 			return ret;
479 		}
480 
481 		return -ENOSYS;
482 	};
483 
484 	if (convert_path_to_afp(vol->server->path_encoding,
485 		converted_path,(char *) path,AFP_MAX_PATH)) {
486 		return -EINVAL;
487 	}
488 
489 	ret=appledouble_chmod(vol,path,mode);
490 	if (ret<0) return ret;
491 	if (ret==1) return 0;
492 
493 	get_dirid(vol,converted_path,basename,&dirid );
494 
495 	if ((rc=get_unixprivs(vol,
496 		dirid,basename, &fp)))
497 		return rc;
498 
499 	mode&=(~S_IFDIR);
500 
501 	/* Don't bother updating it if it's already the same */
502 	if ((fp.unixprivs.permissions&(~S_IFDIR))==mode)
503 		return 0;
504 
505 	/* Check to make sure that we can; some servers (at least netatalk)
506 	   don't report an error when you try to setfileparm when you don't
507 	   own the file.  */
508 
509 	/* Try to guess if the operation is possible */
510 
511 	uid=fp.unixprivs.uid;
512 	gid=fp.unixprivs.gid;
513 	if (translate_uidgid_to_client(vol, &uid,&gid))
514 		return -EIO;
515 
516 	if ((gid!=getgid()) && (uid!=geteuid())) {
517 		return -EPERM;
518 	}
519 
520 	fp.unixprivs.permissions=mode;
521 
522 	rc=set_unixprivs(vol, dirid,basename, &fp);
523 	if (rc==-ENOSYS) {
524 		return -ENOSYS;
525 	}
526 
527 
528 
529 	return -ret;
530 }
531 
532 
ml_unlink(struct afp_volume * vol,const char * path)533 int ml_unlink(struct afp_volume * vol, const char *path)
534 {
535 	int ret,rc;
536 	unsigned int dirid;
537 	char basename[AFP_MAX_PATH];
538 	char converted_path[AFP_MAX_PATH];
539 
540 	if (convert_path_to_afp(vol->server->path_encoding,
541 		converted_path,(char *) path,AFP_MAX_PATH))
542 		return -EINVAL;
543 
544 	if (volume_is_readonly(vol))
545 		return -EACCES;
546 
547 	ret=appledouble_unlink(vol,path);
548 	if (ret<0) return ret;
549 	if (ret==1) return 0;
550 
551 	get_dirid(vol, (char * ) converted_path, basename, &dirid);
552 
553 	if (is_dir(vol,dirid,basename) ) return -EISDIR;
554 
555 	if (invalid_filename(vol->server,converted_path))
556 		return -ENAMETOOLONG;
557 
558 	rc=afp_delete(vol,dirid,basename);
559 
560 	switch(rc) {
561 	case kFPAccessDenied:
562 		ret=EACCES;
563 		break;
564 	case kFPObjectLocked:
565 		ret=EBUSY;
566 		break;
567 	case kFPObjectNotFound:
568 		ret=ENOENT;
569 		break;
570 	case kFPVolLocked:
571 		ret=EACCES;
572 		break;
573 	case kFPObjectTypeErr:
574 	case kFPDirNotEmpty:
575 	case kFPMiscErr:
576 	case kFPParamErr:
577 	case -1:
578 		ret=EINVAL;
579 		break;
580 	default:
581 		ret=0;
582 	}
583 	return -ret;
584 }
585 
586 
587 
588 
ml_mkdir(struct afp_volume * vol,const char * path,mode_t mode)589 int ml_mkdir(struct afp_volume * vol, const char * path, mode_t mode)
590 {
591 	int ret,rc;
592 	unsigned int result_did;
593 	char basename[AFP_MAX_PATH];
594 	char converted_path[AFP_MAX_PATH];
595 	unsigned int dirid;
596 
597 	if (convert_path_to_afp(vol->server->path_encoding,
598 		converted_path,(char *) path,AFP_MAX_PATH))
599 		return -EINVAL;
600 
601 	if (invalid_filename(vol->server,path))
602 		return -ENAMETOOLONG;
603 
604 	if (volume_is_readonly(vol))
605 		return -EACCES;
606 
607 	ret=appledouble_mkdir(vol,path,mode);
608 	if (ret<0) return ret;
609 	if (ret==1) return 0;
610 
611 	get_dirid(vol,converted_path,basename,&dirid);
612 
613 	rc = afp_createdir(vol,dirid, basename,&result_did);
614 
615 	switch (rc) {
616 	case kFPAccessDenied:
617 		ret = EACCES;
618 		break;
619 	case kFPDiskFull:
620 		ret = ENOSPC;
621 		break;
622 	case kFPObjectNotFound:
623 		ret = ENOENT;
624 		break;
625 	case kFPObjectExists:
626 		ret = EEXIST;
627 		break;
628 	case kFPVolLocked:
629 		ret = EBUSY;
630 		break;
631 	case kFPFlatVol:
632 	case kFPMiscErr:
633 	case kFPParamErr:
634 	case -1:
635 		ret = EFAULT;
636 		break;
637 	default:
638 		ret =0;
639 	}
640 
641 	return -ret;
642 }
643 
ml_close(struct afp_volume * volume,const char * path,struct afp_file_info * fp)644 int ml_close(struct afp_volume * volume, const char * path,
645 	struct afp_file_info * fp)
646 {
647 
648 	int ret=0;
649 	char converted_path[AFP_MAX_PATH];
650 
651 	if (convert_path_to_afp(volume->server->path_encoding,
652 		converted_path, (char *) path,AFP_MAX_PATH)) {
653 		return -EINVAL;
654 	}
655 
656 	if (invalid_filename(volume->server,converted_path))
657 		return -ENAMETOOLONG;
658 
659 	/* The logic here is that if we don't have an fp anymore, then the
660 	   fork must already be closed. */
661 	if (!fp)
662 		return EBADF;
663 
664 	if (fp->icon) {
665 		free(fp->icon);
666 	}
667 	if (fp->resource) {
668 		return appledouble_close(volume,fp);
669 	}
670 
671 	switch(afp_closefork(volume,fp->forkid)) {
672 		case kFPNoErr:
673 			break;
674 		default:
675 		case kFPParamErr:
676 		case kFPMiscErr:
677 			ret=EIO;
678 			goto error;
679 	}
680 	remove_opened_fork(volume, fp);
681 
682 error:
683 	return ret;
684 }
685 
ml_getattr(struct afp_volume * volume,const char * path,struct stat * stbuf)686 int ml_getattr(struct afp_volume * volume, const char *path, struct stat *stbuf)
687 {
688 	char converted_path[AFP_MAX_PATH];
689 	int ret;
690 
691 	memset(stbuf, 0, sizeof(struct stat));
692 
693 	if (convert_path_to_afp(volume->server->path_encoding,
694 		converted_path,(char *) path,AFP_MAX_PATH)) {
695 		return -EINVAL;
696 	}
697 
698 	/* If this is a fake file (comment, finderinfo, rsrc), continue since
699 	 * we'll use the permissions of the real file */
700 
701 	ret = appledouble_getattr(volume,converted_path, stbuf);
702 
703 	if (ret<0) return ret;
704 	if (ret>0) return 0;
705 
706 	return ll_getattr(volume,path,stbuf,0);
707 }
708 
ml_write(struct afp_volume * volume,const char * path,const char * data,size_t size,off_t offset,struct afp_file_info * fp,uid_t uid,gid_t gid)709 int ml_write(struct afp_volume * volume, const char * path,
710 		const char *data, size_t size, off_t offset,
711                   struct afp_file_info * fp, uid_t uid,
712 		gid_t gid)
713 {
714 
715 	int ret,err=0;
716 	size_t totalwritten = 0;
717 	uint64_t sizetowrite, ignored;
718 	unsigned char flags = 0;
719 	unsigned int max_packet_size=volume->server->tx_quantum;
720 	off_t o=0;
721 	char converted_path[AFP_MAX_PATH];
722 /* TODO:
723    - handle nonblocking IO correctly
724 */
725 	if ((volume->server->using_version->av_number < 30) &&
726 		(size > AFP_MAX_AFP2_FILESIZE)) return -EFBIG;
727 
728 	if (convert_path_to_afp(volume->server->path_encoding,
729 		converted_path,(char *) path,AFP_MAX_PATH))
730 		return -EINVAL;
731 
732 	if (volume_is_readonly(volume))
733 		return -EACCES;
734 
735 	ret=appledouble_write(volume,fp,data,size,offset,&totalwritten);
736 
737 	if (ret<0) return ret;
738 	if (ret==1) return totalwritten;
739 
740 	/* Set the time and perms */
741 
742 	/* There's no way to do this in AFP < 3.0 */
743 	if (volume->extra_flags & VOLUME_EXTRA_FLAGS_VOL_SUPPORTS_UNIX) {
744 
745 		flags|=kFPUnixPrivsBit;
746 		set_uidgid(volume,fp,uid, gid);
747 		fp->unixprivs.permissions=0100644;
748 	};
749 
750 
751 	update_time(&fp->modification_date);
752 	flags|=kFPModDateBit;
753 
754 	ret=ll_write(volume,data,size,offset,fp,&totalwritten);
755 	if (ret<0) return ret;
756 	return totalwritten;
757 }
758 
ml_readlink(struct afp_volume * vol,const char * path,char * buf,size_t size)759 int ml_readlink(struct afp_volume * vol, const char * path,
760 	char *buf, size_t size)
761 {
762 	int rc,ret;
763 	struct afp_file_info fp;
764 	struct afp_rx_buffer buffer;
765 	char basename[AFP_MAX_PATH];
766 	char converted_path[AFP_MAX_PATH];
767 	unsigned int dirid;
768 	char link_path[AFP_MAX_PATH];
769 
770 	memset(buf,0,size);
771 	memset(link_path,0,AFP_MAX_PATH);
772 
773 	buffer.data=link_path;
774 	buffer.maxsize=size;
775 	buffer.size=0;
776 
777 	if (convert_path_to_afp(vol->server->path_encoding,
778 		converted_path,(char *) path,AFP_MAX_PATH)) {
779 		return -EINVAL;
780 	}
781 
782 	get_dirid(vol, converted_path, basename, &dirid);
783 
784 	/* Open the fork */
785 	rc=afp_openfork(vol,0, dirid,
786 		AFP_OPENFORK_ALLOWWRITE|AFP_OPENFORK_ALLOWREAD,
787 		basename,&fp);
788 	switch (rc) {
789 	case kFPAccessDenied:
790 		ret=EACCES;
791 		goto error;
792 	case kFPObjectNotFound:
793 		ret=ENOENT;
794 		goto error;
795 	case kFPObjectLocked:
796 		ret=EROFS;
797 		goto error;
798 	case kFPObjectTypeErr:
799 		ret=EISDIR;
800 		goto error;
801 	case kFPParamErr:
802 		ret=EACCES;
803 		goto error;
804 	case kFPTooManyFilesOpen:
805 		ret=EMFILE;
806 		goto error;
807 	case kFPVolLocked:
808 	case kFPDenyConflict:
809 	case kFPMiscErr:
810 	case kFPBitmapErr:
811 	case 0:
812 		ret=0;
813 		break;
814 	case -1:
815 	default:
816 		ret=EFAULT;
817 		goto error;
818 	}
819 
820 	add_opened_fork(vol, &fp);
821 
822 	/* Read the name of the file from it */
823 	if (vol->server->using_version->av_number < 30)
824 		rc=afp_read(vol, fp.forkid,0,size,&buffer);
825 	else
826 		rc=afp_readext(vol, fp.forkid,0,size,&buffer);
827 
828 	switch(rc) {
829 	case kFPAccessDenied:
830 		ret=EACCES;
831 		goto error;
832 	case kFPLockErr:
833 		ret=EBUSY;
834 		goto error;
835 	case kFPMiscErr:
836 	case kFPParamErr:
837 		ret=EIO;
838 		goto error;
839 	case kFPEOFErr:
840 	case kFPNoErr:
841 		break;
842 	}
843 
844 	switch(afp_closefork(vol,fp.forkid)) {
845 	case kFPNoErr:
846 		break;
847 	default:
848 	case kFPParamErr:
849 	case kFPMiscErr:
850 		ret=EIO;
851 		goto error;
852 	}
853 
854 	remove_opened_fork(vol, &fp);
855 
856 	/* Convert the name back precomposed UTF8 */
857 	convert_path_to_unix(vol->server->path_encoding,
858 		buf,(char *) link_path,AFP_MAX_PATH);
859 
860 	return 0;
861 
862 error:
863 	return -ret;
864 }
865 
ml_rmdir(struct afp_volume * vol,const char * path)866 int ml_rmdir(struct afp_volume * vol, const char *path)
867 {
868 	int ret,rc;
869 	unsigned int dirid;
870 	char basename[AFP_MAX_PATH];
871 	char converted_path[AFP_MAX_PATH];
872 
873 	if (invalid_filename(vol->server,path))
874 		return -ENAMETOOLONG;
875 
876 	if (convert_path_to_afp(vol->server->path_encoding,
877 		converted_path,(char *) path,AFP_MAX_PATH))
878 		return -EINVAL;
879 
880 	if (volume_is_readonly(vol))
881 		return -EACCES;
882 
883 	ret=appledouble_rmdir(vol,path);
884 	if (ret<0) return ret;
885 	if (ret==1) return 0;
886 
887 	get_dirid(vol, converted_path, basename, &dirid);
888 
889 	if (!is_dir(vol,dirid,basename)) return -ENOTDIR;
890 
891 	rc=afp_delete(vol,dirid,basename);
892 
893 	switch(rc) {
894 	case kFPAccessDenied:
895 		ret=EACCES;
896 		break;
897 	case kFPObjectLocked:
898 		ret=EBUSY;
899 		break;
900 	case kFPObjectNotFound:
901 		ret=ENOENT;
902 		break;
903 	case kFPVolLocked:
904 		ret=EACCES;
905 		break;
906 	case kFPDirNotEmpty:
907 		ret=ENOTEMPTY;
908 		break;
909 	case kFPObjectTypeErr:
910 	case kFPMiscErr:
911 	case kFPParamErr:
912 	case -1:
913 		ret=EINVAL;
914 		break;
915 	default:
916 		remove_did_entry(vol,converted_path);
917 		ret=0;
918 	}
919 	return -ret;
920 }
921 
ml_chown(struct afp_volume * vol,const char * path,uid_t uid,gid_t gid)922 int ml_chown(struct afp_volume * vol, const char * path,
923 	uid_t uid, gid_t gid)
924 {
925 	int ret;
926 	struct afp_file_info fp;
927 	int rc;
928 	unsigned int dirid;
929 	char basename[AFP_MAX_PATH];
930 	char converted_path[AFP_MAX_PATH];
931 
932 	if (convert_path_to_afp(vol->server->path_encoding,
933 		converted_path,(char *) path,AFP_MAX_PATH))
934 		return -EINVAL;
935 
936 	if (invalid_filename(vol->server,converted_path))
937 		return -ENAMETOOLONG;
938 
939 	if (volume_is_readonly(vol))
940 		return -EACCES;
941 
942 	ret=appledouble_chown(vol,path,uid,gid);
943 	if (ret<0) return ret;
944 	if (ret==1) return 0;
945 
946 	/* There's no way to do this in AFP < 3.0 */
947 	if (~ vol->extra_flags & VOLUME_EXTRA_FLAGS_VOL_SUPPORTS_UNIX) {
948 
949 		if (vol->extra_flags & VOLUME_EXTRA_FLAGS_IGNORE_UNIXPRIVS) {
950 			struct stat stbuf;
951 			/* See if the file exists */
952 			ret=ll_getattr(vol,path,&stbuf,0);
953 			return ret;
954 		}
955 
956 		return -ENOSYS;
957 	};
958 
959 	get_dirid(vol,converted_path,basename,&dirid );
960 
961 	if ((rc=get_unixprivs(vol,
962 		dirid,basename, &fp)))
963 		return rc;
964 
965 #if 0
966 FIXME
967 	set_uidgid(volume,&fp,uid,gid);
968 THIS IS the wrong set of returns to check...
969 #endif
970 	rc=set_unixprivs(vol, dirid, basename, &fp);
971 
972 	switch(rc) {
973 	case -ENOSYS:
974 		return -ENOSYS;
975 	case kFPNoErr:
976 		break;
977 	case kFPAccessDenied:
978 		return -EACCES;
979 	case kFPObjectNotFound:
980 		return -ENOENT;
981 	case kFPBitmapErr:
982 	case kFPMiscErr:
983 	case kFPObjectTypeErr:
984 	case kFPParamErr:
985 	default:
986 		break;
987 
988 	}
989 
990 	return 0;
991 }
992 
ml_truncate(struct afp_volume * vol,const char * path,off_t offset)993 int ml_truncate(struct afp_volume * vol, const char * path, off_t offset)
994 {
995 	int ret=0;
996 	char converted_path[AFP_MAX_PATH];
997 	struct afp_file_info *fp;
998 	int flags;
999 
1000 	if (convert_path_to_afp(vol->server->path_encoding,
1001 		converted_path,(char *) path,AFP_MAX_PATH))
1002 		return -EINVAL;
1003 
1004 	/* The approach here is to get the forkid by calling ml_open()
1005 	   (and not afp_openfork).  Note the fake afp_file_info used
1006 	   just to grab this forkid. */
1007 
1008 	if (invalid_filename(vol->server,converted_path))
1009 		return -ENAMETOOLONG;
1010 
1011 	if (volume_is_readonly(vol))
1012 		return -EACCES;
1013 
1014 	ret=appledouble_truncate(vol,path,offset);
1015 	if (ret<0) return ret;
1016 	if (ret==1) return 0;
1017 
1018 	/* Here, we're going to use the untranslated path since it is
1019 	   translated through the ml_open() */
1020 
1021 	flags=O_WRONLY;
1022 	if ((ml_open(vol,path,flags,&fp))) {
1023 		return ret;
1024 	};
1025 
1026 	if ((ret=ll_zero_file(vol,fp->forkid,0)))
1027 		goto out;
1028 
1029 	afp_closefork(vol,fp->forkid);
1030 	remove_opened_fork(vol, fp);
1031 	free(fp);
1032 
1033 out:
1034 	return -ret;
1035 }
1036 
1037 
ml_utime(struct afp_volume * vol,const char * path,struct utimbuf * timebuf)1038 int ml_utime(struct afp_volume * vol, const char * path,
1039 	struct utimbuf * timebuf)
1040 {
1041 
1042 	int ret=0;
1043 	unsigned int dirid;
1044 	struct afp_file_info fp;
1045 	char basename[AFP_MAX_PATH];
1046 	char converted_path[AFP_MAX_PATH];
1047 	int rc;
1048 
1049 	if (volume_is_readonly(vol))
1050 		return -EACCES;
1051 
1052 	memset(&fp,0,sizeof(struct afp_file_info));
1053 
1054 	fp.modification_date=timebuf->modtime;
1055 
1056 	if (invalid_filename(vol->server,path))
1057 		return -ENAMETOOLONG;
1058 
1059 	if (convert_path_to_afp(vol->server->path_encoding,
1060 		converted_path,(char *) path,AFP_MAX_PATH)) {
1061 		return -EINVAL;
1062 	}
1063 
1064 	ret=appledouble_utime(vol,path,timebuf);
1065 	if (ret<0) return ret;
1066 	if (ret==1) return 0;
1067 
1068 	get_dirid(vol,converted_path,basename,&dirid );
1069 
1070 	if (is_dir(vol,dirid,basename)) {
1071 		rc=afp_setdirparms(vol,
1072 			dirid,basename, kFPModDateBit, &fp);
1073 	} else {
1074 		rc=afp_setfileparms(vol,
1075 			dirid,basename, kFPModDateBit, &fp);
1076 	}
1077 
1078 	switch(rc) {
1079 	case kFPNoErr:
1080 		break;
1081 	case kFPAccessDenied:
1082 		return -EACCES;
1083 	case kFPObjectNotFound:
1084 		return -ENOENT;
1085 	case kFPBitmapErr:
1086 	case kFPMiscErr:
1087 	case kFPObjectTypeErr:
1088 	case kFPParamErr:
1089 	default:
1090 		break;
1091 
1092 	}
1093 
1094 	return -ret;
1095 }
1096 
1097 
ml_symlink(struct afp_volume * vol,const char * path1,const char * path2)1098 int ml_symlink(struct afp_volume *vol, const char * path1, const char * path2)
1099 {
1100 
1101 	int ret;
1102 	struct afp_file_info fp;
1103 	uint64_t written;
1104 	int rc;
1105 	unsigned int dirid2;
1106 	char basename2[AFP_MAX_PATH];
1107 	char converted_path1[AFP_MAX_PATH];
1108 	char converted_path2[AFP_MAX_PATH];
1109 
1110 	if (vol->server->using_version->av_number<30) {
1111 		/* No symlinks for AFP 2.x. */
1112 		ret=ENOSYS;
1113 		goto error;
1114 	}
1115 	/* Yes, you can create symlinks for AFP >=30.  Tested with 10.3.2 */
1116 
1117 	if (convert_path_to_afp(vol->server->path_encoding,
1118 		converted_path1,(char *) path1,AFP_MAX_PATH))
1119 		return -EINVAL;
1120 
1121 	if (convert_path_to_afp(vol->server->path_encoding,
1122 		converted_path2,(char *) path2,AFP_MAX_PATH))
1123 		return -EINVAL;
1124 
1125 	if (volume_is_readonly(vol))
1126 		return -EACCES;
1127 
1128 	ret=appledouble_symlink(vol,path1,path2);
1129 	if (ret<0) return ret;
1130 	if (ret==1) return 0;
1131 
1132 	get_dirid(vol,converted_path2,basename2,&dirid2 );
1133 
1134 	/* 1. create the file */
1135 	rc=afp_createfile(vol,kFPHardCreate,dirid2,basename2);
1136 	switch (rc) {
1137 	case kFPAccessDenied:
1138 		ret=EACCES;
1139 		goto error;
1140 	case kFPDiskFull:
1141 		ret=ENOSPC;
1142 		goto error;
1143 	case kFPObjectExists:
1144 		ret=EEXIST;
1145 		goto error;
1146 	case kFPObjectNotFound:
1147 		ret=ENOENT;
1148 		goto error;
1149 	case kFPFileBusy:
1150 	case kFPVolLocked:
1151 		ret=EBUSY;
1152 		goto error;
1153 	case kFPNoErr:
1154 		ret=0;
1155 		break;
1156 	default:
1157 	case kFPParamErr:
1158 	case kFPMiscErr:
1159 		ret=EIO;
1160 		goto error;
1161 	}
1162 
1163 	/* Open the fork */
1164 	rc=afp_openfork(vol,0,
1165 		dirid2,
1166 		AFP_OPENFORK_ALLOWWRITE|AFP_OPENFORK_ALLOWREAD,
1167 		basename2,&fp);
1168 	switch (ret) {
1169 	case kFPAccessDenied:
1170 		ret=EACCES;
1171 		goto error;
1172 	case kFPObjectNotFound:
1173 		ret=ENOENT;
1174 		goto error;
1175 	case kFPObjectLocked:
1176 		ret=EROFS;
1177 		goto error;
1178 	case kFPObjectTypeErr:
1179 		ret=EISDIR;
1180 		goto error;
1181 	case kFPParamErr:
1182 		ret=EACCES;
1183 		goto error;
1184 	case kFPTooManyFilesOpen:
1185 		ret=EMFILE;
1186 		goto error;
1187 	case 0:
1188 		ret=0;
1189 		break;
1190 	case kFPVolLocked:
1191 	case kFPDenyConflict:
1192 	case kFPMiscErr:
1193 	case kFPBitmapErr:
1194 	case -1:
1195 	default:
1196 		ret=EFAULT;
1197 		goto error;
1198 	}
1199 
1200 	add_opened_fork(vol, &fp);
1201 
1202 	/* Write the name of the file to it */
1203 
1204 	rc=afp_writeext(vol,fp.forkid,0,strlen(converted_path1),
1205 		converted_path1,&written);
1206 
1207 	switch(afp_closefork(vol,fp.forkid)) {
1208 	case kFPNoErr:
1209 		break;
1210 	default:
1211 	case kFPParamErr:
1212 	case kFPMiscErr:
1213 		ret=EIO;
1214 		goto error;
1215 	}
1216 
1217 	remove_opened_fork(vol, &fp);
1218 
1219 	/* And now for the undocumented magic */
1220 	memset(&fp.finderinfo,0,32);
1221 	fp.finderinfo[0]='s';
1222 	fp.finderinfo[1]='l';
1223 	fp.finderinfo[2]='n';
1224 	fp.finderinfo[3]='k';
1225 	fp.finderinfo[4]='r';
1226 	fp.finderinfo[5]='h';
1227 	fp.finderinfo[6]='a';
1228 	fp.finderinfo[7]='p';
1229 
1230 	rc=afp_setfiledirparms(vol,dirid2,basename2,
1231 		kFPFinderInfoBit, &fp);
1232 	switch (rc) {
1233 	case kFPAccessDenied:
1234 		ret=EPERM;
1235 		goto error;
1236 	case kFPBitmapErr:
1237 		/* This is the case where it isn't supported */
1238 		ret=ENOSYS;
1239 		goto error;
1240 	case kFPObjectNotFound:
1241 		ret=ENOENT;
1242 		goto error;
1243 	case 0:
1244 		ret=0;
1245 		break;
1246 	case kFPMiscErr:
1247 	case kFPObjectTypeErr:
1248 	case kFPParamErr:
1249 	default:
1250 		ret=EIO;
1251 		goto error;
1252 	}
1253 error:
1254 	return -ret;
1255 };
1256 
ml_rename(struct afp_volume * vol,const char * path_from,const char * path_to)1257 int ml_rename(struct afp_volume * vol,
1258 	const char * path_from, const char * path_to)
1259 {
1260 	int ret,rc;
1261 	char basename_from[AFP_MAX_PATH];
1262 	char basename_to[AFP_MAX_PATH];
1263 	char converted_path_from[AFP_MAX_PATH];
1264 	char converted_path_to[AFP_MAX_PATH];
1265 	unsigned int dirid_from,dirid_to;
1266 
1267 	if (convert_path_to_afp(vol->server->path_encoding,
1268 		converted_path_from,(char *) path_from,AFP_MAX_PATH))
1269 		return -EINVAL;
1270 
1271 	if (convert_path_to_afp(vol->server->path_encoding,
1272 		converted_path_to,(char *) path_to,AFP_MAX_PATH))
1273 		return -EINVAL;
1274 
1275 	if (volume_is_readonly(vol))
1276 		return -EACCES;
1277 
1278 	get_dirid(vol, converted_path_from, basename_from, &dirid_from);
1279 	get_dirid(vol, converted_path_to, basename_to, &dirid_to);
1280 
1281 	if (is_dir(vol,dirid_to,converted_path_to)) {
1282 		rc=afp_moveandrename(vol,
1283 			dirid_from,dirid_to,
1284 			basename_from,basename_to,basename_from);
1285 	} else {
1286 		rc=afp_moveandrename(vol,
1287 			dirid_from,dirid_to,
1288 			basename_from,NULL,basename_to);
1289 	}
1290 	switch(rc) {
1291 	case kFPObjectLocked:
1292 	case kFPAccessDenied:
1293 		ret=EACCES;
1294 		break;
1295 	case kFPCantRename:
1296 		ret=EROFS;
1297 		break;
1298 	case kFPObjectExists:
1299 		/* First, remove the old file. */
1300 		switch(afp_delete(vol,dirid_to,basename_to)) {
1301 
1302 		case kFPAccessDenied:
1303 			ret=EACCES;
1304 			break;
1305 		case kFPObjectLocked:
1306 			ret=EBUSY;
1307 			break;
1308 		case kFPObjectNotFound:
1309 			ret=ENOENT;
1310 			break;
1311 		case kFPVolLocked:
1312 			ret=EACCES;
1313 			break;
1314 		case kFPDirNotEmpty:
1315 			ret=ENOTEMPTY;
1316 			break;
1317 		case kFPObjectTypeErr:
1318 		case kFPMiscErr:
1319 		case kFPParamErr:
1320 		case -1:
1321 			ret=EINVAL;
1322 			break;
1323 		}
1324 		/* Then, do the move again */
1325 		switch(afp_moveandrename(vol,
1326 			dirid_from,dirid_to,
1327 			basename_from,NULL,basename_to)) {
1328 		case kFPObjectLocked:
1329 		case kFPAccessDenied:
1330 			ret=EACCES;
1331 			break;
1332 		case kFPCantRename:
1333 			ret=EROFS;
1334 			break;
1335 		case kFPObjectExists:
1336 		case kFPObjectNotFound:
1337 			ret=ENOENT;
1338 			break;
1339 		case kFPParamErr:
1340 		case kFPMiscErr:
1341 			ret=EIO;
1342 		default:
1343 		case kFPNoErr:
1344 			ret=0;
1345 			break;
1346 		}
1347 		break;
1348 	case kFPObjectNotFound:
1349 		ret=ENOENT;
1350 	case kFPNoErr:
1351 		ret=0;
1352 		break;
1353 	default:
1354 	case kFPParamErr:
1355 	case kFPMiscErr:
1356 		ret=EIO;
1357 	}
1358 	return -ret;
1359 }
1360 
ml_statfs(struct afp_volume * vol,const char * path,struct statvfs * stat)1361 int ml_statfs(struct afp_volume * vol, const char *path, struct statvfs *stat)
1362 {
1363 	unsigned short flags;
1364 	int ret;
1365 
1366 	memset(stat,0,sizeof(*stat));
1367 
1368 	if (vol->server->using_version->av_number<30)
1369 		flags = kFPVolBytesFreeBit | kFPVolBytesTotalBit ;
1370 	else
1371 		flags = kFPVolExtBytesFreeBit | kFPVolExtBytesTotalBit | kFPVolBlockSizeBit;
1372 
1373 	ret=afp_getvolparms(vol,flags);
1374 	switch(ret) {
1375 	case kFPNoErr:
1376 		break;
1377 	case kFPParamErr:
1378 	case kFPMiscErr:
1379 	default:
1380 		return -EIO;
1381 	}
1382 	if (vol->stat.f_bsize==0) vol->stat.f_bsize=4096;
1383 	stat->f_blocks=vol->stat.f_blocks / vol->stat.f_bsize;
1384 	stat->f_bfree=vol->stat.f_bfree / vol->stat.f_bsize;
1385 	stat->f_bsize=vol->stat.f_bsize;
1386 	stat->f_frsize=vol->stat.f_bsize;
1387 	stat->f_bavail=stat->f_bfree;
1388 	stat->f_frsize=0;
1389 	stat->f_files=0;
1390 	stat->f_ffree=0;
1391 	stat->f_favail=0;
1392 	stat->f_fsid=0;
1393 	stat->f_flag=0;
1394 	stat->f_namemax=255;
1395 	return 0;
1396 
1397 }
1398 
ml_passwd(struct afp_server * server,char * username,char * oldpasswd,char * newpasswd)1399 int ml_passwd(struct afp_server *server,
1400                 char * username, char * oldpasswd, char * newpasswd)
1401 {
1402 	afp_dopasswd(server,server->using_uam,username,oldpasswd,newpasswd);
1403 	return 0;
1404 }
1405