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

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

Dont add '/' to fuse path when the cwd is only '/', breaks some IPC stuff

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