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

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

Fix some bugs with relative file pathnames in pbifs, and make sure to load
procfs / linprocfs / linsysfs in PBIs

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