source: src-sh/pbi-manager/pbifs/main.c @ 3d98a68

releng/10.0.1releng/10.0.2releng/10.0.3releng/10.1
Last change on this file since 3d98a68 was 3d98a68, checked in by Kris Moore <kris@…>, 10 months ago

Add a fix to prevent resursive listings of the fuse mounted directories

  • Property mode set to 100644
File size: 13.8 KB
Line 
1/*
2 *  AUTHOR: Kris Moore <kris@pcbsd.org>
3 * LICENSE: BSD
4 * COMMENT: FUSE based filesystem for creating virtual PBI container chroot environments
5 *
6 */
7
8#define FUSE_USE_VERSION 26
9
10#include <dirent.h>
11#include <errno.h>
12#include <fcntl.h>
13#include <fuse.h>
14#include <stddef.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <sys/mount.h>
19#include <sys/param.h>
20#include <sys/types.h>
21#include <unistd.h>
22
23
24#define NOT_FOUND -1
25
26// Set some globals
27char hintsdir[MAXPATHLEN];
28char pbidir[MAXPATHLEN];
29char linuxdir[MAXPATHLEN];
30
31int strpos(const char *haystack, char *needle)
32{
33   char *p = strstr(haystack, needle);
34   if (p) {
35      return p - haystack;
36   }
37   return NOT_FOUND;
38}
39
40char *replace_str_all(const char *str, const char *old, const char *new)
41{
42        char *ret, *r;
43        const char *p, *q;
44        size_t oldlen = strlen(old);
45        size_t count, retlen, newlen = strlen(new);
46
47        if (oldlen != newlen) {
48                for (count = 0, p = str; (q = strstr(p, old)) != NULL; p = q + oldlen)
49                        count++;
50                /* this is undefined if p - str > PTRDIFF_MAX */
51                retlen = p - str + strlen(p) + count * (newlen - oldlen);
52        } else
53                retlen = strlen(str);
54
55        if ((ret = malloc(retlen + 1)) == NULL)
56                return NULL;
57
58        for (r = ret, p = str; (q = strstr(p, old)) != NULL; p = q + oldlen) {
59                /* this is undefined if q - p > PTRDIFF_MAX */
60                ptrdiff_t l = q - p;
61                memcpy(r, p, l);
62                r += l;
63                memcpy(r, new, newlen);
64                r += newlen;
65        }
66        strcpy(r, p);
67
68        return ret;
69}
70
71const char *replace_str(const char *str, char *orig, char *rep)
72{
73        static char buffer[4096];
74        char *p;
75
76        if(!(p = strstr(str, orig)))  // Is 'orig' even in 'str'?
77                return str;
78
79        strncpy(buffer, str, p-str); // Copy characters from 'str' start to 'orig' st$
80        buffer[p-str] = '\0';
81
82        sprintf(buffer+(p-str), "%s%s", rep, p+strlen(orig));
83
84        return buffer;
85}
86
87/**********************************************************************************
88*  OK, this is where all the magic happens.
89*  We are going to be taking the current filesystem path, and checking it against
90*  a list of ones we want to "virtualize" so that it appears we have a different
91*  /usr/local namespace
92***********************************************************************************/
93int get_modified_path(char *npath, const char *opath)
94{
95        // First, lets look for calls to /var/run/ld-elf*.so.hints
96        // These need to be replaced with calls to our new hints file in hintsdir
97        if ( strpos(opath, "/var/run/") == 0 )
98        {
99                if ( strcmp(opath, "/var/run/ld-elf.so.hints") == 0 )
100                {
101                        strcpy(npath, hintsdir);
102                        strcat(npath, "/ld-elf.so.hints");
103                        return 0;
104                }
105                if ( strcmp(opath, "/var/run/ld-elf32.so.hints") == 0 )
106                {
107                        strcpy(npath, hintsdir);
108                        strcat(npath, "/ld-elf32.so.hints");
109                        return 0;
110                }
111        }
112
113        if ( strcmp(opath, "/etc/rc.d/ldconfig") == 0 )
114        {
115                strcpy(npath, "/usr/pbi/.ldconfig");
116                return 0;
117        }
118
119        // Check if we have a virtual linux /compat directory to fake as well
120        if ( strpos(opath, "/compat/linux") == 0 )
121        {
122                // If this is /compat/linux/proc, lets use the systems
123                if ( strpos(opath, "/compat/linux/proc") == 0 ) {
124                        strcpy(npath, opath);
125                        return 0;
126                }
127
128                if (0 == access(linuxdir, F_OK))
129                {
130                        strcpy(npath, replace_str(opath, "/compat/linux", linuxdir));
131                        return 0;
132                }
133                strcpy(npath, opath);
134                return 0;
135        }
136
137        // Lastly, lets do all the parsing for /usr/local matching
138        if ( strpos(opath, "/usr/local") == 0 )
139        {
140                // Use the systems copies of some font / icon directories
141                if ( strpos(opath, "/usr/local/etc/fonts") == 0 ) {
142                        strcpy(npath, opath);
143                        return 0;
144                }
145                if ( strpos(opath, "/usr/local/lib/X11/fonts") == 0 ) {
146                        strcpy(npath, opath);
147                        return 0;
148                }
149                if ( strpos(opath, "/usr/local/lib/X11/icons") == 0 ) {
150                        strcpy(npath, opath);
151                        return 0;
152                }
153                if ( strpos(opath, "/usr/local/share/icons") == 0 )
154                {
155                        strcpy(npath, opath);
156                        return 0;
157                }
158                if ( strpos(opath, "/usr/local/share/font-") == 0 ) {
159                        strcpy(npath, opath);
160                        return 0;
161                }
162
163                // Look for some of our callback utils
164                if ( strpos(opath, "/usr/local/bin/pbisyscmd") == 0 ) {
165                        strcpy(npath, "/usr/pbi/.pbisyscmd");
166                        return 0;
167                }
168                if ( strpos(opath, "/usr/local/bin/openwith") == 0 ) {
169                        strcpy(npath, "/usr/pbi/.pbisyscmd");
170                        return 0;
171                }
172                if ( strpos(opath, "/usr/local/bin/xdg-open") == 0 ) {
173                        strcpy(npath, "/usr/pbi/.pbisyscmd");
174                        return 0;
175                }
176       
177                strcpy(npath, replace_str(opath, "/usr/local", pbidir));
178                return 0;
179        }
180
181        strcpy(npath, opath);
182        return 0;
183}
184
185int newfile_chown(const char *path) {
186        struct fuse_context *ctx = fuse_get_context();
187        if (ctx->uid != 0 && ctx->gid != 0) {
188                int res = lchown(path, ctx->uid, ctx->gid);
189                if (res)
190                        return -errno;
191        }
192        return 0;
193}
194
195int cp(const char *to, const char *from)
196{
197        int fd_to, fd_from;
198        char buf[4096];
199        ssize_t nread;
200        int saved_errno;
201
202        fd_from = open(from, O_RDONLY);
203        if (fd_from < 0)
204                return -1;
205
206        fd_to = open(to, O_WRONLY | O_CREAT | O_EXCL, 0666);
207        if (fd_to < 0)
208                goto out_error;
209
210        while (nread = read(fd_from, buf, sizeof buf), nread > 0)
211        {
212                char *out_ptr = buf;
213                ssize_t nwritten;
214
215                do {
216                        nwritten = write(fd_to, out_ptr, nread);
217
218                        if (nwritten >= 0)
219                        {
220                                nread -= nwritten;
221                                out_ptr += nwritten;
222                        }
223                        else if (errno != EINTR)
224                        {
225                                goto out_error;
226                        }
227                } while (nread > 0);
228        }
229
230        if (nread == 0)
231        {
232                if (close(fd_to) < 0)
233                {
234                        fd_to = -1;
235                        goto out_error;
236                }
237                close(fd_from);
238
239                /* Success! */
240                return 0;
241        }
242
243        out_error:
244                saved_errno = errno;
245
246                close(fd_from);
247                if (fd_to >= 0)
248                        close(fd_to);
249
250                errno = saved_errno;
251                return -1;
252}
253
254static int pbi_getattr(const char *path, struct stat *stbuf)
255{
256        char newpath[MAXPATHLEN];
257        get_modified_path(newpath, path);
258
259        /*
260        char testcmd[MAXPATHLEN];
261        strcpy(testcmd, "echo \"getattr: ");
262        strcat(testcmd, newpath);
263        strcat(testcmd, "\" >> /tmp/newpath");
264        system(testcmd);
265        */
266
267        int res = lstat(newpath, stbuf);
268        if (res == -1)
269                return -errno;
270
271        return res;
272}
273
274static int pbi_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
275                         off_t offset, struct fuse_file_info *fi)
276{
277        (void) offset;
278        (void) fi;
279
280        char newpath[MAXPATHLEN];
281        get_modified_path(newpath, path);
282
283        // Avoid a recursive directory tree
284        if ( strpos(newpath, "/usr/pbi/.mounts") == 0)
285           return 0;
286
287        DIR *openDir = opendir(newpath);
288
289        struct dirent *de;
290        while ((de = readdir(openDir)) != NULL) {
291               
292                struct stat st;
293                memset(&st, 0, sizeof(st));
294                st.st_ino = de->d_ino;
295                st.st_mode = de->d_type << 12;
296
297                if (filler(buf, de->d_name, &st, 0)) break;
298        }
299
300        closedir(openDir);
301
302        return 0;
303}
304
305static int pbi_open(const char *path, struct fuse_file_info *fi)
306{
307
308        char newpath[MAXPATHLEN];
309        get_modified_path(newpath, path);
310
311        int fd = open(newpath, fi->flags);
312        if (fd == -1)
313                return -errno;
314        fi->fh = (unsigned long)fd;
315
316        return 0;
317}
318
319static int pbi_read(const char *path, char *buf, size_t size, off_t offset,
320                      struct fuse_file_info *fi)
321{
322        (void) fi;
323
324        int result = pread(fi->fh, buf, size, offset);
325        if (result == -1)
326                return -errno;
327
328        return result;
329}
330
331static int pbi_statfs(const char *path, struct statvfs *stbuf) 
332{
333        char newpath[MAXPATHLEN];
334        get_modified_path(newpath, path);
335
336
337        return statvfs(newpath, stbuf);
338}
339
340static int pbi_symlink(const char *from, const char *to)
341{
342        char newpath[MAXPATHLEN];
343        get_modified_path(newpath, from);
344
345        return symlink(newpath, to);
346}
347
348static int pbi_readlink(const char *path, char *buf, size_t size)
349{
350        char newpath[MAXPATHLEN];
351        get_modified_path(newpath, path);
352
353        int result= readlink(newpath, buf, size - 1);
354        if (result == -1)
355                return -errno;
356        buf[result] = '\0';
357        return 0;
358}
359
360static int pbi_chmod(const char *path, mode_t mode)
361{
362        char newpath[MAXPATHLEN];
363        get_modified_path(newpath, path);
364
365        int result = lchmod(newpath, mode);
366        if (result == -1) 
367                return -errno;
368        return 0;
369}
370
371static int pbi_chown(const char *path, uid_t uid, gid_t gid)
372{
373        char newpath[MAXPATHLEN];
374        get_modified_path(newpath, path);
375
376        int result = lchown(newpath, uid, gid);
377        if (result == -1)
378                return -errno;
379        return 0;       
380}
381
382static int pbi_create(const char *path, mode_t mode, struct fuse_file_info *fi)
383{
384        char newpath[MAXPATHLEN];
385        get_modified_path(newpath, path);
386
387        int res = open(newpath, fi->flags, 0);
388        if (res == -1)
389                return -errno;
390
391        newfile_chown(newpath);
392        fchmod(res, mode);
393
394        fi->fh = res;
395        return 0;
396}
397
398static int pbi_flush(const char *path, struct fuse_file_info *fi)
399{
400        int fd = dup(fi->fh);
401        if (fd == -1) {
402                if (fsync(fi->fh) == -1) 
403                        return -EIO;
404                return -errno;
405        }
406
407        int res = close(fd);
408        if (res == -1)
409                return -errno;
410
411        return 0;
412}
413
414static int pbi_fsync(const char *path, int isdatasync, struct fuse_file_info *fi)
415{
416        (void) isdatasync;
417        int res = fsync(fi->fh);
418        if (res == -1)
419                return -errno;
420        return 0;
421}
422
423static int pbi_link(const char *from, const char *to)
424{
425        char newpath[MAXPATHLEN];
426        get_modified_path(newpath, from);
427
428        int res = link(newpath, to);
429        if (res == -1)
430                return -errno;
431        return 0;
432}
433
434static int pbi_mkdir(const char *path, mode_t mode)
435{
436        char newpath[MAXPATHLEN];
437        get_modified_path(newpath, path);
438
439        int res = mkdir(newpath, 0);
440        if (res == -1)
441                return -errno;
442
443        newfile_chown(newpath);
444        chmod(newpath, mode);
445
446        return 0;
447}
448
449static int pbi_mknod(const char *path, mode_t mode, dev_t rdev)
450{
451        int ftype = mode & S_IFMT;
452        int fpath = mode & (S_ISUID| S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
453
454        char newpath[MAXPATHLEN];
455        get_modified_path(newpath, path);
456
457        int res = mknod(newpath, ftype, rdev);
458        if (res == -1)
459                return -errno;
460
461        newfile_chown(newpath);
462        chmod(newpath, fpath);
463
464        return 0;
465}
466
467static int pbi_release(const char *path, struct fuse_file_info *fi)
468{
469        (void) path;
470
471        int res = close(fi->fh);
472        if (res == -1)
473                return -errno;
474
475        return 0;
476}
477
478static int pbi_rename(const char *from, const char *to)
479{
480        char newfrom[MAXPATHLEN];
481        char newto[MAXPATHLEN];
482        get_modified_path(newfrom, from);
483        get_modified_path(newto, to);
484
485        if ( strcmp(from, newfrom) != 0 || strcmp(to, newto) != 0 )
486        {
487                // Unlink the target first
488                if (0 == access(newto, F_OK)) {
489                        if ( unlink(newto) == -1 )
490                                return -errno;
491                }
492
493                // Doing a across file-system move, need to use copy instead of rename
494                if ( cp(newto, newfrom) != 0)
495                        return -errno;
496
497                // Set owner / permissions now
498                struct stat fst;
499                stat(newfrom, &fst);
500                chown(newto, fst.st_uid,fst.st_gid);
501                chmod(newto, fst.st_mode);
502
503                if ( unlink(newfrom) == -1 )
504                        return -errno;
505               
506        } else {
507                int res = rename(newfrom, newto);
508                if (res == -1)
509                        return -errno;
510        }
511        return 0;
512}
513
514int pbi_rmdir(const char *path)
515{
516        char newpath[MAXPATHLEN];
517        get_modified_path(newpath, path);
518
519        int res = rmdir(newpath);
520        if (res == -1)
521                return -errno;
522        return 0;
523}
524
525static int pbi_truncate(const char *path, off_t size)
526{
527        char newpath[MAXPATHLEN];
528        get_modified_path(newpath, path);
529
530        int res = truncate(newpath, size);
531        if (res == -1)
532                return -errno;
533        return 0;
534}
535
536int pbi_unlink(const char *path)
537{
538        char newpath[MAXPATHLEN];
539        get_modified_path(newpath, path);
540
541        int res = unlink(newpath);
542        if (res == -1)
543                return -errno;
544        return 0;
545}
546
547static int pbi_utimens(const char *path, const struct timespec ts[2])
548{
549        char newpath[MAXPATHLEN];
550        get_modified_path(newpath, path);
551
552        struct timeval tv[2];
553        tv[0].tv_sec = ts[0].tv_sec;
554        tv[0].tv_usec = ts[0].tv_nsec / 1000;
555        tv[1].tv_sec = ts[0].tv_sec;
556        tv[1].tv_usec = ts[0].tv_nsec / 1000;
557        int res = lutimes(newpath, tv);
558        if (res == -1)
559                return -errno;
560        return 0;
561}
562
563static int pbi_write(const char *path, const char *buf, size_t size, off_t offset,
564        struct fuse_file_info *fi)
565{
566        (void) path;
567        int written = pwrite(fi->fh, buf, size, offset);
568        if (written == -1)
569                return -errno;
570        return written;
571}
572
573static struct fuse_operations pbi_oper = {
574        .chmod          = pbi_chmod,
575        .chown          = pbi_chown,
576        .create         = pbi_create,
577        .flush          = pbi_flush,
578        .fsync          = pbi_fsync,
579        .getattr        = pbi_getattr,
580        .link           = pbi_link,
581        .mkdir          = pbi_mkdir,
582        .mknod          = pbi_mknod,
583        .open           = pbi_open,
584        .read           = pbi_read,
585        .readlink       = pbi_readlink,
586        .readdir        = pbi_readdir,
587        .release        = pbi_release,
588        .rename         = pbi_rename,
589        .rmdir          = pbi_rmdir,
590        .statfs         = pbi_statfs,
591        .symlink        = pbi_symlink,
592        .truncate       = pbi_truncate,
593        .unlink         = pbi_unlink,
594        .utimens        = pbi_utimens,
595        .write          = pbi_write,
596};
597
598int main(int argc, char *argv[])
599{
600
601        if ( argc != 4 ) 
602        {
603                printf("Usage: %s <pbidir> <hintsdir> <mountpoint>\n", argv[0]);
604                printf("Where: ");
605                printf("       <pbidir>  = PBI directory with a new /local directory\n");
606                printf("                   and optional /linux directory\n");
607                printf("     <hintsdir>  = Directory with new ld-elf*.so.hints files\n");
608                printf("   <mountpoint>  = Directory to mount PBI container\n");
609                return 1;
610        }
611
612        if ( strlen(argv[1]) >= MAXPATHLEN || strlen(argv[2]) >= MAXPATHLEN ) {
613                printf("MAXPATHLEN violation!\n");
614                return 1;
615        }
616
617        // Verify the pbidir exists
618        strcpy(pbidir, argv[1]);
619        strcat(pbidir, "/local");
620        if (0 != access(pbidir, F_OK)) {
621                if (ENOENT == errno) {
622                        // does not exist
623                        printf("ERROR: The directory %s does not exist!", pbidir);
624                        return 1;
625                }
626                if (ENOTDIR == errno) {
627                        // not a directory
628                        printf("ERROR: %s is not a directory!", pbidir);
629                        return 1;
630                }
631        }
632
633
634        // Add a linux dir
635        strcpy(linuxdir, argv[1]);
636        strcat(linuxdir, "/linux");
637
638        // Verify that hintsdir exists
639        strcpy(hintsdir, argv[2]);
640        if (0 != access(hintsdir, F_OK)) {
641                if (ENOENT == errno) {
642                        // does not exist
643                        printf("ERROR: The directory %s does not exist!", pbidir);
644                        return 1;
645                }
646                if (ENOTDIR == errno) {
647                        // not a directory
648                        printf("ERROR: %s is not a directory!", pbidir);
649                        return 1;
650                }
651        }
652
653
654        // Set the mountpoint before starting fuse
655        char *newargv[1024];
656        newargv[0] = argv[0];
657        newargv[1] = argv[3];
658
659        // Set some fuse options
660        struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
661        for ( int i = 0; i < argc ; i++ )
662        {
663                if ( i == 1 || i == 2 )
664                        continue;
665                fuse_opt_add_arg(&args, argv[i]);
666        }
667        fuse_opt_add_arg(&args, "-oallow_other");
668
669        return fuse_main(args.argc, args.argv, &pbi_oper, NULL);
670}
671
Note: See TracBrowser for help on using the repository browser.