source: src-sh/pbi-manager/pbifs/main.c @ 6d736fb

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

Add a nullfs backed "socket" passthrough layer to pbifs, this allows sockets in
/var/run to be available to PBIs, same as in /tmp

This fixes bugs with CUPS not being able to work in PBIs

  • Property mode set to 100644
File size: 15.2 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        // Get the real pathname we are using now
96        char rPath[MAXPATHLEN];
97        // Check if this is a relative path, or absolute
98        if ( strpos(opath, "/") == 0 ) {
99                strcpy(rPath, opath);
100        } else {
101                getwd(rPath);
102                // If the cwd is just '/' then don't add anything to string
103                if ( strcmp(rPath, "/") == 0 ) {
104                        strcpy(rPath, opath);
105                } else {
106                        strcat(rPath, opath);
107                }
108
109                // Debugging
110                /*
111                char testcmd[MAXPATHLEN];
112                strcpy(testcmd, "echo \"failedpath: ");
113                strcat(testcmd, opath);
114                strcat(testcmd, " -> ");
115                strcat(testcmd, rPath);
116                strcat(testcmd, "\" >> /tmp/failedpath");
117                system(testcmd);
118                */
119        }
120
121        // First, lets look for calls to /var/run/ld-elf*.so.hints
122        // These need to be replaced with calls to our new hints file in hintsdir
123        if ( strpos(rPath, "/var/run") == 0 )
124        {
125                if ( strcmp(rPath, "/var/run/ld-elf.so.hints") == 0 )
126                {
127                        strcpy(npath, hintsdir);
128                        strcat(npath, "/ld-elf.so.hints");
129                        return 0;
130                }
131                if ( strcmp(rPath, "/var/run/ld-elf32.so.hints") == 0 )
132                {
133                        strcpy(npath, hintsdir);
134                        strcat(npath, "/ld-elf32.so.hints");
135                        return 0;
136                }
137
138                // Check for requests to socket files
139                if ( strpos(rPath, ".sock") != -1 )
140                {
141                        // Return the "nullfs-mounted" /var/run
142                        strcpy(npath, replace_str(rPath, "/var/run", "/usr/pbi/varrun"));
143                        return 0;
144                }
145                // Check for requests to socket files
146                if ( strpos(rPath, "socket") != -1 )
147                {
148                        // Return the "nullfs-mounted" /var/run
149                        strcpy(npath, replace_str(rPath, "/var/run", "/usr/pbi/varrun"));
150                        return 0;
151                }
152
153        }
154
155        if ( strcmp(rPath, "/etc/rc.d/ldconfig") == 0 )
156        {
157                strcpy(npath, "/usr/pbi/.ldconfig");
158                return 0;
159        }
160
161        // Check if we have a virtual linux /compat directory to fake as well
162        if ( strpos(rPath, "/compat/linux") == 0 )
163        {
164                // If this is /compat/linux/proc, lets use the systems
165                if ( strpos(rPath, "/compat/linux/proc") == 0 ) {
166                        strcpy(npath, rPath);
167                        return 0;
168                }
169                // If this is /compat/linux/sys, lets use the systems
170                if ( strpos(rPath, "/compat/linux/sys") == 0 ) {
171                        strcpy(npath, rPath);
172                        return 0;
173                }
174
175                if (0 == access(linuxdir, F_OK))
176                {
177                        strcpy(npath, replace_str(rPath, "/compat/linux", linuxdir));
178                        return 0;
179                }
180                strcpy(npath, rPath);
181                return 0;
182        }
183
184        // Lastly, lets do all the parsing for /usr/local matching
185        if ( strpos(rPath, "/usr/local") == 0 )
186        {
187                // Use the systems copies of some font / icon directories
188                if ( strpos(rPath, "/usr/local/etc/fonts") == 0 ) {
189                        strcpy(npath, rPath);
190                        return 0;
191                }
192                if ( strpos(rPath, "/usr/local/lib/X11/fonts") == 0 ) {
193                        strcpy(npath, rPath);
194                        return 0;
195                }
196                if ( strpos(rPath, "/usr/local/lib/X11/icons") == 0 ) {
197                        strcpy(npath, rPath);
198                        return 0;
199                }
200                if ( strpos(rPath, "/usr/local/share/icons") == 0 )
201                {
202                        strcpy(npath, rPath);
203                        return 0;
204                }
205                if ( strpos(rPath, "/usr/local/share/font-") == 0 ) {
206                        strcpy(npath, rPath);
207                        return 0;
208                }
209
210                // Look for some of our callback utils
211                if ( strpos(rPath, "/usr/local/bin/pbisyscmd") == 0 ) {
212                        strcpy(npath, "/usr/pbi/.pbisyscmd");
213                        return 0;
214                }
215                if ( strpos(rPath, "/usr/local/bin/openwith") == 0 ) {
216                        strcpy(npath, "/usr/pbi/.pbisyscmd");
217                        return 0;
218                }
219                if ( strpos(rPath, "/usr/local/bin/xdg-open") == 0 ) {
220                        strcpy(npath, "/usr/pbi/.pbisyscmd");
221                        return 0;
222                }
223       
224                strcpy(npath, replace_str(rPath, "/usr/local", pbidir));
225                return 0;
226        }
227
228        strcpy(npath, rPath);
229        return 0;
230}
231
232int newfile_chown(const char *path) {
233        struct fuse_context *ctx = fuse_get_context();
234        if (ctx->uid != 0 && ctx->gid != 0) {
235                int res = lchown(path, ctx->uid, ctx->gid);
236                if (res)
237                        return -errno;
238        }
239        return 0;
240}
241
242int cp(const char *to, const char *from)
243{
244        int fd_to, fd_from;
245        char buf[4096];
246        ssize_t nread;
247        int saved_errno;
248
249        fd_from = open(from, O_RDONLY);
250        if (fd_from < 0)
251                return -1;
252
253        fd_to = open(to, O_WRONLY | O_CREAT | O_EXCL, 0666);
254        if (fd_to < 0)
255                goto out_error;
256
257        while (nread = read(fd_from, buf, sizeof buf), nread > 0)
258        {
259                char *out_ptr = buf;
260                ssize_t nwritten;
261
262                do {
263                        nwritten = write(fd_to, out_ptr, nread);
264
265                        if (nwritten >= 0)
266                        {
267                                nread -= nwritten;
268                                out_ptr += nwritten;
269                        }
270                        else if (errno != EINTR)
271                        {
272                                goto out_error;
273                        }
274                } while (nread > 0);
275        }
276
277        if (nread == 0)
278        {
279                if (close(fd_to) < 0)
280                {
281                        fd_to = -1;
282                        goto out_error;
283                }
284                close(fd_from);
285
286                /* Success! */
287                return 0;
288        }
289
290        out_error:
291                saved_errno = errno;
292
293                close(fd_from);
294                if (fd_to >= 0)
295                        close(fd_to);
296
297                errno = saved_errno;
298                return -1;
299}
300
301static int pbi_access(const char *path, int mask)
302{
303        char newpath[MAXPATHLEN];
304        get_modified_path(newpath, path);
305
306        int res = access(newpath, mask);
307        if (res == -1)
308                return -errno;
309        return 0;
310}
311
312static int pbi_getattr(const char *path, struct stat *stbuf)
313{
314        char newpath[MAXPATHLEN];
315        get_modified_path(newpath, path);
316
317        /*
318        char testcmd[MAXPATHLEN];
319        strcpy(testcmd, "echo \"getattr: ");
320        strcat(testcmd, newpath);
321        strcat(testcmd, "\" >> /tmp/newpath");
322        system(testcmd);
323        */
324
325        int res = lstat(newpath, stbuf);
326        if (res == -1)
327                return -errno;
328
329        return res;
330}
331
332static int pbi_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
333                         off_t offset, struct fuse_file_info *fi)
334{
335        (void) offset;
336        (void) fi;
337
338        char newpath[MAXPATHLEN];
339        get_modified_path(newpath, path);
340
341        // Avoid a recursive directory tree
342        if ( strpos(newpath, "/usr/pbi/.mounts") == 0)
343           return 0;
344
345        DIR *openDir = opendir(newpath);
346
347        struct dirent *de;
348        while ((de = readdir(openDir)) != NULL) {
349               
350                struct stat st;
351                memset(&st, 0, sizeof(st));
352                st.st_ino = de->d_ino;
353                st.st_mode = de->d_type << 12;
354
355                if (filler(buf, de->d_name, &st, 0)) break;
356        }
357
358        closedir(openDir);
359
360        return 0;
361}
362
363static int pbi_open(const char *path, struct fuse_file_info *fi)
364{
365
366        char newpath[MAXPATHLEN];
367        get_modified_path(newpath, path);
368
369        int fd = open(newpath, fi->flags);
370        if (fd == -1)
371                return -errno;
372        fi->fh = (unsigned long)fd;
373
374        return 0;
375}
376
377static int pbi_read(const char *path, char *buf, size_t size, off_t offset,
378                      struct fuse_file_info *fi)
379{
380        (void) fi;
381
382        int result = pread(fi->fh, buf, size, offset);
383        if (result == -1)
384                return -errno;
385
386        return result;
387}
388
389static int pbi_statfs(const char *path, struct statvfs *stbuf) 
390{
391        char newpath[MAXPATHLEN];
392        get_modified_path(newpath, path);
393
394
395        return statvfs(newpath, stbuf);
396}
397
398static int pbi_symlink(const char *from, const char *to)
399{
400        char newpath[MAXPATHLEN];
401        get_modified_path(newpath, from);
402
403        return symlink(newpath, to);
404}
405
406static int pbi_readlink(const char *path, char *buf, size_t size)
407{
408        char newpath[MAXPATHLEN];
409        get_modified_path(newpath, path);
410
411        int result= readlink(newpath, buf, size - 1);
412        if (result == -1)
413                return -errno;
414        buf[result] = '\0';
415        return 0;
416}
417
418static int pbi_chmod(const char *path, mode_t mode)
419{
420        char newpath[MAXPATHLEN];
421        get_modified_path(newpath, path);
422
423        int result = lchmod(newpath, mode);
424        if (result == -1) 
425                return -errno;
426        return 0;
427}
428
429static int pbi_chown(const char *path, uid_t uid, gid_t gid)
430{
431        char newpath[MAXPATHLEN];
432        get_modified_path(newpath, path);
433
434        int result = lchown(newpath, uid, gid);
435        if (result == -1)
436                return -errno;
437        return 0;       
438}
439
440static int pbi_create(const char *path, mode_t mode, struct fuse_file_info *fi)
441{
442        char newpath[MAXPATHLEN];
443        get_modified_path(newpath, path);
444
445        int res = open(newpath, fi->flags, 0);
446        if (res == -1)
447                return -errno;
448
449        newfile_chown(newpath);
450        fchmod(res, mode);
451
452        fi->fh = res;
453        return 0;
454}
455
456static int pbi_flush(const char *path, struct fuse_file_info *fi)
457{
458        int fd = dup(fi->fh);
459        if (fd == -1) {
460                if (fsync(fi->fh) == -1) 
461                        return -EIO;
462                return -errno;
463        }
464
465        int res = close(fd);
466        if (res == -1)
467                return -errno;
468
469        return 0;
470}
471
472static int pbi_fsync(const char *path, int isdatasync, struct fuse_file_info *fi)
473{
474        (void) isdatasync;
475        int res = fsync(fi->fh);
476        if (res == -1)
477                return -errno;
478        return 0;
479}
480
481static int pbi_link(const char *from, const char *to)
482{
483        char newpath[MAXPATHLEN];
484        get_modified_path(newpath, from);
485
486        int res = link(newpath, to);
487        if (res == -1)
488                return -errno;
489        return 0;
490}
491
492static int pbi_mkdir(const char *path, mode_t mode)
493{
494        char newpath[MAXPATHLEN];
495        get_modified_path(newpath, path);
496
497        int res = mkdir(newpath, 0);
498        if (res == -1)
499                return -errno;
500
501        newfile_chown(newpath);
502        chmod(newpath, mode);
503
504        return 0;
505}
506
507static int pbi_mknod(const char *path, mode_t mode, dev_t rdev)
508{
509        int ftype = mode & S_IFMT;
510        int fpath = mode & (S_ISUID| S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
511
512        char newpath[MAXPATHLEN];
513        get_modified_path(newpath, path);
514
515        int res = mknod(newpath, ftype, rdev);
516        if (res == -1)
517                return -errno;
518
519        newfile_chown(newpath);
520        chmod(newpath, fpath);
521
522        return 0;
523}
524
525static int pbi_release(const char *path, struct fuse_file_info *fi)
526{
527        (void) path;
528
529        int res = close(fi->fh);
530        if (res == -1)
531                return -errno;
532
533        return 0;
534}
535
536static int pbi_rename(const char *from, const char *to)
537{
538        char newfrom[MAXPATHLEN];
539        char newto[MAXPATHLEN];
540        get_modified_path(newfrom, from);
541        get_modified_path(newto, to);
542
543        if ( strcmp(from, newfrom) != 0 || strcmp(to, newto) != 0 )
544        {
545                // Unlink the target first
546                if (0 == access(newto, F_OK)) {
547                        if ( unlink(newto) == -1 )
548                                return -errno;
549                }
550
551                // Doing a across file-system move, need to use copy instead of rename
552                if ( cp(newto, newfrom) != 0)
553                        return -errno;
554
555                // Set owner / permissions now
556                struct stat fst;
557                stat(newfrom, &fst);
558                chown(newto, fst.st_uid,fst.st_gid);
559                chmod(newto, fst.st_mode);
560
561                if ( unlink(newfrom) == -1 )
562                        return -errno;
563               
564        } else {
565                int res = rename(newfrom, newto);
566                if (res == -1)
567                        return -errno;
568        }
569        return 0;
570}
571
572int pbi_rmdir(const char *path)
573{
574        char newpath[MAXPATHLEN];
575        get_modified_path(newpath, path);
576
577        int res = rmdir(newpath);
578        if (res == -1)
579                return -errno;
580        return 0;
581}
582
583static int pbi_truncate(const char *path, off_t size)
584{
585        char newpath[MAXPATHLEN];
586        get_modified_path(newpath, path);
587
588        int res = truncate(newpath, size);
589        if (res == -1)
590                return -errno;
591        return 0;
592}
593
594int pbi_unlink(const char *path)
595{
596        char newpath[MAXPATHLEN];
597        get_modified_path(newpath, path);
598
599        int res = unlink(newpath);
600        if (res == -1)
601                return -errno;
602        return 0;
603}
604
605static int pbi_utimens(const char *path, const struct timespec ts[2])
606{
607        char newpath[MAXPATHLEN];
608        get_modified_path(newpath, path);
609
610        struct timeval tv[2];
611        tv[0].tv_sec = ts[0].tv_sec;
612        tv[0].tv_usec = ts[0].tv_nsec / 1000;
613        tv[1].tv_sec = ts[0].tv_sec;
614        tv[1].tv_usec = ts[0].tv_nsec / 1000;
615        int res = lutimes(newpath, tv);
616        if (res == -1)
617                return -errno;
618        return 0;
619}
620
621static int pbi_write(const char *path, const char *buf, size_t size, off_t offset,
622        struct fuse_file_info *fi)
623{
624        (void) path;
625        int written = pwrite(fi->fh, buf, size, offset);
626        if (written == -1)
627                return -errno;
628        return written;
629}
630
631static struct fuse_operations pbi_oper = {
632        .access         = pbi_access,
633        .chmod          = pbi_chmod,
634        .chown          = pbi_chown,
635        .create         = pbi_create,
636        .flush          = pbi_flush,
637        .fsync          = pbi_fsync,
638        .getattr        = pbi_getattr,
639        .link           = pbi_link,
640        .mkdir          = pbi_mkdir,
641        .mknod          = pbi_mknod,
642        .open           = pbi_open,
643        .read           = pbi_read,
644        .readlink       = pbi_readlink,
645        .readdir        = pbi_readdir,
646        .release        = pbi_release,
647        .rename         = pbi_rename,
648        .rmdir          = pbi_rmdir,
649        .statfs         = pbi_statfs,
650        .symlink        = pbi_symlink,
651        .truncate       = pbi_truncate,
652        .unlink         = pbi_unlink,
653        .utimens        = pbi_utimens,
654        .write          = pbi_write,
655};
656
657int main(int argc, char *argv[])
658{
659
660        if ( argc != 4 ) 
661        {
662                printf("Usage: %s <pbidir> <hintsdir> <mountpoint>\n", argv[0]);
663                printf("Where: ");
664                printf("       <pbidir>  = PBI directory with a new /local directory\n");
665                printf("                   and optional /linux directory\n");
666                printf("     <hintsdir>  = Directory with new ld-elf*.so.hints files\n");
667                printf("   <mountpoint>  = Directory to mount PBI container\n");
668                return 1;
669        }
670
671        if ( strlen(argv[1]) >= MAXPATHLEN || strlen(argv[2]) >= MAXPATHLEN ) {
672                printf("MAXPATHLEN violation!\n");
673                return 1;
674        }
675
676        // Verify the pbidir exists
677        strcpy(pbidir, argv[1]);
678        strcat(pbidir, "/local");
679        if (0 != access(pbidir, F_OK)) {
680                if (ENOENT == errno) {
681                        // does not exist
682                        printf("ERROR: The directory %s does not exist!", pbidir);
683                        return 1;
684                }
685                if (ENOTDIR == errno) {
686                        // not a directory
687                        printf("ERROR: %s is not a directory!", pbidir);
688                        return 1;
689                }
690        }
691
692
693        // Add a linux dir
694        strcpy(linuxdir, argv[1]);
695        strcat(linuxdir, "/linux");
696
697        // Verify that hintsdir exists
698        strcpy(hintsdir, argv[2]);
699        if (0 != access(hintsdir, F_OK)) {
700                if (ENOENT == errno) {
701                        // does not exist
702                        printf("ERROR: The directory %s does not exist!", pbidir);
703                        return 1;
704                }
705                if (ENOTDIR == errno) {
706                        // not a directory
707                        printf("ERROR: %s is not a directory!", pbidir);
708                        return 1;
709                }
710        }
711
712
713        // Set the mountpoint before starting fuse
714        char *newargv[1024];
715        newargv[0] = argv[0];
716        newargv[1] = argv[3];
717
718        // Set some fuse options
719        struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
720        for ( int i = 0; i < argc ; i++ )
721        {
722                if ( i == 1 || i == 2 )
723                        continue;
724                fuse_opt_add_arg(&args, argv[i]);
725        }
726        fuse_opt_add_arg(&args, "-oallow_other");
727        fuse_opt_add_arg(&args, "-s");
728
729        return fuse_main(args.argc, args.argv, &pbi_oper, NULL);
730}
731
Note: See TracBrowser for help on using the repository browser.