chiark / gitweb /
udevd: create standard symlinks and handle /lib/udev/devices
authorKay Sievers <kay.sievers@vrfy.org>
Thu, 20 May 2010 07:04:26 +0000 (09:04 +0200)
committerKay Sievers <kay.sievers@vrfy.org>
Thu, 20 May 2010 07:04:26 +0000 (09:04 +0200)
libudev/libudev-private.h
libudev/libudev-selinux-private.c
udev/udevd.c

index 3758c5b1b4da9052dfe94fd6bca8838f0e907baa..fa9722360b9df5ebc603ff53f628fc51bd043b63 100644 (file)
@@ -227,12 +227,15 @@ static inline void udev_selinux_init(struct udev *udev) {}
 static inline void udev_selinux_exit(struct udev *udev) {}
 static inline void udev_selinux_lsetfilecon(struct udev *udev, const char *file, unsigned int mode) {}
 static inline void udev_selinux_setfscreatecon(struct udev *udev, const char *file, unsigned int mode) {}
+static inline void udev_selinux_setfscreateconat(struct udev *udev, int dirfd, const char *file, unsigned int mode) {}
 static inline void udev_selinux_resetfscreatecon(struct udev *udev) {}
 #else
 void udev_selinux_init(struct udev *udev);
 void udev_selinux_exit(struct udev *udev);
 void udev_selinux_lsetfilecon(struct udev *udev, const char *file, unsigned int mode);
 void udev_selinux_setfscreatecon(struct udev *udev, const char *file, unsigned int mode);
+void udev_selinux_setfscreateconat(struct udev *udev, int dirfd, const char *file, unsigned int mode);
 void udev_selinux_resetfscreatecon(struct udev *udev);
 #endif
+
 #endif
index 84f8b6a63fc0cbea6a6c171ddfa0ccb095c39f38..2d4463d8648ef26277aa4fcf2815b76dd9a6534e 100644 (file)
@@ -53,7 +53,7 @@ void udev_selinux_lsetfilecon(struct udev *udev, const char *file, unsigned int
        if (matchpathcon(file, mode, &scontext) < 0) {
                err(udev, "matchpathcon(%s) failed\n", file);
                return;
-       } 
+       }
        if (lsetfilecon(file, scontext) < 0)
                err(udev, "setfilecon %s failed: %m\n", file);
        freecon(scontext);
@@ -65,6 +65,7 @@ void udev_selinux_setfscreatecon(struct udev *udev, const char *file, unsigned i
 
        if (!selinux_enabled)
                return;
+
        if (matchpathcon(file, mode, &scontext) < 0) {
                err(udev, "matchpathcon(%s) failed\n", file);
                return;
@@ -81,3 +82,28 @@ void udev_selinux_resetfscreatecon(struct udev *udev)
        if (setfscreatecon(selinux_prev_scontext) < 0)
                err(udev, "setfscreatecon failed: %m\n");
 }
+
+void udev_selinux_setfscreateconat(struct udev *udev, int dirfd, const char *file, unsigned int mode)
+{
+       char filename[UTIL_PATH_SIZE];
+
+       if (!selinux_enabled)
+               return;
+
+       /* resolve relative filename */
+       if (file[0] != '/') {
+               char procfd[UTIL_PATH_SIZE];
+               char target[UTIL_PATH_SIZE];
+               ssize_t len;
+
+               snprintf(procfd, sizeof(procfd), "/proc/%u/fd/%u", getpid(), dirfd);
+               len = readlink(procfd, target, sizeof(target));
+               if (len <= 0 || len == sizeof(target))
+                       return;
+               target[len] = '\0';
+
+               util_strscpyl(filename, sizeof(filename), target, "/", file, NULL);
+               file = filename;
+       }
+       udev_selinux_setfscreatecon(udev, file, mode);
+}
index 77a14df7b07afba30589801bfc068ae8db9283f8..1bde8f4a746d19b719d71289137e308e5721ae2f 100644 (file)
@@ -449,29 +449,6 @@ static void worker_kill(struct udev *udev, int retain)
        }
 }
 
-static int mem_size_mb(void)
-{
-       FILE *f;
-       char buf[4096];
-       long int memsize = -1;
-
-       f = fopen("/proc/meminfo", "r");
-       if (f == NULL)
-               return -1;
-
-       while (fgets(buf, sizeof(buf), f) != NULL) {
-               long int value;
-
-               if (sscanf(buf, "MemTotal: %ld kB", &value) == 1) {
-                       memsize = value / 1024;
-                       break;
-               }
-       }
-
-       fclose(f);
-       return memsize;
-}
-
 /* lookup event for identical, parent, child device */
 static bool is_devpath_busy(struct event *event)
 {
@@ -768,6 +745,130 @@ static void handle_signal(struct udev *udev, int signo)
        }
 }
 
