struct udev_device *udev_watch_lookup(struct udev *udev, int wd) { char filename[UTIL_PATH_SIZE]; char majmin[UTIL_PATH_SIZE]; char *s; size_t l; ssize_t len; int maj, min; char type; dev_t devnum; if (inotify_fd < 0 || wd < 0) return NULL; snprintf(filename, sizeof(filename), "%s/.udev/watch/%d", udev_get_dev_path(udev), wd); s = majmin; l = util_strpcpy(&s, sizeof(majmin), udev_get_sys_path(udev)); len = readlink(filename, s, l); if (len <= 0 || (size_t)len == l) return NULL; s[len] = '\0'; if (sscanf(s, "%c%i:%i", &type, &maj, &min) != 3) return NULL; devnum = makedev(maj, min); return udev_device_new_from_devnum(udev, type, devnum); }
/* move any old watches directory out of the way, and then restore * the watches */ void udev_watch_restore(struct udev *udev) { char filename[UTIL_PATH_SIZE], oldname[UTIL_PATH_SIZE]; if (inotify_fd < 0) return; util_strscpyl(oldname, sizeof(oldname), udev_get_dev_path(udev), "/.udev/watch.old", NULL); util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/watch", NULL); if (rename(filename, oldname) == 0) { DIR *dir; struct dirent *ent; dir = opendir(oldname); if (dir == NULL) { err(udev, "unable to open old watches dir '%s', old watches will not be restored: %m", oldname); return; } for (ent = readdir(dir); ent != NULL; ent = readdir(dir)) { char device[UTIL_PATH_SIZE]; char *s; size_t l; ssize_t len; struct udev_device *dev; int maj, min; char type; if (ent->d_name[0] == '.') continue; s = device; l = util_strpcpy(&s, sizeof(device), udev_get_sys_path(udev)); len = readlinkat(dirfd(dir), ent->d_name, s, l); if (len <= 0 || len == (ssize_t)l) goto unlink; s[len] = '\0'; if (sscanf(s, "%c%i:%i", &type, &maj, &min) != 3) goto unlink; dev = udev_device_new_from_devnum(udev, type, makedev(maj, min)); if (dev == NULL) goto unlink; info(udev, "restoring old watch on '%s'\n", udev_device_get_devnode(dev)); udev_watch_begin(udev, dev); udev_device_unref(dev); unlink: unlinkat(dirfd(dir), ent->d_name, 0); } closedir(dir); rmdir(oldname); } else if (errno != ENOENT) { err(udev, "unable to move watches dir '%s', old watches will not be restored: %m", filename); } }
struct udev_device *udev_watch_lookup(struct udev *udev, int wd) { char filename[UTIL_PATH_SIZE]; char majmin[UTIL_PATH_SIZE]; char *s; size_t l; ssize_t len; if (inotify_fd < 0 || wd < 0) return NULL; snprintf(filename, sizeof(filename), "%s/watch/%d", udev_get_run_path(udev), wd); s = majmin; l = util_strpcpy(&s, sizeof(majmin), udev_get_sys_path(udev)); len = readlink(filename, s, l); if (len <= 0 || (size_t)len == l) return NULL; s[len] = '\0'; return udev_device_new_from_device_id(udev, s); }
static int node_symlink(struct udev *udev, const char *node, const char *slink) { struct stat stats; char target[UTIL_PATH_SIZE]; char *s; size_t l; char slink_tmp[UTIL_PATH_SIZE + sizeof(TMP_FILE_EXT)]; int i = 0; int tail = 0; int err = 0; /* use relative link */ target[0] = '\0'; while (node[i] && (node[i] == slink[i])) { if (node[i] == '/') tail = i+1; i++; } s = target; l = sizeof(target); while (slink[i] != '\0') { if (slink[i] == '/') l = util_strpcpy(&s, l, "../"); i++; } l = util_strscpy(s, l, &node[tail]); if (l == 0) { err = -EINVAL; goto exit; } /* preserve link with correct target, do not replace node of other device */ if (lstat(slink, &stats) == 0) { if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { struct stat stats2; info(udev, "found existing node instead of symlink '%s'\n", slink); if (lstat(node, &stats2) == 0) { if ((stats.st_mode & S_IFMT) == (stats2.st_mode & S_IFMT) && stats.st_rdev == stats2.st_rdev && stats.st_ino != stats2.st_ino) { info(udev, "replace device node '%s' with symlink to our node '%s'\n", slink, node); } else { err(udev, "device node '%s' already exists, " "link to '%s' will not overwrite it\n", slink, node); goto exit; } } } else if (S_ISLNK(stats.st_mode)) { char buf[UTIL_PATH_SIZE]; int len; dbg(udev, "found existing symlink '%s'\n", slink); len = readlink(slink, buf, sizeof(buf)); if (len > 0 && len < (int)sizeof(buf)) { buf[len] = '\0'; if (strcmp(target, buf) == 0) { info(udev, "preserve already existing symlink '%s' to '%s'\n", slink, target); udev_selinux_lsetfilecon(udev, slink, S_IFLNK); utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW); goto exit; } } } } else { info(udev, "creating symlink '%s' to '%s'\n", slink, target); do { err = util_create_path_selinux(udev, slink); if (err != 0 && err != -ENOENT) break; udev_selinux_setfscreatecon(udev, slink, S_IFLNK); err = symlink(target, slink); if (err != 0) err = -errno; udev_selinux_resetfscreatecon(udev); } while (err == -ENOENT); if (err == 0) goto exit; } info(udev, "atomically replace '%s'\n", slink); util_strscpyl(slink_tmp, sizeof(slink_tmp), slink, TMP_FILE_EXT, NULL); unlink(slink_tmp); do { err = util_create_path_selinux(udev, slink_tmp); if (err != 0 && err != -ENOENT) break; udev_selinux_setfscreatecon(udev, slink_tmp, S_IFLNK); err = symlink(target, slink_tmp); if (err != 0) err = -errno; udev_selinux_resetfscreatecon(udev); } while (err == -ENOENT); if (err != 0) { err(udev, "symlink '%s' '%s' failed: %m\n", target, slink_tmp); goto exit; } err = rename(slink_tmp, slink); if (err != 0) { err(udev, "rename '%s' '%s' failed: %m\n", slink_tmp, slink); unlink(slink_tmp); } exit: return err; }
static int node_symlink(struct udev *udev, const char *node, const char *slink) { struct stat stats; char target[UTIL_PATH_SIZE]; char *s; size_t l; char slink_tmp[UTIL_PATH_SIZE + sizeof(TMP_FILE_EXT)]; int i = 0; int tail = 0; int err = 0; /* use relative link */ target[0] = '\0'; while (node[i] && (node[i] == slink[i])) { if (node[i] == '/') tail = i+1; i++; } s = target; l = sizeof(target); while (slink[i] != '\0') { if (slink[i] == '/') l = util_strpcpy(&s, l, "../"); i++; } l = util_strscpy(s, l, &node[tail]); if (l == 0) { err = -EINVAL; goto exit; } /* preserve link with correct target, do not replace node of other device */ if (lstat(slink, &stats) == 0) { if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { log_error("conflicting device node '%s' found, link to '%s' will not be created\n", slink, node); goto exit; } else if (S_ISLNK(stats.st_mode)) { char buf[UTIL_PATH_SIZE]; int len; len = readlink(slink, buf, sizeof(buf)); if (len > 0 && len < (int)sizeof(buf)) { buf[len] = '\0'; if (strcmp(target, buf) == 0) { log_debug("preserve already existing symlink '%s' to '%s'\n", slink, target); label_fix(slink, true, false); utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW); goto exit; } } } } else { log_debug("creating symlink '%s' to '%s'\n", slink, target); do { err = mkdir_parents_label(slink, 0755); if (err != 0 && err != -ENOENT) break; label_context_set(slink, S_IFLNK); err = symlink(target, slink); if (err != 0) err = -errno; label_context_clear(); } while (err == -ENOENT); if (err == 0) goto exit; } log_debug("atomically replace '%s'\n", slink); util_strscpyl(slink_tmp, sizeof(slink_tmp), slink, TMP_FILE_EXT, NULL); unlink(slink_tmp); do { err = mkdir_parents_label(slink_tmp, 0755); if (err != 0 && err != -ENOENT) break; label_context_set(slink_tmp, S_IFLNK); err = symlink(target, slink_tmp); if (err != 0) err = -errno; label_context_clear(); } while (err == -ENOENT); if (err != 0) { log_error("symlink '%s' '%s' failed: %m\n", target, slink_tmp); goto exit; } err = rename(slink_tmp, slink); if (err != 0) { log_error("rename '%s' '%s' failed: %m\n", slink_tmp, slink); unlink(slink_tmp); } exit: return err; }