/* * PoC for using new mount API for mount.cifs */ #include #include #include #include #include /* * For MS_*, MNT_* constants */ #include /* * For FSCONFIG_*, FSMOUNT_*, MOVE_* constants * * TODO: need to add configure check for header availability */ #include /* * For AT_* constants */ #include /* * The new mount API syscalls have currently no wrappers in * glibc. Call them via syscall(). * * Wrap in #ifdef to be future-proof once they eventually get wrappers * * TODO: add configure checks for fsopen symbol -> HAVE_FSOPEN */ #ifndef HAVE_FSOPEN #include #ifndef SYS_fsopen enum {SYS_move_mount=429, SYS_fsopen, SYS_fsconfig, SYS_fsmount}; #endif int fsopen(const char *fsname, unsigned int flags) { return syscall(SYS_fsopen, fsname, flags); } int fsmount(int fd, unsigned int flags, unsigned int mount_attrs) { return syscall(SYS_fsmount, fd, flags, mount_attrs); } int fsconfig(int fd, unsigned int cmd, const char *key, const void *value, int aux) { return syscall(SYS_fsconfig, fd, cmd, key, value, aux); } int move_mount(int from_dirfd, const char *from_pathname, int to_dirfd, const char *to_pathname, unsigned int flags) { return syscall(SYS_move_mount, from_dirfd, from_pathname, to_dirfd, to_pathname, flags); } #endif /* * To get the kernel log emitted after a call, simply read the context fd * * "e " * An error message string was logged. * * "i " * An informational message string was logged. * * "w " * An warning message string was logged. * * Messages are removed from the queue as they're read. * */ void print_context_log(int fd) { char buf[512]; ssize_t rc; int first = 1; while (1) { memset(buf, 0, sizeof(buf)); rc = read(fd, buf, sizeof(buf)-2); if (rc == 0 || (rc < 0 && errno == ENODATA)) { errno = 0; break; } else if (rc < 0) { perror("read"); break; } if (first) { printf("kernel mount errors:\n"); first = 0; } printf("%s", buf); } } #define ASSERT(x) \ do { \ if (!(x)) { \ printf("ASSERT FAIL %s\n", #x); \ exit(1); \ } \ } while (0) #define CHECK(x) \ do { \ if ((x) < 0) { \ perror(#x); \ exit(1); \ } \ } while (0) #define CHECK_AND_LOG(fd, x) \ do { \ if ((x) < 0) { \ perror(#x); \ print_context_log(fd); \ exit(1); \ } \ } while (0) char *g_mount_params; /* read whole file in g_mount_params buffer */ void read_mount_params(void) { int fd; size_t off; ssize_t rc; size_t size; off = 0; size = 2048; g_mount_params = malloc(size); if (!g_mount_params) goto out; g_mount_params[off++] = '\n'; fd = open("/proc/fs/cifs/mount_params", O_RDONLY); if (fd < 0) goto out; do { if (size - off <= 1) { void *p = realloc(g_mount_params, size*2); if (!p) goto err; size *= 2; g_mount_params = p; } rc = read(fd, g_mount_params+off, size-off-1); if (rc < 0) goto err; off += rc; g_mount_params[off] = 0; } while (rc > 0); out: if (fd >= 0) close(fd); printf("%s", g_mount_params); return; err: free(g_mount_params); g_mount_params = NULL; goto out; } /* Dumb string search in the buffer */ enum {PARAM_FLAG, PARAM_STRING, PARAM_U32, PARAM_U64}; int param_type(const char *name) { char needle[128] = {0}; snprintf(needle, sizeof(needle)-1, "\n%s:", name); char *p = strstr(g_mount_params, needle); if (!p) return -1; p = strchr(p, ':'); if (!p) return -1; if (!*p || !*(p+1) || !*(p+2)) return -1; char *beg = p+1; char *end = strchr(beg, '\n'); if (strncmp(beg, "flag", end-beg) == 0) return PARAM_FLAG; if (strncmp(beg, "noflag", end-beg) == 0) return PARAM_FLAG; if (strncmp(beg, "string", end-beg) == 0) return PARAM_STRING; if (strncmp(beg, "u32", end-beg) == 0) return PARAM_U32; if (strncmp(beg, "u64", end-beg) == 0) return PARAM_U64; return -1; } int main(void) { int sfd; int mfd; /* * Test param type detection */ read_mount_params(); ASSERT(param_type("non-existing") < 0); ASSERT(param_type("ip") == PARAM_STRING); ASSERT(param_type("max_channels") == PARAM_U32); ASSERT(param_type("mfsymlinks") == PARAM_FLAG); ASSERT(param_type("nodfs") == PARAM_FLAG); ASSERT(param_type("user_xattr") == PARAM_FLAG); ASSERT(param_type("prefixpath") == PARAM_STRING); /* * Converting following old-style mount syscall to new mount api * * mount( * "//192.168.2.110/data", * "/mnt/test", * "cifs", * 0, * "ip=192.168.2.110,unc=\\\\192.168.2.110\\data,vers=3.0,user=administrator,pass=my-pass" * ) * */ CHECK(sfd = fsopen("cifs", 0)); CHECK_AND_LOG(sfd, fsconfig(sfd, FSCONFIG_SET_STRING, "source", "//192.168.2.110/data", 0)); CHECK_AND_LOG(sfd, fsconfig(sfd, FSCONFIG_SET_STRING, "ip", "192.168.2.110", 0)); CHECK_AND_LOG(sfd, fsconfig(sfd, FSCONFIG_SET_STRING, "unc", "\\\\192.168.2.110\\data", 0)); CHECK_AND_LOG(sfd, fsconfig(sfd, FSCONFIG_SET_STRING, "vers", "3.0", 0)); CHECK_AND_LOG(sfd, fsconfig(sfd, FSCONFIG_SET_STRING, "user", "administrator", 0)); CHECK_AND_LOG(sfd, fsconfig(sfd, FSCONFIG_SET_STRING, "pass", "my-pass", 0)); CHECK_AND_LOG(sfd, fsconfig(sfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0)); CHECK_AND_LOG(sfd, (mfd = fsmount(sfd, FSMOUNT_CLOEXEC, 0))); CHECK(move_mount(mfd, "", AT_FDCWD, "/mnt/test", MOVE_MOUNT_F_EMPTY_PATH)); return 0; }