From 98631e1c21090db8fe8800f2a6658592406670ac Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Tue, 9 Jun 2009 23:12:12 +0200 Subject: libudev: rename private files to *-private.c --- libudev/libudev-device-db-write.c | 139 ----------- libudev/libudev-device-private.c | 139 +++++++++++ libudev/libudev-private.h | 4 +- libudev/libudev-queue-export.c | 470 -------------------------------------- libudev/libudev-queue-private.c | 470 ++++++++++++++++++++++++++++++++++++++ udev/Makefile.am | 4 +- 6 files changed, 613 insertions(+), 613 deletions(-) delete mode 100644 libudev/libudev-device-db-write.c create mode 100644 libudev/libudev-device-private.c delete mode 100644 libudev/libudev-queue-export.c create mode 100644 libudev/libudev-queue-private.c diff --git a/libudev/libudev-device-db-write.c b/libudev/libudev-device-db-write.c deleted file mode 100644 index 68dc0a5b98..0000000000 --- a/libudev/libudev-device-db-write.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008 Kay Sievers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -static size_t devpath_to_db_path(struct udev *udev, const char *devpath, char *filename, size_t len) -{ - char *s; - size_t l; - - s = filename; - l = util_strpcpyl(&s, len, udev_get_dev_path(udev), "/.udev/db/", NULL); - return util_path_encode(devpath, s, l); -} - -int udev_device_update_db(struct udev_device *udev_device) -{ - struct udev *udev = udev_device_get_udev(udev_device); - char filename[UTIL_PATH_SIZE]; - FILE *f; - char target[232]; /* on 64bit, tmpfs inlines up to 239 bytes */ - size_t devlen = strlen(udev_get_dev_path(udev))+1; - char *s; - size_t l; - struct udev_list_entry *list_entry; - int ret; - - devpath_to_db_path(udev, udev_device_get_devpath(udev_device), filename, sizeof(filename)); - util_create_path(udev, filename); - unlink(filename); - - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) - if (udev_list_entry_get_flag(list_entry)) - goto file; - if (udev_device_get_num_fake_partitions(udev_device) != 0) - goto file; - if (udev_device_get_ignore_remove(udev_device)) - goto file; - if (udev_device_get_devlink_priority(udev_device) != 0) - goto file; - if (udev_device_get_event_timeout(udev_device) >= 0) - goto file; - if (udev_device_get_watch_handle(udev_device) >= 0) - goto file; - if (udev_device_get_devnode(udev_device) == NULL) - goto out; - - /* - * if we have only the node and symlinks to store, try not to waste - * tmpfs memory -- store values, if they fit, in a symlink target - */ - s = target; - l = util_strpcpy(&s, sizeof(target), &udev_device_get_devnode(udev_device)[devlen]); - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(udev_device)) { - l = util_strpcpyl(&s, l, " ", &udev_list_entry_get_name(list_entry)[devlen], NULL); - if (l == 0) { - info(udev, "size of links too large, create file\n"); - goto file; - } - } - info(udev, "create db link (%s)\n", target); - udev_selinux_setfscreatecon(udev, filename, S_IFLNK); - ret = symlink(target, filename); - udev_selinux_resetfscreatecon(udev); - if (ret == 0) - goto out; -file: - f = fopen(filename, "w"); - if (f == NULL) { - err(udev, "unable to create db file '%s': %m\n", filename); - return -1; - } - info(udev, "created db file for '%s' in '%s'\n", udev_device_get_devpath(udev_device), filename); - - if (udev_device_get_devnode(udev_device) != NULL) { - fprintf(f, "N:%s\n", &udev_device_get_devnode(udev_device)[devlen]); - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(udev_device)) - fprintf(f, "S:%s\n", &udev_list_entry_get_name(list_entry)[devlen]); - } - if (udev_device_get_devlink_priority(udev_device) != 0) - fprintf(f, "L:%u\n", udev_device_get_devlink_priority(udev_device)); - if (udev_device_get_event_timeout(udev_device) >= 0) - fprintf(f, "T:%u\n", udev_device_get_event_timeout(udev_device)); - if (udev_device_get_num_fake_partitions(udev_device) != 0) - fprintf(f, "A:%u\n", udev_device_get_num_fake_partitions(udev_device)); - if (udev_device_get_ignore_remove(udev_device)) - fprintf(f, "R:%u\n", udev_device_get_ignore_remove(udev_device)); - if (udev_device_get_watch_handle(udev_device) >= 0) - fprintf(f, "W:%u\n", udev_device_get_watch_handle(udev_device)); - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) { - if (!udev_list_entry_get_flag(list_entry)) - continue; - fprintf(f, "E:%s=%s\n", - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); - } - fclose(f); -out: - return 0; -} - -int udev_device_delete_db(struct udev_device *udev_device) -{ - struct udev *udev = udev_device_get_udev(udev_device); - char filename[UTIL_PATH_SIZE]; - - devpath_to_db_path(udev, udev_device_get_devpath(udev_device), filename, sizeof(filename)); - unlink(filename); - return 0; -} - -int udev_device_rename_db(struct udev_device *udev_device, const char *devpath_old) -{ - struct udev *udev = udev_device_get_udev(udev_device); - char filename_old[UTIL_PATH_SIZE]; - char filename[UTIL_PATH_SIZE]; - - devpath_to_db_path(udev, devpath_old, filename_old, sizeof(filename_old)); - devpath_to_db_path(udev, udev_device_get_devpath(udev_device), filename, sizeof(filename)); - return rename(filename_old, filename); -} diff --git a/libudev/libudev-device-private.c b/libudev/libudev-device-private.c new file mode 100644 index 0000000000..68dc0a5b98 --- /dev/null +++ b/libudev/libudev-device-private.c @@ -0,0 +1,139 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2008 Kay Sievers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +static size_t devpath_to_db_path(struct udev *udev, const char *devpath, char *filename, size_t len) +{ + char *s; + size_t l; + + s = filename; + l = util_strpcpyl(&s, len, udev_get_dev_path(udev), "/.udev/db/", NULL); + return util_path_encode(devpath, s, l); +} + +int udev_device_update_db(struct udev_device *udev_device) +{ + struct udev *udev = udev_device_get_udev(udev_device); + char filename[UTIL_PATH_SIZE]; + FILE *f; + char target[232]; /* on 64bit, tmpfs inlines up to 239 bytes */ + size_t devlen = strlen(udev_get_dev_path(udev))+1; + char *s; + size_t l; + struct udev_list_entry *list_entry; + int ret; + + devpath_to_db_path(udev, udev_device_get_devpath(udev_device), filename, sizeof(filename)); + util_create_path(udev, filename); + unlink(filename); + + udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) + if (udev_list_entry_get_flag(list_entry)) + goto file; + if (udev_device_get_num_fake_partitions(udev_device) != 0) + goto file; + if (udev_device_get_ignore_remove(udev_device)) + goto file; + if (udev_device_get_devlink_priority(udev_device) != 0) + goto file; + if (udev_device_get_event_timeout(udev_device) >= 0) + goto file; + if (udev_device_get_watch_handle(udev_device) >= 0) + goto file; + if (udev_device_get_devnode(udev_device) == NULL) + goto out; + + /* + * if we have only the node and symlinks to store, try not to waste + * tmpfs memory -- store values, if they fit, in a symlink target + */ + s = target; + l = util_strpcpy(&s, sizeof(target), &udev_device_get_devnode(udev_device)[devlen]); + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(udev_device)) { + l = util_strpcpyl(&s, l, " ", &udev_list_entry_get_name(list_entry)[devlen], NULL); + if (l == 0) { + info(udev, "size of links too large, create file\n"); + goto file; + } + } + info(udev, "create db link (%s)\n", target); + udev_selinux_setfscreatecon(udev, filename, S_IFLNK); + ret = symlink(target, filename); + udev_selinux_resetfscreatecon(udev); + if (ret == 0) + goto out; +file: + f = fopen(filename, "w"); + if (f == NULL) { + err(udev, "unable to create db file '%s': %m\n", filename); + return -1; + } + info(udev, "created db file for '%s' in '%s'\n", udev_device_get_devpath(udev_device), filename); + + if (udev_device_get_devnode(udev_device) != NULL) { + fprintf(f, "N:%s\n", &udev_device_get_devnode(udev_device)[devlen]); + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(udev_device)) + fprintf(f, "S:%s\n", &udev_list_entry_get_name(list_entry)[devlen]); + } + if (udev_device_get_devlink_priority(udev_device) != 0) + fprintf(f, "L:%u\n", udev_device_get_devlink_priority(udev_device)); + if (udev_device_get_event_timeout(udev_device) >= 0) + fprintf(f, "T:%u\n", udev_device_get_event_timeout(udev_device)); + if (udev_device_get_num_fake_partitions(udev_device) != 0) + fprintf(f, "A:%u\n", udev_device_get_num_fake_partitions(udev_device)); + if (udev_device_get_ignore_remove(udev_device)) + fprintf(f, "R:%u\n", udev_device_get_ignore_remove(udev_device)); + if (udev_device_get_watch_handle(udev_device) >= 0) + fprintf(f, "W:%u\n", udev_device_get_watch_handle(udev_device)); + udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) { + if (!udev_list_entry_get_flag(list_entry)) + continue; + fprintf(f, "E:%s=%s\n", + udev_list_entry_get_name(list_entry), + udev_list_entry_get_value(list_entry)); + } + fclose(f); +out: + return 0; +} + +int udev_device_delete_db(struct udev_device *udev_device) +{ + struct udev *udev = udev_device_get_udev(udev_device); + char filename[UTIL_PATH_SIZE]; + + devpath_to_db_path(udev, udev_device_get_devpath(udev_device), filename, sizeof(filename)); + unlink(filename); + return 0; +} + +int udev_device_rename_db(struct udev_device *udev_device, const char *devpath_old) +{ + struct udev *udev = udev_device_get_udev(udev_device); + char filename_old[UTIL_PATH_SIZE]; + char filename[UTIL_PATH_SIZE]; + + devpath_to_db_path(udev, devpath_old, filename_old, sizeof(filename_old)); + devpath_to_db_path(udev, udev_device_get_devpath(udev_device), filename, sizeof(filename)); + return rename(filename_old, filename); +} diff --git a/libudev/libudev-private.h b/libudev/libudev-private.h index 9cda7bcb1e..9770e8b63c 100644 --- a/libudev/libudev-private.h +++ b/libudev/libudev-private.h @@ -99,7 +99,7 @@ int udev_device_get_watch_handle(struct udev_device *udev_device); int udev_device_set_watch_handle(struct udev_device *udev_device, int handle); void udev_device_set_info_loaded(struct udev_device *device); -/* libudev-device-db-write.c */ +/* libudev-device-private.c */ int udev_device_update_db(struct udev_device *udev_device); int udev_device_delete_db(struct udev_device *udev_device); int udev_device_rename_db(struct udev_device *udev_device, const char *devpath); @@ -177,7 +177,7 @@ int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum); ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size); ssize_t udev_queue_skip_devpath(FILE *queue_file); -/* libudev-queue-export.c */ +/* libudev-queue-private.c */ struct udev_queue_export *udev_queue_export_new(struct udev *udev); void udev_queue_export_unref(struct udev_queue_export *udev_queue_export); void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export); diff --git a/libudev/libudev-queue-export.c b/libudev/libudev-queue-export.c deleted file mode 100644 index 9ae680c386..0000000000 --- a/libudev/libudev-queue-export.c +++ /dev/null @@ -1,470 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008 Kay Sievers - * Copyright (C) 2009 Alan Jenkins - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -/* - * DISCLAIMER - The file format mentioned here is private to udev/libudev, - * and may be changed without notice. - * - * The udev event queue is exported as a binary log file. - * Each log record consists of a sequence number followed by the device path. - * - * When a new event is queued, its details are appended to the log. - * When the event finishes, a second record is appended to the log - * with the same sequence number but a null devpath. - * - * Example: - * {1, "/devices/virtual/mem/null" }, - * {2, "/devices/virtual/mem/zero" }, - * {1, "" }, - * Event 2 is still queued, but event 1 has been finished - * - * The queue does not grow indefinitely. It is periodically re-created - * to remove finished events. Atomic rename() makes this transparent to readers. - * - * The queue file starts with a single sequence number which specifies the - * minimum sequence number in the log that follows. Any events prior to this - * sequence number have already finished. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -static int rebuild_queue_file(struct udev_queue_export *udev_queue_export); - -struct udev_queue_export { - struct udev *udev; - int failed_count; /* number of failed events exported */ - int queued_count; /* number of unfinished events exported in queue file */ - FILE *queue_file; - unsigned long long int seqnum_max; /* earliest sequence number in queue file */ - unsigned long long int seqnum_min; /* latest sequence number in queue file */ - int waste_bytes; /* queue file bytes wasted on finished events */ -}; - -struct udev_queue_export *udev_queue_export_new(struct udev *udev) -{ - struct udev_queue_export *udev_queue_export; - unsigned long long int initial_seqnum; - - if (udev == NULL) - return NULL; - - udev_queue_export = calloc(1, sizeof(struct udev_queue_export)); - if (udev_queue_export == NULL) - return NULL; - udev_queue_export->udev = udev; - - initial_seqnum = udev_get_kernel_seqnum(udev); - udev_queue_export->seqnum_min = initial_seqnum; - udev_queue_export->seqnum_max = initial_seqnum; - - udev_queue_export_cleanup(udev_queue_export); - if (rebuild_queue_file(udev_queue_export) != 0) { - free(udev_queue_export); - return NULL; - } - - return udev_queue_export; -} - -void udev_queue_export_unref(struct udev_queue_export *udev_queue_export) -{ - if (udev_queue_export == NULL) - return; - if (udev_queue_export->queue_file != NULL) - fclose(udev_queue_export->queue_file); - free(udev_queue_export); -} - -void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export) -{ - char filename[UTIL_PATH_SIZE]; - - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.tmp", NULL); - unlink(filename); - - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.bin", NULL); - unlink(filename); -} - -static int skip_to(FILE *file, long offset) -{ - long old_offset; - - /* fseek may drop buffered data, avoid it for small seeks */ - old_offset = ftell(file); - if (offset > old_offset && offset - old_offset <= BUFSIZ) { - size_t skip_bytes = offset - old_offset; - char buf[skip_bytes]; - - if (fread(buf, skip_bytes, 1, file) != skip_bytes) - return -1; - } - - return fseek(file, offset, SEEK_SET); -} - -struct queue_devpaths { - unsigned int devpaths_first; /* index of first queued event */ - unsigned int devpaths_size; - long devpaths[]; /* seqnum -> offset of devpath in queue file (or 0) */ -}; - -/* - * Returns a table mapping seqnum to devpath file offset for currently queued events. - * devpaths[i] represents the event with seqnum = i + udev_queue_export->seqnum_min. - */ -static struct queue_devpaths *build_index(struct udev_queue_export *udev_queue_export) -{ - struct queue_devpaths *devpaths; - unsigned long long int range; - long devpath_offset; - ssize_t devpath_len; - unsigned long long int seqnum; - unsigned long long int n; - unsigned int i; - - /* seek to the first event in the file */ - rewind(udev_queue_export->queue_file); - udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum); - - /* allocate the table */ - range = udev_queue_export->seqnum_min - udev_queue_export->seqnum_max; - if (range - 1 > INT_MAX) { - err(udev_queue_export->udev, "queue file overflow\n"); - return NULL; - } - devpaths = calloc(1, sizeof(struct queue_devpaths) + (range + 1) * sizeof(long)); - if (index == NULL) - return NULL; - devpaths->devpaths_size = range + 1; - - /* read all records and populate the table */ - while(1) { - if (udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum) < 0) - break; - n = seqnum - udev_queue_export->seqnum_max; - if (n >= devpaths->devpaths_size) - goto read_error; - - devpath_offset = ftell(udev_queue_export->queue_file); - devpath_len = udev_queue_skip_devpath(udev_queue_export->queue_file); - if (devpath_len < 0) - goto read_error; - - if (devpath_len > 0) - devpaths->devpaths[n] = devpath_offset; - else - devpaths->devpaths[n] = 0; - } - - /* find first queued event */ - for (i = 0; i < devpaths->devpaths_size; i++) { - if (devpaths->devpaths[i] != 0) - break; - } - devpaths->devpaths_first = i; - - return devpaths; - -read_error: - err(udev_queue_export->udev, "queue file corrupted\n"); - free(devpaths); - return NULL; -} - -static int rebuild_queue_file(struct udev_queue_export *udev_queue_export) -{ - unsigned long long int seqnum; - struct queue_devpaths *devpaths = NULL; - char filename[UTIL_PATH_SIZE]; - char filename_tmp[UTIL_PATH_SIZE]; - FILE *new_queue_file = NULL; - unsigned int i; - - /* read old queue file */ - if (udev_queue_export->queue_file != NULL) { - dbg(udev_queue_export->udev, "compacting queue file, freeing %d bytes\n", - udev_queue_export->waste_bytes); - - devpaths = build_index(udev_queue_export); - if (devpaths != NULL) - udev_queue_export->seqnum_max += devpaths->devpaths_first; - } - if (devpaths == NULL) { - dbg(udev_queue_export->udev, "creating empty queue file\n"); - udev_queue_export->queued_count = 0; - udev_queue_export->seqnum_max = udev_queue_export->seqnum_min; - } - - /* create new queue file */ - util_strscpyl(filename_tmp, sizeof(filename_tmp), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.tmp", NULL); - new_queue_file = fopen(filename_tmp, "w+"); - if (new_queue_file == NULL) - goto error; - seqnum = udev_queue_export->seqnum_max; - fwrite(&seqnum, 1, sizeof(unsigned long long int), new_queue_file); - - /* copy unfinished events only to the new file */ - if (devpaths != NULL) { - for (i = devpaths->devpaths_first; i < devpaths->devpaths_size; i++) { - char devpath[UTIL_PATH_SIZE]; - int err; - unsigned short devpath_len; - - if (devpaths->devpaths[i] != 0) - { - skip_to(udev_queue_export->queue_file, devpaths->devpaths[i]); - err = udev_queue_read_devpath(udev_queue_export->queue_file, devpath, sizeof(devpath)); - devpath_len = err; - - fwrite(&seqnum, sizeof(unsigned long long int), 1, new_queue_file); - fwrite(&devpath_len, sizeof(unsigned short), 1, new_queue_file); - fwrite(devpath, 1, devpath_len, new_queue_file); - } - seqnum++; - } - free(devpaths); - devpaths = NULL; - } - fflush(new_queue_file); - if (ferror(new_queue_file)) - goto error; - - /* rename the new file on top of the old one */ - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.bin", NULL); - if (rename(filename_tmp, filename) != 0) - goto error; - - if (udev_queue_export->queue_file != NULL) - fclose(udev_queue_export->queue_file); - udev_queue_export->queue_file = new_queue_file; - udev_queue_export->waste_bytes = 0; - - return 0; - -error: - err(udev_queue_export->udev, "failed to create queue file: %m\n"); - udev_queue_export_cleanup(udev_queue_export); - - if (udev_queue_export->queue_file != NULL) { - fclose(udev_queue_export->queue_file); - udev_queue_export->queue_file = NULL; - } - if (new_queue_file != NULL) - fclose(new_queue_file); - - if (devpaths != NULL) - free(devpaths); - udev_queue_export->queued_count = 0; - udev_queue_export->waste_bytes = 0; - udev_queue_export->seqnum_max = udev_queue_export->seqnum_min; - - return -1; -} - -static int write_queue_record(struct udev_queue_export *udev_queue_export, - unsigned long long int seqnum, const char *devpath, size_t devpath_len) -{ - unsigned short len; - - if (udev_queue_export->queue_file == NULL) { - dbg(udev_queue_export->udev, "can't record event: queue file not available\n"); - return -1; - } - - if (fwrite(&seqnum, sizeof(unsigned long long int), 1, udev_queue_export->queue_file) != 1) - goto write_error; - - len = (devpath_len < USHRT_MAX) ? devpath_len : USHRT_MAX; - if (fwrite(&len, sizeof(unsigned short), 1, udev_queue_export->queue_file) != 1) - goto write_error; - if (fwrite(devpath, 1, len, udev_queue_export->queue_file) != len) - goto write_error; - - /* *must* flush output; caller may fork */ - if (fflush(udev_queue_export->queue_file) != 0) - goto write_error; - - return 0; - -write_error: - /* if we failed half way through writing a record to a file, - we should not try to write any further records to it. */ - err(udev_queue_export->udev, "error writing to queue file: %m\n"); - fclose(udev_queue_export->queue_file); - udev_queue_export->queue_file = NULL; - - return -1; -} - -enum device_state { - DEVICE_QUEUED, - DEVICE_FINISHED, - DEVICE_FAILED, -}; - -static inline size_t queue_record_size(size_t devpath_len) -{ - return sizeof(unsigned long long int) + sizeof(unsigned short int) + devpath_len; -} - -static int update_queue(struct udev_queue_export *udev_queue_export, - struct udev_device *udev_device, enum device_state state) -{ - unsigned long long int seqnum = udev_device_get_seqnum(udev_device); - const char *devpath = NULL; - size_t devpath_len = 0; - int bytes; - int err; - - if (state == DEVICE_QUEUED) { - devpath = udev_device_get_devpath(udev_device); - devpath_len = strlen(devpath); - } - - /* recover from an earlier failed rebuild */ - if (udev_queue_export->queue_file == NULL) { - if (rebuild_queue_file(udev_queue_export) != 0) - return -1; - } - - /* when the queue files grow too large, they must be garbage collected and rebuilt */ - bytes = ftell(udev_queue_export->queue_file) + queue_record_size(devpath_len); - - /* if we're removing the last event from the queue, that's the best time to rebuild it */ - if (state != DEVICE_QUEUED && udev_queue_export->queued_count == 1 && bytes > 2048) { - /* because we don't need to read the old queue file */ - fclose(udev_queue_export->queue_file); - udev_queue_export->queue_file = NULL; - rebuild_queue_file(udev_queue_export); - return 0; - } - - /* try to rebuild the queue files before they grow larger than one page. */ - if ((udev_queue_export->waste_bytes > bytes / 2) && bytes > 4096) - rebuild_queue_file(udev_queue_export); - - /* don't record a finished event, if we already dropped the event in a failed rebuild */ - if (seqnum < udev_queue_export->seqnum_max) - return 0; - - /* now write to the queue */ - if (state == DEVICE_QUEUED) { - udev_queue_export->queued_count++; - udev_queue_export->seqnum_min = seqnum; - } else { - udev_queue_export->waste_bytes += queue_record_size(devpath_len) + queue_record_size(0); - udev_queue_export->queued_count--; - } - err = write_queue_record(udev_queue_export, seqnum, devpath, devpath_len); - - /* try to handle ENOSPC */ - if (err != 0 && udev_queue_export->queued_count == 0) { - udev_queue_export_cleanup(udev_queue_export); - err = rebuild_queue_file(udev_queue_export); - } - - return err; -} - -static void update_failed(struct udev_queue_export *udev_queue_export, - struct udev_device *udev_device, enum device_state state) -{ - struct udev *udev = udev_device_get_udev(udev_device); - char filename[UTIL_PATH_SIZE]; - char *s; - size_t l; - - if (state != DEVICE_FAILED && udev_queue_export->failed_count == 0) - return; - - /* location of failed file */ - s = filename; - l = util_strpcpyl(&s, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/failed/", NULL); - util_path_encode(udev_device_get_devpath(udev_device), s, l); - - switch (state) { - case DEVICE_FAILED: - /* record event in the failed directory */ - if (udev_queue_export->failed_count == 0) - util_create_path(udev, filename); - udev_queue_export->failed_count++; - - udev_selinux_setfscreatecon(udev, filename, S_IFLNK); - symlink(udev_device_get_devpath(udev_device), filename); - udev_selinux_resetfscreatecon(udev); - break; - - case DEVICE_QUEUED: - /* delete failed file */ - if (unlink(filename) == 0) { - util_delete_path(udev, filename); - udev_queue_export->failed_count--; - } - break; - - case DEVICE_FINISHED: - if (udev_device_get_devpath_old(udev_device) != NULL) { - /* "move" event - rename failed file to current name, do not delete failed */ - char filename_old[UTIL_PATH_SIZE]; - - s = filename_old; - l = util_strpcpyl(&s, sizeof(filename_old), udev_get_dev_path(udev_queue_export->udev), "/.udev/failed/", NULL); - util_path_encode(udev_device_get_devpath_old(udev_device), s, l); - - if (rename(filename_old, filename) == 0) - info(udev, "renamed devpath, moved failed state of '%s' to %s'\n", - udev_device_get_devpath_old(udev_device), udev_device_get_devpath(udev_device)); - } - break; - } - - return; -} - -static int update(struct udev_queue_export *udev_queue_export, - struct udev_device *udev_device, enum device_state state) -{ - update_failed(udev_queue_export, udev_device, state); - - if (update_queue(udev_queue_export, udev_device, state) != 0) - return -1; - - return 0; -} - -int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device) -{ - return update(udev_queue_export, udev_device, DEVICE_QUEUED); -} - -int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device) -{ - return update(udev_queue_export, udev_device, DEVICE_FINISHED); -} - -int udev_queue_export_device_failed(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device) -{ - return update(udev_queue_export, udev_device, DEVICE_FAILED); -} diff --git a/libudev/libudev-queue-private.c b/libudev/libudev-queue-private.c new file mode 100644 index 0000000000..9ae680c386 --- /dev/null +++ b/libudev/libudev-queue-private.c @@ -0,0 +1,470 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2008 Kay Sievers + * Copyright (C) 2009 Alan Jenkins + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +/* + * DISCLAIMER - The file format mentioned here is private to udev/libudev, + * and may be changed without notice. + * + * The udev event queue is exported as a binary log file. + * Each log record consists of a sequence number followed by the device path. + * + * When a new event is queued, its details are appended to the log. + * When the event finishes, a second record is appended to the log + * with the same sequence number but a null devpath. + * + * Example: + * {1, "/devices/virtual/mem/null" }, + * {2, "/devices/virtual/mem/zero" }, + * {1, "" }, + * Event 2 is still queued, but event 1 has been finished + * + * The queue does not grow indefinitely. It is periodically re-created + * to remove finished events. Atomic rename() makes this transparent to readers. + * + * The queue file starts with a single sequence number which specifies the + * minimum sequence number in the log that follows. Any events prior to this + * sequence number have already finished. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +static int rebuild_queue_file(struct udev_queue_export *udev_queue_export); + +struct udev_queue_export { + struct udev *udev; + int failed_count; /* number of failed events exported */ + int queued_count; /* number of unfinished events exported in queue file */ + FILE *queue_file; + unsigned long long int seqnum_max; /* earliest sequence number in queue file */ + unsigned long long int seqnum_min; /* latest sequence number in queue file */ + int waste_bytes; /* queue file bytes wasted on finished events */ +}; + +struct udev_queue_export *udev_queue_export_new(struct udev *udev) +{ + struct udev_queue_export *udev_queue_export; + unsigned long long int initial_seqnum; + + if (udev == NULL) + return NULL; + + udev_queue_export = calloc(1, sizeof(struct udev_queue_export)); + if (udev_queue_export == NULL) + return NULL; + udev_queue_export->udev = udev; + + initial_seqnum = udev_get_kernel_seqnum(udev); + udev_queue_export->seqnum_min = initial_seqnum; + udev_queue_export->seqnum_max = initial_seqnum; + + udev_queue_export_cleanup(udev_queue_export); + if (rebuild_queue_file(udev_queue_export) != 0) { + free(udev_queue_export); + return NULL; + } + + return udev_queue_export; +} + +void udev_queue_export_unref(struct udev_queue_export *udev_queue_export) +{ + if (udev_queue_export == NULL) + return; + if (udev_queue_export->queue_file != NULL) + fclose(udev_queue_export->queue_file); + free(udev_queue_export); +} + +void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export) +{ + char filename[UTIL_PATH_SIZE]; + + util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.tmp", NULL); + unlink(filename); + + util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.bin", NULL); + unlink(filename); +} + +static int skip_to(FILE *file, long offset) +{ + long old_offset; + + /* fseek may drop buffered data, avoid it for small seeks */ + old_offset = ftell(file); + if (offset > old_offset && offset - old_offset <= BUFSIZ) { + size_t skip_bytes = offset - old_offset; + char buf[skip_bytes]; + + if (fread(buf, skip_bytes, 1, file) != skip_bytes) + return -1; + } + + return fseek(file, offset, SEEK_SET); +} + +struct queue_devpaths { + unsigned int devpaths_first; /* index of first queued event */ + unsigned int devpaths_size; + long devpaths[]; /* seqnum -> offset of devpath in queue file (or 0) */ +}; + +/* + * Returns a table mapping seqnum to devpath file offset for currently queued events. + * devpaths[i] represents the event with seqnum = i + udev_queue_export->seqnum_min. + */ +static struct queue_devpaths *build_index(struct udev_queue_export *udev_queue_export) +{ + struct queue_devpaths *devpaths; + unsigned long long int range; + long devpath_offset; + ssize_t devpath_len; + unsigned long long int seqnum; + unsigned long long int n; + unsigned int i; + + /* seek to the first event in the file */ + rewind(udev_queue_export->queue_file); + udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum); + + /* allocate the table */ + range = udev_queue_export->seqnum_min - udev_queue_export->seqnum_max; + if (range - 1 > INT_MAX) { + err(udev_queue_export->udev, "queue file overflow\n"); + return NULL; + } + devpaths = calloc(1, sizeof(struct queue_devpaths) + (range + 1) * sizeof(long)); + if (index == NULL) + return NULL; + devpaths->devpaths_size = range + 1; + + /* read all records and populate the table */ + while(1) { + if (udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum) < 0) + break; + n = seqnum - udev_queue_export->seqnum_max; + if (n >= devpaths->devpaths_size) + goto read_error; + + devpath_offset = ftell(udev_queue_export->queue_file); + devpath_len = udev_queue_skip_devpath(udev_queue_export->queue_file); + if (devpath_len < 0) + goto read_error; + + if (devpath_len > 0) + devpaths->devpaths[n] = devpath_offset; + else + devpaths->devpaths[n] = 0; + } + + /* find first queued event */ + for (i = 0; i < devpaths->devpaths_size; i++) { + if (devpaths->devpaths[i] != 0) + break; + } + devpaths->devpaths_first = i; + + return devpaths; + +read_error: + err(udev_queue_export->udev, "queue file corrupted\n"); + free(devpaths); + return NULL; +} + +static int rebuild_queue_file(struct udev_queue_export *udev_queue_export) +{ + unsigned long long int seqnum; + struct queue_devpaths *devpaths = NULL; + char filename[UTIL_PATH_SIZE]; + char filename_tmp[UTIL_PATH_SIZE]; + FILE *new_queue_file = NULL; + unsigned int i; + + /* read old queue file */ + if (udev_queue_export->queue_file != NULL) { + dbg(udev_queue_export->udev, "compacting queue file, freeing %d bytes\n", + udev_queue_export->waste_bytes); + + devpaths = build_index(udev_queue_export); + if (devpaths != NULL) + udev_queue_export->seqnum_max += devpaths->devpaths_first; + } + if (devpaths == NULL) { + dbg(udev_queue_export->udev, "creating empty queue file\n"); + udev_queue_export->queued_count = 0; + udev_queue_export->seqnum_max = udev_queue_export->seqnum_min; + } + + /* create new queue file */ + util_strscpyl(filename_tmp, sizeof(filename_tmp), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.tmp", NULL); + new_queue_file = fopen(filename_tmp, "w+"); + if (new_queue_file == NULL) + goto error; + seqnum = udev_queue_export->seqnum_max; + fwrite(&seqnum, 1, sizeof(unsigned long long int), new_queue_file); + + /* copy unfinished events only to the new file */ + if (devpaths != NULL) { + for (i = devpaths->devpaths_first; i < devpaths->devpaths_size; i++) { + char devpath[UTIL_PATH_SIZE]; + int err; + unsigned short devpath_len; + + if (devpaths->devpaths[i] != 0) + { + skip_to(udev_queue_export->queue_file, devpaths->devpaths[i]); + err = udev_queue_read_devpath(udev_queue_export->queue_file, devpath, sizeof(devpath)); + devpath_len = err; + + fwrite(&seqnum, sizeof(unsigned long long int), 1, new_queue_file); + fwrite(&devpath_len, sizeof(unsigned short), 1, new_queue_file); + fwrite(devpath, 1, devpath_len, new_queue_file); + } + seqnum++; + } + free(devpaths); + devpaths = NULL; + } + fflush(new_queue_file); + if (ferror(new_queue_file)) + goto error; + + /* rename the new file on top of the old one */ + util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.bin", NULL); + if (rename(filename_tmp, filename) != 0) + goto error; + + if (udev_queue_export->queue_file != NULL) + fclose(udev_queue_export->queue_file); + udev_queue_export->queue_file = new_queue_file; + udev_queue_export->waste_bytes = 0; + + return 0; + +error: + err(udev_queue_export->udev, "failed to create queue file: %m\n"); + udev_queue_export_cleanup(udev_queue_export); + + if (udev_queue_export->queue_file != NULL) { + fclose(udev_queue_export->queue_file); + udev_queue_export->queue_file = NULL; + } + if (new_queue_file != NULL) + fclose(new_queue_file); + + if (devpaths != NULL) + free(devpaths); + udev_queue_export->queued_count = 0; + udev_queue_export->waste_bytes = 0; + udev_queue_export->seqnum_max = udev_queue_export->seqnum_min; + + return -1; +} + +static int write_queue_record(struct udev_queue_export *udev_queue_export, + unsigned long long int seqnum, const char *devpath, size_t devpath_len) +{ + unsigned short len; + + if (udev_queue_export->queue_file == NULL) { + dbg(udev_queue_export->udev, "can't record event: queue file not available\n"); + return -1; + } + + if (fwrite(&seqnum, sizeof(unsigned long long int), 1, udev_queue_export->queue_file) != 1) + goto write_error; + + len = (devpath_len < USHRT_MAX) ? devpath_len : USHRT_MAX; + if (fwrite(&len, sizeof(unsigned short), 1, udev_queue_export->queue_file) != 1) + goto write_error; + if (fwrite(devpath, 1, len, udev_queue_export->queue_file) != len) + goto write_error; + + /* *must* flush output; caller may fork */ + if (fflush(udev_queue_export->queue_file) != 0) + goto write_error; + + return 0; + +write_error: + /* if we failed half way through writing a record to a file, + we should not try to write any further records to it. */ + err(udev_queue_export->udev, "error writing to queue file: %m\n"); + fclose(udev_queue_export->queue_file); + udev_queue_export->queue_file = NULL; + + return -1; +} + +enum device_state { + DEVICE_QUEUED, + DEVICE_FINISHED, + DEVICE_FAILED, +}; + +static inline size_t queue_record_size(size_t devpath_len) +{ + return sizeof(unsigned long long int) + sizeof(unsigned short int) + devpath_len; +} + +static int update_queue(struct udev_queue_export *udev_queue_export, + struct udev_device *udev_device, enum device_state state) +{ + unsigned long long int seqnum = udev_device_get_seqnum(udev_device); + const char *devpath = NULL; + size_t devpath_len = 0; + int bytes; + int err; + + if (state == DEVICE_QUEUED) { + devpath = udev_device_get_devpath(udev_device); + devpath_len = strlen(devpath); + } + + /* recover from an earlier failed rebuild */ + if (udev_queue_export->queue_file == NULL) { + if (rebuild_queue_file(udev_queue_export) != 0) + return -1; + } + + /* when the queue files grow too large, they must be garbage collected and rebuilt */ + bytes = ftell(udev_queue_export->queue_file) + queue_record_size(devpath_len); + + /* if we're removing the last event from the queue, that's the best time to rebuild it */ + if (state != DEVICE_QUEUED && udev_queue_export->queued_count == 1 && bytes > 2048) { + /* because we don't need to read the old queue file */ + fclose(udev_queue_export->queue_file); + udev_queue_export->queue_file = NULL; + rebuild_queue_file(udev_queue_export); + return 0; + } + + /* try to rebuild the queue files before they grow larger than one page. */ + if ((udev_queue_export->waste_bytes > bytes / 2) && bytes > 4096) + rebuild_queue_file(udev_queue_export); + + /* don't record a finished event, if we already dropped the event in a failed rebuild */ + if (seqnum < udev_queue_export->seqnum_max) + return 0; + + /* now write to the queue */ + if (state == DEVICE_QUEUED) { + udev_queue_export->queued_count++; + udev_queue_export->seqnum_min = seqnum; + } else { + udev_queue_export->waste_bytes += queue_record_size(devpath_len) + queue_record_size(0); + udev_queue_export->queued_count--; + } + err = write_queue_record(udev_queue_export, seqnum, devpath, devpath_len); + + /* try to handle ENOSPC */ + if (err != 0 && udev_queue_export->queued_count == 0) { + udev_queue_export_cleanup(udev_queue_export); + err = rebuild_queue_file(udev_queue_export); + } + + return err; +} + +static void update_failed(struct udev_queue_export *udev_queue_export, + struct udev_device *udev_device, enum device_state state) +{ + struct udev *udev = udev_device_get_udev(udev_device); + char filename[UTIL_PATH_SIZE]; + char *s; + size_t l; + + if (state != DEVICE_FAILED && udev_queue_export->failed_count == 0) + return; + + /* location of failed file */ + s = filename; + l = util_strpcpyl(&s, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/failed/", NULL); + util_path_encode(udev_device_get_devpath(udev_device), s, l); + + switch (state) { + case DEVICE_FAILED: + /* record event in the failed directory */ + if (udev_queue_export->failed_count == 0) + util_create_path(udev, filename); + udev_queue_export->failed_count++; + + udev_selinux_setfscreatecon(udev, filename, S_IFLNK); + symlink(udev_device_get_devpath(udev_device), filename); + udev_selinux_resetfscreatecon(udev); + break; + + case DEVICE_QUEUED: + /* delete failed file */ + if (unlink(filename) == 0) { + util_delete_path(udev, filename); + udev_queue_export->failed_count--; + } + break; + + case DEVICE_FINISHED: + if (udev_device_get_devpath_old(udev_device) != NULL) { + /* "move" event - rename failed file to current name, do not delete failed */ + char filename_old[UTIL_PATH_SIZE]; + + s = filename_old; + l = util_strpcpyl(&s, sizeof(filename_old), udev_get_dev_path(udev_queue_export->udev), "/.udev/failed/", NULL); + util_path_encode(udev_device_get_devpath_old(udev_device), s, l); + + if (rename(filename_old, filename) == 0) + info(udev, "renamed devpath, moved failed state of '%s' to %s'\n", + udev_device_get_devpath_old(udev_device), udev_device_get_devpath(udev_device)); + } + break; + } + + return; +} + +static int update(struct udev_queue_export *udev_queue_export, + struct udev_device *udev_device, enum device_state state) +{ + update_failed(udev_queue_export, udev_device, state); + + if (update_queue(udev_queue_export, udev_device, state) != 0) + return -1; + + return 0; +} + +int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device) +{ + return update(udev_queue_export, udev_device, DEVICE_QUEUED); +} + +int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device) +{ + return update(udev_queue_export, udev_device, DEVICE_FINISHED); +} + +int udev_queue_export_device_failed(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device) +{ + return update(udev_queue_export, udev_device, DEVICE_FAILED); +} diff --git a/udev/Makefile.am b/udev/Makefile.am index f2ceb99774..14ec79e8d6 100644 --- a/udev/Makefile.am +++ b/udev/Makefile.am @@ -22,11 +22,11 @@ common_files = \ ../libudev/libudev-util.c \ ../libudev/libudev-util-private.c \ ../libudev/libudev-device.c \ - ../libudev/libudev-device-db-write.c \ + ../libudev/libudev-device-private.c \ ../libudev/libudev-monitor.c \ ../libudev/libudev-enumerate.c \ ../libudev/libudev-queue.c \ - ../libudev/libudev-queue-export.c \ + ../libudev/libudev-queue-private.c \ ../libudev/libudev-ctrl.c if USE_SELINUX -- cgit v1.2.3-54-g00ecf