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