Ejemplo n.º 1
0
static int try_open(const char *dev, char **devp)
{
    int fd;
     
    if (restore_privs())
	return -1;
    fd = open(dev, O_RDWR);
    if (drop_privs())
	return -1;
    if (fd != -1) {
        *devp = strdup(dev);
        if (*devp == NULL) {
            fprintf(stderr, "%s: failed to allocate memory\n", progname);
            close(fd);
            fd = -1;
        }
    } else if (errno == ENODEV ||
               errno == ENOENT) /* check for ENOENT too, for the udev case */
        return -2;
    else {
        fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
                strerror(errno));
    }
    return fd;
}
Ejemplo n.º 2
0
static int do_mount(const char *dev, const char *mnt, const char *type,
                    mode_t rootmode, int fd, int fuseflags)
{
    int res;
    struct fuse_mount_data data;
    int flags = MS_NOSUID | MS_NODEV;

    if(getuid() != 0) {
        res = drop_privs();
        if(res == -1)
            return -1;

        flags |= MS_PERMISSION;
    }

    data.version = FUSE_KERNEL_VERSION;
    data.fd = fd;
    data.rootmode = rootmode;
    data.uid = getuid();
    data.flags = fuseflags;

    res = mount(dev, mnt, type, flags, &data);
    if(res == -1)
        fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno));

    if(getuid() != 0)
        restore_privs();

    return res;
}
Ejemplo n.º 3
0
static int try_open_fuse_device(char **devp)
{
	int fd;
	int err;

	drop_privs();
	fd = try_open(FUSE_DEV_NEW, devp, 0);
	restore_privs();
	if (fd >= 0)
		return fd;

	err = fd;
	fd = try_open(FUSE_DEV_OLD, devp, 1);
	if (fd >= 0)
		return fd;

	return err;
}
Ejemplo n.º 4
0
int main(int argc, char *argv[])
{
	int ch;
	int fd;
	int res;
	char *origmnt;
	char *mnt;
	static int unmount = 0;
	static int lazy = 0;
	static int quiet = 0;
	char *devfd;
	char *commfd;
	int cfd;
	const char *opts = "";

	static const struct option long_opts[] = {
		{"unmount", no_argument, NULL, 'u'},
		{"lazy",    no_argument, NULL, 'z'},
		{"quiet",   no_argument, NULL, 'q'},
		{"help",    no_argument, NULL, 'h'},
		{"version", no_argument, NULL, 'V'},
		{0, 0, 0, 0}};

	progname = strdup(argv[0]);
	if (progname == NULL) {
		fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
		exit(1);
	}

	while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts,
				 NULL)) != -1) {
		switch (ch) {
		case 'h':
			usage();
			break;

		case 'V':
			show_version();
			break;

		case 'o':
			opts = optarg;
			break;

		case 'u':
			unmount = 1;
			break;

		case 'z':
			lazy = 1;
			break;

		case 'q':
			quiet = 1;
			break;

		default:
			exit(1);
		}
	}

	if (lazy && !unmount) {
		fprintf(stderr, "%s: -z can only be used with -u\n", progname);
		exit(1);
	}

	if (optind >= argc) {
		fprintf(stderr, "%s: missing mountpoint argument\n", progname);
		exit(1);
	} else if (argc > optind + 1) {
		fprintf(stderr, "%s: extra arguments after the mountpoint\n",
			progname);
		exit(1);
	}

	origmnt = argv[optind];

	drop_privs();
	mnt = fuse_mnt_resolve_path(progname, origmnt);
	if (mnt != NULL) {
		res = chdir("/");
		if (res == -1) {
			fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
			exit(1);
		}
	}
	restore_privs();
	if (mnt == NULL)
		exit(1);

	umask(033);
	if (unmount) {
		if (geteuid() == 0)
			res = unmount_fuse(mnt, quiet, lazy);
		else {
			res = umount2(mnt, lazy ? 2 : 0);
			if (res == -1 && !quiet)
				fprintf(stderr,
					"%s: failed to unmount %s: %s\n",
					progname, mnt, strerror(errno));
		}
		if (res == -1)
			exit(1);
		return 0;
	}

	devfd = getenv(FUSE_DEVFD_ENV);
	if (devfd == NULL) {
		commfd = getenv(FUSE_COMMFD_ENV);
		if (commfd == NULL) {
			fprintf(stderr, "%s: old style mounting not supported\n",
				progname);
			exit(1);
		}
	}

	fd = mount_fuse(mnt, opts, devfd);
	if (fd == -1)
		exit(1);

	if (devfd == NULL) {
		cfd = atoi(commfd);
		res = send_fd(cfd, fd);
		if (res == -1)
			exit(1);
	}

	return 0;
}
Ejemplo n.º 5
0
static int mount_fuse(const char *mnt, const char *opts, char *devfd)
{
	int res;
	int fd;
	char *dev;
	struct stat stbuf;
	char *type = NULL;
	char *source = NULL;
	char *mnt_opts = NULL;
	const char *real_mnt = mnt;
	int mountpoint_fd = -1;

	fd = devfd ? check_fuse_device(devfd, &dev) : open_fuse_device(&dev);
	if (fd == -1)
		return -1;

	drop_privs();
	read_conf();

	if (getuid() != 0 && mount_max != -1) {
		int mount_count = count_fuse_fs();
		if (mount_count >= mount_max) {
			fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in /etc/fuse.conf\n", progname);
			goto fail_close_fd;
		}
	}

	res = check_version(dev);
	if (res != -1) {
		res = check_perm(&real_mnt, &stbuf, &mountpoint_fd);
		restore_privs();
		if (res != -1)
			res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT,
				       fd, opts, dev, &source, &mnt_opts,
				       stbuf.st_size);
	} else
		restore_privs();

	if (mountpoint_fd != -1)
		close(mountpoint_fd);

	if (res == -1)
		goto fail_close_fd;

	res = chdir("/");
	if (res == -1) {
		fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
		goto fail_close_fd;
	}

	if (geteuid() == 0) {
		res = add_mount(source, mnt, type, mnt_opts);
		if (res == -1) {
			/* Can't clean up mount in a non-racy way */
			goto fail_close_fd;
		}
	}