+static int copy_dir(struct udev *udev, DIR *dir_from, DIR *dir_to, int maxdepth)
+{
+       struct dirent *dent;
+
+       for (dent = readdir(dir_from); dent != NULL; dent = readdir(dir_from)) {
+               struct stat stats;
+
+               if (dent->d_name[0] == '.')
+                       continue;
+               if (fstatat(dirfd(dir_from), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
+                       continue;
+
+               if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) {
+                       udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, stats.st_mode & 0777);
+                       if (mknodat(dirfd(dir_to), dent->d_name, stats.st_mode, stats.st_rdev) == 0) {
+                               fchmodat(dirfd(dir_to), dent->d_name, stats.st_mode & 0777, 0);
+                               fchownat(dirfd(dir_to), dent->d_name, stats.st_uid, stats.st_gid, 0);
+                       } else {
+                               utimensat(dirfd(dir_to), dent->d_name, NULL, 0);
+                       }
+                       udev_selinux_resetfscreatecon(udev);
+               } else if (S_ISLNK(stats.st_mode)) {
+                       char target[UTIL_PATH_SIZE];
+                       ssize_t len;
+
+                       len = readlinkat(dirfd(dir_from), dent->d_name, target, sizeof(target));
+                       if (len <= 0 || len == (ssize_t)sizeof(target))
+                               continue;
+                       target[len] = '\0';
+                       udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, S_IFLNK);
+                       if (symlinkat(target, dirfd(dir_to), dent->d_name) < 0 && errno == EEXIST)
+                               utimensat(dirfd(dir_to), dent->d_name, NULL, AT_SYMLINK_NOFOLLOW);
+                       udev_selinux_resetfscreatecon(udev);
+               } else if (S_ISDIR(stats.st_mode)) {
+                       DIR *dir2_from, *dir2_to;
+
+                       if (maxdepth == 0)
+                               continue;
+
+                       udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, S_IFDIR|0755);
+                       mkdirat(dirfd(dir_to), dent->d_name, 0755);
+                       udev_selinux_resetfscreatecon(udev);
+
+                       dir2_to = fdopendir(openat(dirfd(dir_to), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
+                       if (dir2_to == NULL)
+                               continue;
+
+                       dir2_from = fdopendir(openat(dirfd(dir_from), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
+                       if (dir2_from == NULL) {
+                               closedir(dir2_to);
+                               continue;
+                       }
+
+                       copy_dir(udev, dir2_from, dir2_to, maxdepth-1);
+
+                       closedir(dir2_to);
+                       closedir(dir2_from);
+               }
+       }
+
+       return 0;
+}
+
+static void prepare_dev(struct udev *udev)
+{
+       struct stdlinks {
+               const char *link;
+               const char *target;
+       };
+       static const struct stdlinks stdlinks[] = {
+               { "core", "/proc/kcore" },
+               { "fd", "/proc/fd" },
+               { "stdin", "/proc/self/fd/0" },
+               { "stdout", "/proc/self/fd/1" },
+               { "stderr", "/proc/self/fd/2" },
+       };
+       unsigned int i;
+       DIR *dir_from, *dir_to;
+
+       dir_to = opendir(udev_get_dev_path(udev));
+       if (dir_to == NULL)
+               return;
+
+       /* create standard symlinks to /proc */
+       for (i = 0; i < ARRAY_SIZE(stdlinks); i++) {
+               udev_selinux_setfscreateconat(udev, dirfd(dir_to), stdlinks[i].link, S_IFLNK);
+               if (symlinkat(stdlinks[i].target, dirfd(dir_to), stdlinks[i].link) < 0 && errno == EEXIST)
+                       utimensat(dirfd(dir_to), stdlinks[i].link, NULL, AT_SYMLINK_NOFOLLOW);
+               udev_selinux_resetfscreatecon(udev);
+       }
+
+       /* copy content from /lib/udev/devices to /dev */
+       dir_from = opendir(LIBEXECDIR "/devices");
+       if (dir_from != NULL) {
+               copy_dir(udev, dir_from, dir_to, 8);
+               closedir(dir_from);
+       }
+
+       closedir(dir_to);
+}
+
+static int mem_size_mb(void)
+{
+       FILE *f;
+       char buf[4096];
+       long int memsize = -1;
+
+       f = fopen("/proc/meminfo", "r");
+       if (f == NULL)
+               return -1;
+
+       while (fgets(buf, sizeof(buf), f) != NULL) {
+               long int value;
+
+               if (sscanf(buf, "MemTotal: %ld kB", &value) == 1) {
+                       memsize = value / 1024;
+                       break;
+               }
+       }
+
+       fclose(f);
+       return memsize;
+}
+
 int main(int argc, char *argv[])
 {
        struct udev *udev;
@@ -858,6 +959,8 @@ int main(int argc, char *argv[])
        if (write(STDERR_FILENO, 0, 0) < 0)
                dup2(fd, STDERR_FILENO);
 
+       prepare_dev(udev);
+
        /* init control socket, bind() ensures, that only one udevd instance is running */
        udev_ctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH);
        if (udev_ctrl == NULL) {