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

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

Add access() calls to PBIFS fuse module

  • Property mode set to 100644
File size: 14.0 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_access(const char *path, int mask)
255{
256        char newpath[MAXPATHLEN];
257        get_modified_path(newpath, path);
258
259        int res = access(newpath, mask);
260        if (res == -1)
261                return -errno;
262        return 0;
263}
264
265static int pbi_getattr(const char *path, struct stat *stbuf)
266{
267        char newpath[MAXPATHLEN];
268        get_modified_path(newpath, path);
269
270        /*
271        char testcmd[MAXPATHLEN];
272        strcpy(testcmd, "echo \"getattr: ");
273        strcat(testcmd, newpath);
274        strcat(testcmd, "\" >> /tmp/newpath");
275        system(testcmd);
276        */
277
278        int res = lstat(newpath, stbuf);
279        if (res == -1)
280                return -errno;
281
282        return res;
283}
284
285static int pbi_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
286                         off_t offset, struct fuse_file_info *fi)
287{
288        (void) offset;
289        (void) fi;
290
291        char newpath[MAXPATHLEN];
292        get_modified_path(newpath, path);
293
294        // Avoid a recursive directory tree
295        if ( strpos(newpath, "/usr/pbi/.mounts") == 0)
296           return 0;
297
298        DIR *openDir = opendir(newpath);
299
300        struct dirent *de;
301        while ((de = readdir(openDir)) != NULL) {
302               
303                struct stat st;
304                memset(&st, 0, sizeof(st));
305                st.st_ino = de->d_ino;
306                st.st_mode = de->d_type << 12;
307
308                if (filler(buf, de->d_name, &st, 0)) break;
309        }
310
311        closedir(openDir);
312
313        return 0;
314}
315
316static int pbi_open(const char *path, struct fuse_file_info *fi)
317{
318
319        char newpath[MAXPATHLEN];
320        get_modified_path(newpath, path);
321
322        int fd = open(newpath, fi->flags);
323        if (fd == -1)
324                return -errno;
325        fi->fh = (unsigned long)fd;
326
327        return 0;
328}
329
330static int pbi_read(const char *path, char *buf, size_t size, off_t offset,
331                      struct fuse_file_info *fi)
332{
333        (void) fi;
334
335        int result = pread(fi->fh, buf, size, offset);
336        if (result == -1)
337                return -errno;
338
339        return result;
340}
341
342static int pbi_statfs(const char *path, struct statvfs *stbuf) 
343{
344        char newpath[MAXPATHLEN];
345        get_modified_path(newpath, path);
346
347
348        return statvfs(newpath, stbuf);
349}
350
351static int pbi_symlink(const char *from, const char *to)
352{
353        char newpath[MAXPATHLEN];
354        get_modified_path(newpath, from);
355
356        return symlink(newpath, to);
357}
358
359static int pbi_readlink(const char *path, char *buf, size_t size)
360{
361        char newpath[MAXPATHLEN];
362        get_modified_path(newpath, path);
363
364        int result= readlink(newpath, buf, size - 1);
365        if (result == -1)
366                return -errno;
367        buf[result] = '\0';
368        return 0;
369}
370
371static int pbi_chmod(const char *path, mode_t mode)
372{
373        char newpath[MAXPATHLEN];
374        get_modified_path(newpath, path);
375
376        int result = lchmod(newpath, mode);
377        if (result == -1) 
378                return -errno;
379        return 0;
380}
381
382static int pbi_chown(const char *path, uid_t uid, gid_t gid)
383{
384        char newpath[MAXPATHLEN];
385        get_modified_path(newpath, path);
386
387        int result = lchown(newpath, uid, gid);
388        if (result == -1)
389                return -errno;
390        return 0;       
391}
392
393static int pbi_create(const char *path, mode_t mode, struct fuse_file_info *fi)
394{
395        char newpath[MAXPATHLEN];
396        get_modified_path(newpath, path);
397
398        int res = open(newpath, fi->flags, 0);
399        if (res == -1)
400                return -errno;
401
402        newfile_chown(newpath);
403        fchmod(res, mode);
404
405        fi->fh = res;
406        return 0;
407}
408
409static int pbi_flush(const char *path, struct fuse_file_info *fi)
410{
411        int fd = dup(fi->fh);
412        if (fd == -1) {
413                if (fsync(fi->fh) == -1) 
414                        return -EIO;
415                return -errno;
416        }
417
418        int res = close(fd);
419        if (res == -1)
420                return -errno;
421
422        return 0;
423}
424
425static int pbi_fsync(const char *path, int isdatasync, struct fuse_file_info *fi)
426{
427        (void) isdatasync;
428        int res = fsync(fi->fh);
429        if (res == -1)
430                return -errno;
431        return 0;
432}
433
434static int pbi_link(const char *from, const char *to)
435{
436        char newpath[MAXPATHLEN];
437        get_modified_path(newpath, from);
438
439        int res = link(newpath, to);
440        if (res == -1)
441                return -errno;
442        return 0;
443}
444
445static int pbi_mkdir(const char *path, mode_t mode)
446{
447        char newpath[MAXPATHLEN];
448        get_modified_path(newpath, path);
449
450        int res = mkdir(newpath, 0);
451        if (res == -1)
452                return -errno;
453
454        newfile_chown(newpath);
455        chmod(newpath, mode);
456
457        return 0;
458}
459
460static int pbi_mknod(const char *path, mode_t mode, dev_t rdev)
461{
462        int ftype = mode & S_IFMT;
463        int fpath = mode & (S_ISUID| S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
464
465        char newpath[MAXPATHLEN];
466        get_modified_path(newpath, path);
467
468        int res = mknod(newpath, ftype, rdev);
469        if (res == -1)
470                return -errno;
471
472        newfile_chown(newpath);
473        chmod(newpath, fpath);
474
475        return 0;
476}
477
478static int pbi_release(const char *path, struct fuse_file_info *fi)
479{
480        (void) path;
481
482        int res = close(fi->fh);
483        if (res == -1)
484                return -errno;
485
486        return 0;
487}
488
489static int pbi_rename(const char *from, const char *to)
490{
491        char newfrom[MAXPATHLEN];
492        char newto[MAXPATHLEN];
493        get_modified_path(newfrom, from);
494        get_modified_path(newto, to);
495
496        if ( strcmp(from, newfrom) != 0 || strcmp(to, newto) != 0 )
497        {
498                // Unlink the target first
499                if (0 == access(newto, F_OK)) {
500                        if ( unlink(newto) == -1 )
501                                return -errno;
502                }
503
504                // Doing a across file-system move, need to use copy instead of rename
505                if ( cp(newto, newfrom) != 0)
506                        return -errno;
507
508                // Set owner / permissions now
509                struct stat fst;
510                stat(newfrom, &fst);
511                chown(newto, fst.st_uid,fst.st_gid);
512                chmod(newto, fst.st_mode);
513
514                if ( unlink(newfrom) == -1 )
515                        return -errno;
516               
517        } else {
518                int res = rename(newfrom, newto);
519                if (res == -1)
520                        return -errno;
521        }
522        return 0;
523}
524
525int pbi_rmdir(const char *path)
526{
527        char newpath[MAXPATHLEN];
528        get_modified_path(newpath, path);
529
530        int res = rmdir(newpath);
531        if (res == -1)
532                return -errno;
533        return 0;
534}
535
536static int pbi_truncate(const char *path, off_t size)
537{
538        char newpath[MAXPATHLEN];
539        get_modified_path(newpath, path);
540
541        int res = truncate(newpath, size);
542        if (res == -1)
543                return -errno;
544        return 0;
545}
546
547int pbi_unlink(const char *path)
548{
549        char newpath[MAXPATHLEN];
550        get_modified_path(newpath, path);
551
552        int res = unlink(newpath);
553        if (res == -1)
554                return -errno;
555        return 0;
556}
557
558static int pbi_utimens(const char *path, const struct timespec ts[2])
559{
560        char newpath[MAXPATHLEN];
561        get_modified_path(newpath, path);
562
563        struct timeval tv[2];
564        tv[0].tv_sec = ts[0].tv_sec;
565        tv[0].tv_usec = ts[0].tv_nsec / 1000;
566        tv[1].tv_sec = ts[0].tv_sec;
567        tv[1].tv_usec = ts[0].tv_nsec / 1000;
568        int res = lutimes(newpath, tv);
569        if (res == -1)
570                return -errno;
571        return 0;
572}
573
574static int pbi_write(const char *path, const char *buf, size_t size, off_t offset,
575        struct fuse_file_info *fi)
576{
577        (void) path;
578        int written = pwrite(fi->fh, buf, size, offset);
579        if (written == -1)
580                return -errno;
581        return written;
582}
583
584static struct fuse_operations pbi_oper = {
585        .access         = pbi_access,
586        .chmod          = pbi_chmod,
587        .chown          = pbi_chown,
588        .create         = pbi_create,
589        .flush          = pbi_flush,
590        .fsync          = pbi_fsync,
591        .getattr        = pbi_getattr,
592        .link           = pbi_link,
593        .mkdir          = pbi_mkdir,
594        .mknod          = pbi_mknod,
595        .open           = pbi_open,
596        .read           = pbi_read,
597        .readlink       = pbi_readlink,
598        .readdir        = pbi_readdir,
599        .release        = pbi_release,
600        .rename         = pbi_rename,
601        .rmdir          = pbi_rmdir,
602        .statfs         = pbi_statfs,
603        .symlink        = pbi_symlink,
604        .truncate       = pbi_truncate,
605        .unlink         = pbi_unlink,
606        .utimens        = pbi_utimens,
607        .write          = pbi_write,
608};
609
610int main(int argc, char *argv[])
611{
612
613        if ( argc != 4 ) 
614        {
615                printf("Usage: %s <pbidir> <hintsdir> <mountpoint>\n", argv[0]);
616                printf("Where: ");
617                printf("       <pbidir>  = PBI directory with a new /local directory\n");
618                printf("                   and optional /linux directory\n");
619                printf("     <hintsdir>  = Directory with new ld-elf*.so.hints files\n");
620                printf("   <mountpoint>  = Directory to mount PBI container\n");
621                return 1;
622        }
623
624        if ( strlen(argv[1]) >= MAXPATHLEN || strlen(argv[2]) >= MAXPATHLEN ) {
625                printf("MAXPATHLEN violation!\n");
626                return 1;
627        }
628
629        // Verify the pbidir exists
630        strcpy(pbidir, argv[1]);
631        strcat(pbidir, "/local");
632        if (0 != access(pbidir, F_OK)) {
633                if (ENOENT == errno) {
634                        // does not exist
635                        printf("ERROR: The directory %s does not exist!", pbidir);
636                        return 1;
637                }
638                if (ENOTDIR == errno) {
639                        // not a directory
640                        printf("ERROR: %s is not a directory!", pbidir);
641                        return 1;
642                }
643        }
644
645
646        // Add a linux dir
647        strcpy(linuxdir, argv[1]);
648        strcat(linuxdir, "/linux");
649
650        // Verify that hintsdir exists
651        strcpy(hintsdir, argv[2]);
652        if (0 != access(hintsdir, F_OK)) {
653                if (ENOENT == errno) {
654                        // does not exist
655                        printf("ERROR: The directory %s does not exist!", pbidir);
656                        return 1;
657                }
658                if (ENOTDIR == errno) {
659                        // not a directory
660                        printf("ERROR: %s is not a directory!", pbidir);
661                        return 1;
662                }
663        }
664
665
666        // Set the mountpoint before starting fuse
667        char *newargv[1024];
668        newargv[0] = argv[0];
669        newargv[1] = argv[3];
670
671        // Set some fuse options
672        struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
673        for ( int i = 0; i < argc ; i++ )
674        {
675                if ( i == 1 || i == 2 )
676                        continue;
677                fuse_opt_add_arg(&args, argv[i]);
678        }
679        fuse_opt_add_arg(&args, "-oallow_other");
680
681        return fuse_main(args.argc, args.argv, &pbi_oper, NULL);
682}
683
Note: See TracBrowser for help on using the repository browser.