out_free:
	free(source);
	free(type);
	free(mnt_opts);
	free(dev);

	return fd;

fail_close_fd:
	close(fd);
	fd = -1;
	goto out_free;
}
Ejemplo n.º 6
0
static int do_mount(const char *mnt, char **typep, mode_t rootmode,
                    int fd, const char *opts, const char *dev, char **sourcep,
                    char **mnt_optsp)
{
    int res;
    int flags = MS_NOSUID | MS_NODEV;
    char *optbuf;
    char *mnt_opts = NULL;
    const char *s;
    char *d;
    char *fsname = NULL;
    char *source = NULL;
    char *type = NULL;
    int blkdev = 0;

    optbuf = (char *) malloc(strlen(opts) + 128);
    if (!optbuf) {
        fprintf(stderr, "%s: failed to allocate memory\n", progname);
        return -1;
    }

    for (s = opts, d = optbuf; *s;) {
        unsigned len;
        const char *fsname_str = "fsname=";
        for (len = 0; s[len] && s[len] != ','; len++);
        if (begins_with(s, fsname_str)) {
            if (!get_string_opt(s, len, fsname_str, &fsname))
                goto err;
        } else if (opt_eq(s, len, "blkdev")) {
            blkdev = 1;
        } else if (!begins_with(s, "fd=") &&
                   !begins_with(s, "rootmode=") &&
                   !begins_with(s, "user_id=") &&
                   !begins_with(s, "group_id=")) {
            int on;
            int flag;
            int skip_option = 0;
            if (opt_eq(s, len, "large_read")) {
                struct utsname utsname;
                unsigned kmaj, kmin;
                res = uname(&utsname);
                if (res == 0 &&
                    sscanf(utsname.release, "%u.%u", &kmaj, &kmin) == 2 &&
                    (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
                    fprintf(stderr, "%s: note: 'large_read' mount option is "
			    "deprecated for %i.%i kernels\n", progname, kmaj, kmin);
                    skip_option = 1;
                }
            }
            if (!skip_option) {
                if (find_mount_flag(s, len, &on, &flag)) {
                    if (on)
                        flags |= flag;
                    else
                        flags  &= ~flag;
                } else {
                    memcpy(d, s, len);
                    d += len;
                    *d++ = ',';
                }
            }
        }
        s += len;
        if (*s)
            s++;
    }
    *d = '\0';
    res = get_mnt_opts(flags, optbuf, &mnt_opts);
    if (res == -1)
        goto err;

    sprintf(d, "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
            fd, rootmode, getuid(), getgid());

    source = malloc((fsname ? strlen(fsname) : 0) + strlen(dev) + 32);

    type = malloc(32);
    if (!type || !source) {
        fprintf(stderr, "%s: failed to allocate memory\n", progname);
        goto err;
    }

    strcpy(type, blkdev ? "fuseblk" : "fuse");

    if (fsname)
        strcpy(source, fsname);
    else
        strcpy(source, dev);

    if (restore_privs())
	goto err;
    
    res = mount(source, mnt, type, flags, optbuf);
    if (res == -1 && errno == EINVAL) {
        /* It could be an old version not supporting group_id */
        sprintf(d, "fd=%i,rootmode=%o,user_id=%i", fd, rootmode, getuid());
        res = mount(source, mnt, type, flags, optbuf);
    }
    
    if (drop_privs())
	goto err;
    
    if (res == -1) {
        int errno_save = errno;
        if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
            fprintf(stderr, "%s: 'fuseblk' support missing\n", progname);
	else {
            fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno_save));
	    if (errno_save == EPERM)
		    fprintf(stderr, "User doesn't have privilege to mount. "
			    "For more information\nplease see: "
			    "http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n");
	}
	goto err;
    } else {
        *sourcep = source;
        *typep = type;
        *mnt_optsp = mnt_opts;
    }
