#include <stddef.h>
#include <sys/mount.h>
#include <sys/stat.h>
+#include <sys/sysmacros.h>
#include <sys/types.h>
// libmount
#include <libmount/libmount.h>
+#include <pakfire/arch.h>
#include <pakfire/logging.h>
#include <pakfire/pakfire.h>
+#include <pakfire/path.h>
#include <pakfire/mount.h>
+#include <pakfire/string.h>
#include <pakfire/util.h>
static const struct pakfire_mountpoint {
const char* fstype;
int flags;
const char* options;
- enum pakfire_mount_flags mount_flags;
} mountpoints[] = {
- // The root filesystem is a tmpfs
- { "pakfire_root", "", "tmpfs", 0, NULL, 0 },
+ // Mount a new instance of /proc
+ { "pakfire_proc", "proc", "proc",
+ MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL, },
+
+ // Make /proc/sys read-only (except /proc/sys/net)
+ { "/proc/sys", "proc/sys", "bind", MS_BIND|MS_REC, NULL, },
+ { "/proc/sys/net", "proc/sys/net", "bind", MS_BIND|MS_REC, NULL, },
+ { "/proc/sys", "proc/sys", "bind",
+ MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
+
+ // Deny write access to /proc/sysrq-trigger (can be used to restart the host)
+ { "/proc/sysrq-trigger", "proc/sysrq-trigger", "bind", MS_BIND|MS_REC, NULL, },
+ { "/proc/sysrq-trigger", "proc/sysrq-trigger", "bind",
+ MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
+
+ // Make /proc/irq read-only
+ { "/proc/irq", "proc/irq", "bind", MS_BIND|MS_REC, NULL, },
+ { "/proc/irq", "proc/irq", "bind",
+ MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
+
+ // Make /proc/bus read-only
+ { "/proc/bus", "proc/bus", "bind", MS_BIND|MS_REC, NULL, },
+ { "/proc/bus", "proc/bus", "bind",
+ MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
+
+ // Bind-Mount /sys ready-only
+ { "/sys", "sys", "bind", MS_BIND|MS_REC, NULL, },
+ { "/sys", "sys", "bind",
+ MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
- { "pakfire_proc", "proc", "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL,
- MOUNT_IN_NEW_NS },
-
- // Bind mount /proc/sys as read-only with the following exceptions:
- // * /proc/sys/net
- { "/proc/sys", "proc/sys", "bind", MS_BIND, NULL,
- MOUNT_IN_NEW_NS },
- { "/proc/sys/net", "proc/sys/net", "bind", MS_BIND, NULL,
- MOUNT_IN_NEW_NS },
- { "/proc/sys", "proc/sys", "bind", MS_BIND|MS_RDONLY|MS_REMOUNT, NULL,
- MOUNT_IN_NEW_NS },
+ // Create a new /dev
+ { "pakfire_dev", "dev", "tmpfs", MS_NOSUID|MS_NOEXEC,
+ "mode=0755,size=4m,nr_inodes=64k", },
+ { "pakfire_dev_pts", "dev/pts", "devpts", MS_NOSUID|MS_NOEXEC,
+ "newinstance,ptmxmode=0666,mode=620", },
- // Bind mount /sys as read-only
- { "/sys", "sys", "bind", MS_BIND, NULL, 0 },
- { "/sys", "sys", "bind", MS_BIND|MS_RDONLY|MS_REMOUNT, NULL, 0 },
+ // Create a new /dev/shm
+ { "pakfire_dev_shm", "dev/shm", "tmpfs",
+ MS_NOSUID|MS_NODEV|MS_STRICTATIME, "mode=1777,size=1024m", },
- // Create a new /dev
- { "pakfire_dev", "dev", "tmpfs", MS_NOSUID|MS_NOEXEC,
- "mode=755,size=4m,nr_inodes=64k", 0 },
- { "/dev/pts", "dev/pts", "bind", MS_BIND, NULL, 0 },
+ // Mount /dev/mqueue
+ { "mqueue", "dev/mqueue", "mqueue",
+ MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL },
// Create a new /run
- { "pakfire_tmpfs", "run", "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV,
- "mode=755,size=4m,nr_inodes=1k", 0 },
+ { "pakfire_run", "run", "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ "mode=755,size=256m,nr_inodes=1k", },
// Create a new /tmp
- { "pakfire_tmpfs", "tmp", "tmpfs", MS_NOSUID|MS_NODEV,
- "mode=755", 0 },
+ { "pakfire_tmp", "tmp", "tmpfs",
+ MS_NOSUID|MS_NODEV|MS_STRICTATIME, "mode=1777,size=4096m", },
// The end
{ NULL },
};
+static const struct pakfire_devnode {
+ const char* path;
+ int major;
+ int minor;
+ mode_t mode;
+ int flags;
+} devnodes[] = {
+ { "/dev/null", 1, 3, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, 0 },
+ { "/dev/zero", 1, 5, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, 0 },
+ { "/dev/full", 1, 7, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, 0 },
+ { "/dev/random", 1, 8, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, 0 },
+ { "/dev/urandom", 1, 9, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, 0 },
+ { "/dev/kmsg", 1, 11, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, 0 },
+ { "/dev/tty", 5, 0, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, 0 },
+ { "/dev/console", 5, 1, S_IFCHR|S_IRUSR|S_IWUSR, 0 },
+ { "/dev/rtc0", 252, 0, S_IFCHR|S_IRUSR|S_IWUSR, 0 },
+
+ // Loop Devices
+ { "/dev/loop-control", 10, 237, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
+ { "/dev/loop0", 7, 0, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
+ { "/dev/loop1", 7, 1, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
+ { "/dev/loop2", 7, 2, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
+ { "/dev/loop3", 7, 3, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
+ { "/dev/loop4", 7, 4, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
+ { "/dev/loop5", 7, 5, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
+ { "/dev/loop6", 7, 6, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
+ { "/dev/loop7", 7, 7, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
+
+ { NULL },
+};
+
+static const struct pakfire_symlink {
+ const char* target;
+ const char* path;
+} symlinks[] = {
+ { "/dev/pts/ptmx", "/dev/ptmx", },
+ { "/proc/self/fd", "/dev/fd", },
+ { "/proc/self/fd/0", "/dev/stdin" },
+ { "/proc/self/fd/1", "/dev/stdout" },
+ { "/proc/self/fd/2", "/dev/stderr" },
+ { "/proc/kcore", "/dev/core" },
+ { NULL },
+};
+
+int pakfire_mount_change_propagation(struct pakfire_ctx* ctx, const char* path, int propagation) {
+ CTX_DEBUG(ctx, "Changing mount propagation on %s\n", path);
+
+ int r = mount(NULL, path, NULL, propagation|MS_REC, NULL);
+ if (r)
+ CTX_ERROR(ctx, "Failed to change mount propagation on %s: %m\n", path);
+
+ return r;
+}
+
+static int pakfire_mount_is_mountpoint(struct pakfire* pakfire, const char* path) {
+ // XXX THIS STILL NEEDS TO BE IMPLEMENTED
+ return 1;
+}
+
+int pakfire_mount_make_mounpoint(struct pakfire* pakfire, const char* path) {
+ int r;
+
+ // Check if path already is a mountpoint
+ r = pakfire_mount_is_mountpoint(pakfire, path);
+ switch (r) {
+ // Already is a mountpoint
+ case 0:
+ return 0;
+
+ // Is not a mountpoint
+ case 1:
+ break;
+
+ default:
+ ERROR(pakfire, "Could not determine whether %s is a mountpoint: %m\n", path);
+ return r;
+ }
+
+ // Bind-mount to self
+ r = mount(path, path, NULL, MS_BIND|MS_REC, NULL);
+ if (r) {
+ ERROR(pakfire, "Could not make %s a mountpoint: %m\n", path);
+ return r;
+ }
+
+ return 0;
+}
+
/*
Easy way to iterate through all mountpoints
*/
static int pakfire_mount_foreach(struct pakfire* pakfire, int direction,
int (*callback)(struct pakfire* pakfire, struct libmnt_fs* fs, const void* data),
- const char* option, const void* data) {
+ const void* data) {
const char* root = pakfire_get_path(pakfire);
- int r = 1;
+ int r = 0;
struct libmnt_iter* iterator = NULL;
struct libmnt_table* tab = NULL;
if (!pakfire_string_startswith(target, root))
continue;
- // Filter by option
- if (option) {
- if (!mnt_fs_match_options(fs, option))
- continue;
- }
-
// Call the callback for each relevant mountpoint
r = callback(pakfire, fs, data);
if (r)
int pakfire_is_mountpoint(struct pakfire* pakfire, const char* path) {
return pakfire_mount_foreach(pakfire, MNT_ITER_FORWARD,
- __pakfire_is_mountpoint, NULL, path);
+ __pakfire_is_mountpoint, path);
}
-int pakfire_mount(struct pakfire* pakfire, const char* source, const char* target,
+static int pakfire_mount(struct pakfire* pakfire, const char* source, const char* target,
const char* fstype, unsigned long mflags, const void* data) {
const char* options = (const char*)data;
- int r;
-
- char error[1024];
// Check for some basic inputs
if (!source || !target) {
return 1;
}
- DEBUG(pakfire, "Mounting %s from %s (%s - %s)\n",
- target, source, fstype, options);
-
-#ifdef PAKFIRE_DEBUG
- // Enable debugging for libmount
- mnt_init_debug(0xffff);
-#endif
+ DEBUG(pakfire, "Mounting %s from %s (%s - %s)\n", target, source, fstype, options);
- // Allocate a new mount context
- struct libmnt_context* ctx = mnt_new_context();
- if (!ctx)
- return 1;
-
- // Disable any mount helpers
- r = mnt_context_disable_helpers(ctx, 1);
+ // Perform mount()
+ int r = mount(source, target, fstype, mflags, data);
if (r) {
- ERROR(pakfire, "Could not disable mount helpers: %m\n");
- goto ERROR;
+ ERROR(pakfire, "Could not mount %s: %m\n", target);
}
- // Set source
- r = mnt_context_set_source(ctx, source);
- if (r) {
- ERROR(pakfire, "Could not set source: %m\n");
- goto ERROR;
- }
+ return r;
+}
- // Set target
- r = mnt_context_set_target(ctx, target);
- if (r) {
- ERROR(pakfire, "Could not set target: %m\n");
- goto ERROR;
- }
+static int __pakfire_mount_print(struct pakfire* pakfire,
+ struct libmnt_fs* fs, const void* data) {
+ DEBUG(pakfire,
+ " %s %s %s %s\n",
+ mnt_fs_get_source(fs),
+ mnt_fs_get_target(fs),
+ mnt_fs_get_fstype(fs),
+ mnt_fs_get_fs_options(fs)
+ );
- // Set filesystem type
- if (fstype) {
- r = mnt_context_set_fstype(ctx, fstype);
- if (r) {
- ERROR(pakfire, "Could not set filesystem type: %m\n");
- goto ERROR;
- }
- }
+ return 0;
+}
- // Set mount flags
- if (mflags) {
- r = mnt_context_set_mflags(ctx, mflags);
- if (r) {
- ERROR(pakfire, "Could not set mount flags: %m\n");
- goto ERROR;
- }
- }
+int pakfire_mount_list(struct pakfire* pakfire) {
+ DEBUG(pakfire, "Mountpoints:\n");
- // Set options
- if (options) {
- r = mnt_context_set_options(ctx, options);
- if (r) {
- ERROR(pakfire, "Could not set mount options: %m\n");
- goto ERROR;
- }
- }
+ return pakfire_mount_foreach(pakfire, MNT_ITER_FORWARD,
+ __pakfire_mount_print, NULL);
+}
- // Perform mount operation
- r = mnt_context_mount(ctx);
- if (r) {
- // Fetch the error message
- mnt_context_get_excode(ctx, r, error, sizeof(error));
+static int pakfire_populate_dev(struct pakfire* pakfire, int flags) {
+ char path[PATH_MAX];
- ERROR(pakfire, "Mount unsuccessful: %s\n", error);
- goto ERROR;
- }
+ // Create device nodes
+ for (const struct pakfire_devnode* devnode = devnodes; devnode->path; devnode++) {
+ DEBUG(pakfire, "Creating device node %s\n", devnode->path);
-ERROR:
- if (ctx)
- mnt_free_context(ctx);
+ // Check if flags match
+ if (devnode->flags && !(flags & devnode->flags))
+ continue;
- return r;
-}
+ int r = pakfire_path(pakfire, path, "%s", devnode->path);
+ if (r)
+ return r;
-static int pakfire_umount(struct pakfire* pakfire, const char* path, int flags) {
- int r;
+ dev_t dev = makedev(devnode->major, devnode->minor);
- DEBUG(pakfire, "Umounting %s\n", path);
+ r = mknod(path, devnode->mode, dev);
-RETRY:
- // Perform umount
- r = umount2(path, flags);
- if (r) {
- // Attempt a lazy umount when busy
- if (errno == EBUSY) {
- flags |= MNT_DETACH;
- goto RETRY;
+ // Continue if mknod was successful
+ if (r == 0)
+ continue;
+
+ // If we could not create the device node because of permission issues,
+ // it might be likely that we are running in a user namespace where creating
+ // device nodes is not permitted. Try bind-mounting them.
+ if (errno == EPERM)
+ goto MOUNT;
+
+ // Otherwise log an error and end
+ ERROR(pakfire, "Could not create %s: %m\n", devnode->path);
+ return r;
+
+MOUNT:
+ // Create an empty file
+ r = pakfire_touch(path, 0444);
+ if (r) {
+ ERROR(pakfire, "Could not create %s: %m\n", path);
+ return r;
}
- ERROR(pakfire, "Could not umount %s: %m\n", path);
+ // Create a bind-mount over the file
+ r = pakfire_mount(pakfire, devnode->path, path, "bind", MS_BIND, NULL);
+ if (r)
+ return r;
}
- return r;
-}
+ // Create symlinks
+ for (const struct pakfire_symlink* s = symlinks; s->target; s++) {
+ DEBUG(pakfire, "Creating symlink %s -> %s\n", s->path, s->target);
-static int __pakfire_mount_print(struct pakfire* pakfire,
- struct libmnt_fs* fs, const void* data) {
- DEBUG(pakfire,
- " %s %s %s %s\n",
- mnt_fs_get_source(fs),
- mnt_fs_get_target(fs),
- mnt_fs_get_fstype(fs),
- mnt_fs_get_fs_options(fs)
- );
+ int r = pakfire_path(pakfire, path, "%s", s->path);
+ if (r)
+ return r;
+
+ r = symlink(s->target, path);
+ if (r) {
+ ERROR(pakfire, "Could not create symlink %s: %m\n", s->path);
+ return r;
+ }
+ }
return 0;
}
-static int pakfire_mount_list(struct pakfire* pakfire) {
- DEBUG(pakfire, "Current mountpoints:\n");
+static int pakfire_mount_interpreter(struct pakfire* pakfire) {
+ char target[PATH_MAX];
- return pakfire_mount_foreach(pakfire, MNT_ITER_FORWARD,
- __pakfire_mount_print, NULL, NULL);
+ // Fetch the target architecture
+ const char* arch = pakfire_get_effective_arch(pakfire);
+
+ // Can we emulate this architecture?
+ char* interpreter = pakfire_arch_find_interpreter(arch);
+
+ // No interpreter required
+ if (!interpreter)
+ return 0;
+
+ DEBUG(pakfire, "Mounting interpreter %s for %s\n", interpreter, arch);
+
+ // Where to mount this?
+ int r = pakfire_path(pakfire, target, "%s", interpreter);
+ if (r)
+ return r;
+
+ // Create directory
+ r = pakfire_mkparentdir(target, 0755);
+ if (r)
+ return r;
+
+ // Create an empty file
+ FILE* f = fopen(target, "w");
+ if (!f)
+ return 1;
+ fclose(f);
+
+ r = pakfire_mount(pakfire, interpreter, target, NULL, MS_BIND|MS_RDONLY, NULL);
+ if (r)
+ ERROR(pakfire, "Could not mount interpreter %s to %s: %m\n", interpreter, target);
+
+ return r;
}
-int pakfire_mount_all(struct pakfire* pakfire, const enum pakfire_mount_flags mount_flags) {
+int pakfire_mount_all(struct pakfire* pakfire, int flags) {
char target[PATH_MAX];
- char options[PATH_MAX];
int r;
// Fetch Pakfire's root directory
const char* root = pakfire_get_path(pakfire);
- // Are we being called from a new namespace?
- const unsigned int in_new_ns = (mount_flags & MOUNT_IN_NEW_NS);
-
for (const struct pakfire_mountpoint* mp = mountpoints; mp->source; mp++) {
- if (!in_new_ns && (mp->mount_flags & MOUNT_IN_NEW_NS))
- continue;
-
- if (in_new_ns && !(mp->mount_flags & MOUNT_IN_NEW_NS))
- continue;
-
// Figure out where to mount
- r = pakfire_path_join(target, root, mp->target);
- if (r < 0)
+ r = pakfire_path_append(target, root, mp->target);
+ if (r)
return r;
- // Append namespace mount option
- if (in_new_ns) {
- if (mp->options)
- pakfire_string_format(options, "%s,%s",
- mp->options, PAKFIRE_MOUNT_OPT_NAMESPACED);
- else
- pakfire_string_set(options, PAKFIRE_MOUNT_OPT_NAMESPACED);
- } else {
- if (mp->options)
- pakfire_string_set(options, mp->options);
- else
- pakfire_string_set(options, "");
+ // Create target if it doesn't exist
+ if (!pakfire_path_exists(target)) {
+ r = pakfire_mkdir(target, 0755);
+ if (r) {
+ ERROR(pakfire, "Could not create %s: %m\n", target);
+ return r;
+ }
}
- // Create target
- pakfire_mkdir(target, 0);
-
-RETRY:
// Perform mount()
- r = pakfire_mount(pakfire, mp->source, target, mp->fstype, mp->flags, options);
- if (r) {
- // If the target directory does not exist, we will create it
- if (errno == ENOENT) {
- r = pakfire_mkdir(target, S_IRUSR|S_IWUSR|S_IXUSR);
- if (r) {
- ERROR(pakfire, "Could not create %s\n", target);
- return r;
- }
-
- goto RETRY;
- }
-
- ERROR(pakfire, "Could not mount /%s: %m\n", mp->target);
+ r = pakfire_mount(pakfire, mp->source, target, mp->fstype, mp->flags, mp->options);
+ if (r)
return r;
- }
}
- // List everything that is mounted
- pakfire_mount_list(pakfire);
+ // Populate /dev
+ r = pakfire_populate_dev(pakfire, flags);
+ if (r)
+ return r;
+
+ // Mount the interpreter (if needed)
+ r = pakfire_mount_interpreter(pakfire);
+ if (r)
+ return r;
return 0;
}
-static int __pakfire_umount(struct pakfire* pakfire,
- struct libmnt_fs* fs, const void* data) {
- const char* target = mnt_fs_get_target(fs);
- if (!target)
- return 1;
+int pakfire_make_ramdisk(struct pakfire* pakfire, char* path, const char* args) {
+ int r;
+
+ // Create a new temporary directory
+ char* p = pakfire_mkdtemp(path);
+ if (!p)
+ return -errno;
+
+ // Mount the ramdisk
+ r = pakfire_mount(pakfire, "pakfire_ramdisk", p, "tmpfs", 0, args);
+ if (r) {
+ ERROR_ERRNO(pakfire, r, "Could not mount ramdisk at %s (%s): %m\n", p, args);
+ return r;
+ }
+
+ DEBUG(pakfire, "Ramdisk mounted at %s (%s)\n", p, args);
- return pakfire_umount(pakfire, target, 0);
+ return 0;
}
-/*
- umounts everything that hasn't been umounted, yet
-*/
-int pakfire_umount_all(struct pakfire* pakfire, const char* option) {
- // List everything that is mounted
- pakfire_mount_list(pakfire);
+int pakfire_bind(struct pakfire* pakfire, const char* src, const char* dst, int flags) {
+ struct stat st;
+ char mountpoint[PATH_MAX];
+
+ if (!dst)
+ dst = src;
+
+ int r = pakfire_path(pakfire, mountpoint, "%s", dst);
+ if (r)
+ return r;
+
+ DEBUG(pakfire, "Bind-mounting %s to %s\n", src, mountpoint);
+
+ r = stat(src, &st);
+ if (r < 0) {
+ ERROR(pakfire, "Could not stat %s: %m\n", src);
+ return 1;
+ }
+
+ // Make sure the mountpoint exists
+ switch (st.st_mode & S_IFMT) {
+ case S_IFDIR:
+ r = pakfire_mkdir(mountpoint, st.st_mode);
+ if (r && errno != EEXIST)
+ return r;
+ break;
+
+ case S_IFREG:
+ case S_IFLNK:
+ // Make parent directory
+ r = pakfire_mkparentdir(mountpoint, 0755);
+ if (r)
+ return r;
+
+ // Create a file
+ FILE* f = fopen(mountpoint, "w");
+ if (!f)
+ return 1;
+ fclose(f);
+ break;
+
+ default:
+ errno = ENOTSUP;
+ return 1;
+ }
+
+ // The Linux kernel seems to be quite funny when trying to bind-mount something
+ // as read-only and requires us to mount the source first, and then remount it
+ // again using MS_RDONLY.
+ if (flags & MS_RDONLY) {
+ r = pakfire_mount(pakfire, src, mountpoint, "bind", MS_BIND|MS_REC, NULL);
+ if (r)
+ return r;
+
+ // Add the remount flag
+ flags |= MS_REMOUNT;
+ }
- return pakfire_mount_foreach(pakfire, MNT_ITER_BACKWARD,
- __pakfire_umount, option, NULL);
+ // Perform mount
+ return pakfire_mount(pakfire, src, mountpoint, "bind", flags|MS_BIND|MS_REC, NULL);
}