out:    
    free(fsname);
    free(optbuf);
    return res;
err:
    free(source);
    free(type);
    free(mnt_opts);
    res = -1;
    goto out;
}
Ejemplo n.º 7
0
static int mount_fuse(const char *mnt, const char *opts)
{
    int res;
    int fd;
    char *dev;
    struct stat stbuf;
    char *type = NULL;
    char *source = NULL;
    char *mnt_opts = NULL;
    const char *real_mnt = mnt;
    int currdir_fd = -1;
    int mountpoint_fd = -1;

    fd = open_fuse_device(&dev);
    if (fd == -1)
        return -1;

    drop_privs();
    read_conf();

    if (getuid() != 0 && mount_max != -1) {
        int mount_count = count_fuse_fs();
        if (mount_count >= mount_max) {
            fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in /etc/fuse.conf\n", progname);
            close(fd);
            return -1;
        }
    }

    res = check_version(dev);
    if (res != -1) {
        res = check_perm(&real_mnt, &stbuf, &currdir_fd, &mountpoint_fd);
        restore_privs();
        if (res != -1)
            res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT, fd, opts,
                           dev, &source, &mnt_opts, stbuf.st_size);
    } else
        restore_privs();

    if (currdir_fd != -1) {
        fchdir(currdir_fd);
        close(currdir_fd);
    }
    if (mountpoint_fd != -1)
        close(mountpoint_fd);

    if (res == -1) {
        close(fd);
        return -1;
    }

    if (geteuid() == 0) {
        res = add_mount(source, mnt, type, mnt_opts);
        if (res == -1) {
            umount2(mnt, 2); /* lazy umount */
            close(fd);
            return -1;
        }
    }

    free(source);
    free(type);
    free(mnt_opts);
    free(dev);

    return fd;
}
Ejemplo n.º 8
0
static int unmount_fuse(const char *mnt, int quiet, int lazy)
{
    int res;
    struct mntent *entp;
    FILE *fp;
    FILE *newfp = NULL;
    const char *user = NULL;
    char uidstr[32];
    unsigned uidlen = 0;
    int found;
    int issymlink = 0;
    struct stat stbuf;
    const char *mtab = _PATH_MOUNTED;
    const char *mtab_new = _PATH_MOUNTED "~fuse~";

    if (lstat(mtab, &stbuf) != -1 && S_ISLNK(stbuf.st_mode))
        issymlink = 1;

    fp = setmntent(mtab, "r");
    if (fp == NULL) {
	fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
		strerror(errno));
	return -1;
    }

    if (!issymlink) {
        newfp = setmntent(mtab_new, "w");
        if (newfp == NULL) {
            fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab_new,
                    strerror(errno));
            endmntent(fp);
            return -1;
        }
    }

    if (getuid() != 0) {
        user = get_user_name();
        if (user == NULL)
            goto err_endmntent;

        uidlen = sprintf(uidstr, "%u", getuid());
    }

    found = 0;
    while ((entp = getmntent(fp)) != NULL) {
        int removed = 0;
        if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
            (strcmp(entp->mnt_type, "fuse") == 0 ||
             strcmp(entp->mnt_type, "fuseblk") == 0)) {
            if (user == NULL)
                removed = 1;
            else {
                char *p = strstr(entp->mnt_opts, "user="******"user_id=")) &&
                         (p == entp->mnt_opts || *(p-1) == ',') &&
                         strncmp(p + 8, uidstr, uidlen) == 0 &&
                         (*(p+8+uidlen) == ',' || *(p+8+uidlen) == '\0'))
                    removed = 1;
            }
        }
        if (removed)
            found = 1;
        else if (!issymlink) {
            res = addmntent(newfp, entp);
            if (res != 0) {
                fprintf(stderr, "%s: failed to add entry to %s: %s\n",
                        progname, mtab_new, strerror(errno));
            }
        }
    }

    endmntent(fp);
    if (!issymlink)
        endmntent(newfp);

    if (!found) {
        if (!quiet)
            fprintf(stderr, "%s: entry for %s not found in %s\n", progname,
                    mnt, mtab);
        goto err;
    }

    drop_privs();
    res = do_unmount(mnt, quiet, lazy);
    restore_privs();
    if (res == -1)
        goto err;

    if (!issymlink) {
        res = unmount_rename(mtab, mtab_new);
        if (res == -1)
            goto err;
    }
    return 0;

 err_endmntent:
    if (!issymlink)
        endmntent(newfp);
    endmntent(fp);
 err:
    if (!issymlink)
        unlink(mtab_new);
    return -1;
}
Ejemplo n.º 9
0
int main(int argc, char *argv[])
{
    int ch;
    int fd;
    int res;
    char *origmnt;
    char *mnt;
    static int unmount = 0;
    static int lazy = 0;
    static int quiet = 0;
    char *commfd;
    int cfd;
    const char *opts = "";

    static const struct option long_opts[] = {
        {"unmount", no_argument, NULL, 'u'},
        {"lazy",    no_argument, NULL, 'z'},
        {"quiet",   no_argument, NULL, 'q'},
        {"help",    no_argument, NULL, 'h'},
        {"version", no_argument, NULL, 'V'},
        {0, 0, 0, 0}};

    progname = strdup(argv[0]);
    if (progname == NULL) {
        fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
        exit(1);
    }

    while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts, NULL)) != -1) {
        switch (ch) {
        case 'h':
            usage();
            break;

        case 'V':
            show_version();
            break;

        case 'o':
            opts = optarg;
            break;

        case 'u':
            unmount = 1;
            break;

        case 'z':
            lazy = 1;
            break;

        case 'q':
            quiet = 1;
            break;

        default:
            exit(1);
        }
    }

    if (lazy && !unmount) {
        fprintf(stderr, "%s: -z can only be used with -u\n", progname);
        exit(1);
    }

    if (optind >= argc) {
        fprintf(stderr, "%s: missing mountpoint argument\n", progname);
        exit(1);
    }

    origmnt = argv[optind];

    drop_privs();
    mnt = resolve_path(origmnt);
    restore_privs();
    if (mnt == NULL)
        exit(1);

    umask(033);
    if (unmount) {
        if (geteuid() == 0) {
            int mtablock = lock_mtab();
            res = unmount_fuse(mnt, quiet, lazy);
            unlock_mtab(mtablock);
        } else
            res = do_unmount(mnt, quiet, lazy);
        if (res == -1)
            exit(1);
        return 0;
    }

    commfd = getenv(FUSE_COMMFD_ENV);
    if (commfd == NULL) {
        fprintf(stderr, "%s: old style mounting not supported\n", progname);
        exit(1);
    }

    fd = mount_fuse(mnt, opts);
    if (fd == -1)
        exit(1);

    cfd = atoi(commfd);
    res = send_fd(cfd, fd);
    if (res == -1)
        exit(1);

    return 0;
}
Ejemplo n.º 10
0
int main(int argc, char *argv[])
{
	sigset_t sigset;
	int ch;
	int fd;
	int res;
	char *origmnt;
	char *mnt;
	static int unmount = 0;
	static int lazy = 0;
	static int quiet = 0;
	char *commfd;
	int cfd;
	const char *opts = "";

	static const struct option long_opts[] = {
		{"unmount", no_argument, NULL, 'u'},
		{"lazy",    no_argument, NULL, 'z'},
		{"quiet",   no_argument, NULL, 'q'},
		{"help",    no_argument, NULL, 'h'},
		{"version", no_argument, NULL, 'V'},
		{0, 0, 0, 0}};

	progname = strdup(argv[0]);
	if (progname == NULL) {
		fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
		exit(1);
	}

	while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts,
				 NULL)) != -1) {
		switch (ch) {
		case 'h':
			usage();
			break;

		case 'V':
			show_version();
			break;

		case 'o':
			opts = optarg;
			break;

		case 'u':
			unmount = 1;
			break;

		case 'z':
			lazy = 1;
			break;

		case 'q':
			quiet = 1;
			break;

		default:
			exit(1);
		}
	}

	if (lazy && !unmount) {
		fprintf(stderr, "%s: -z can only be used with -u\n", progname);
		exit(1);
	}

	if (optind >= argc) {
		fprintf(stderr, "%s: missing mountpoint argument\n", progname);
		exit(1);
	} else if (argc > optind + 1) {
		fprintf(stderr, "%s: extra arguments after the mountpoint\n",
			progname);
		exit(1);
	}

	origmnt = argv[optind];

	drop_privs();
	mnt = fuse_mnt_resolve_path(progname, origmnt);
	if (mnt != NULL) {
		res = chdir("/");
		if (res == -1) {
			fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
			exit(1);
		}
	}
	restore_privs();
	if (mnt == NULL)
		exit(1);

	umask(033);
	if (unmount)
		goto do_unmount;

	commfd = getenv(FUSE_COMMFD_ENV);
	if (commfd == NULL) {
		fprintf(stderr, "%s: old style mounting not supported\n",
			progname);
		exit(1);
	}

	fd = mount_fuse(mnt, opts);
	if (fd == -1)
		exit(1);

	cfd = atoi(commfd);
	res = send_fd(cfd, fd);
	if (res == -1)
		exit(1);
	close(fd);

	if (!auto_unmount)
		return 0;

	/* Become a daemon and wait for the parent to exit or die.
	   ie For the control socket to get closed. 
	   btw We don't want to use daemon() function here because
	   it forks and messes with the file descriptors. */
	setsid();
	res = chdir("/");
	if (res == -1) {
		fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
		exit(1);
	}

	sigfillset(&sigset);
	sigprocmask(SIG_BLOCK, &sigset, NULL);

	lazy  = 1;
	quiet = 1;

	while (1) {
		unsigned char buf[16];
		int n = recv(cfd, buf, sizeof(buf), 0);
		if (!n)
			break;

		if (n < 0) {
			if (errno == EINTR)
				continue;
			break;
		}
	}

do_unmount:
	if (geteuid() == 0)
		res = unmount_fuse(mnt, quiet, lazy);
	else {
		res = umount2(mnt, lazy ? UMOUNT_DETACH : 0);
		if (res == -1 && !quiet)
			fprintf(stderr,
				"%s: failed to unmount %s: %s\n",
				progname, mnt, strerror(errno));
	}
	if (res == -1)
		exit(1);
	return 0;
}
Ejemplo n.º 11
0
int main(int argc, char *argv[])
{
    int a;
    int fd;
    int res;
    char *origmnt;
    char *mnt;
    int unmount = 0;
    char **userprog;
    int numargs;
    char mypath[PATH_MAX];
    char *unmount_cmd;
    char *commfd;
    char verstr[128];
    int flags = 0;

    progname = argv[0];

    for(a = 1; a < argc; a++) {
        if(argv[a][0] != '-')
            break;

        switch(argv[a][1]) {
        case 'c':
            flags |= FUSE_KERNEL_CACHE;
            break;

        case 'h':
            usage();
            break;

        case 'u':
            unmount = 1;
            break;

        case 'p':
            flags |= FUSE_DEFAULT_PERMISSIONS;
            break;

        case 'x':
            if(getuid() != 0) {
                fprintf(stderr, "%s: option %s is allowed only for root\n",
                        progname, argv[a]);
                exit(1);
            }
            flags |= FUSE_ALLOW_OTHER;
            break;

        default:
            fprintf(stderr, "%s: Unknown option %s\n", progname, argv[a]);
            exit(1);
        }
    }

    if(a == argc) {
        fprintf(stderr, "%s: Missing mountpoint argument\n", progname);
        exit(1);
    }

    origmnt = argv[a++];

    if(getpid() != 0)
        drop_privs();

    mnt = resolve_path(origmnt, unmount);
    if(mnt == NULL)
        exit(1);

    if(getpid() != 0)
        restore_privs();

    if(unmount) {
        res = remove_mount(mnt);
        if(res == -1)
            exit(1);

        return 0;
    }

    commfd = getenv(FUSE_COMMFD_ENV);

    if(a == argc && commfd == NULL) {
        fprintf(stderr, "%s: Missing program argument\n", progname);
        exit(1);
    }

    userprog = argv + a;
    numargs = argc - a;

    fd = mount_fuse(mnt, flags);
    if(fd == -1)
        exit(1);

    if(commfd != NULL) {
        int cfd = atoi(commfd);
        res = send_fd(cfd, fd);
        if(res == -1)
            exit(1);
        exit(0);
    }

    /* Dup the file descriptor to stdin */
    if(fd != 0) {
        dup2(fd, 0);
        close(fd);
    }

    /* Strangely this doesn't work after dropping permissions... */
    res = readlink("/proc/self/exe", mypath, sizeof(mypath) - 1);
    if(res == -1) {
        fprintf(stderr, "%s: failed to determine self path: %s\n",
                progname, strerror(errno));
        strcpy(mypath, "fusermount");
        fprintf(stderr, "using %s as the default\n", mypath);
    }
    else
        mypath[res] = '\0';

    /* Drop setuid/setgid permissions */
    setuid(getuid());
    setgid(getgid());

    unmount_cmd = (char *) malloc(strlen(mypath) + strlen(mnt) + 64);
    sprintf(unmount_cmd, "%s -u %s", mypath, mnt);
    setenv(FUSE_UMOUNT_CMD_ENV, unmount_cmd, 1);
    sprintf(verstr, "%i", FUSE_KERNEL_VERSION);
    setenv(FUSE_KERNEL_VERSION_ENV, verstr, 1);
    setenv(FUSE_MOUNTED_ENV, "", 1);

    execvp(userprog[0], userprog);
    fprintf(stderr, "%s: failed to exec %s: %s\n", progname, userprog[0],
            strerror(errno));

    close(0);
    system(unmount_cmd);
    return 1;
}