#include <unistd.h>
#include "fd-util.h"
+#include "fs-util.h"
#include "hexdecoct.h"
#include "id128-util.h"
#include "io-util.h"
#include "string-util.h"
#include "sync-util.h"
-char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]) {
- unsigned n, k = 0;
-
- assert(s);
-
- /* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */
-
- for (n = 0; n < 16; n++) {
-
- if (IN_SET(n, 4, 6, 8, 10))
- s[k++] = '-';
-
- s[k++] = hexchar(id.bytes[n] >> 4);
- s[k++] = hexchar(id.bytes[n] & 0xF);
- }
-
- assert(k == 36);
-
- s[k] = 0;
-
- return s;
-}
-
bool id128_is_valid(const char *s) {
- size_t i, l;
+ size_t l;
assert(s);
l = strlen(s);
- if (l == 32) {
-
- /* Plain formatted 128bit hex string */
-
- for (i = 0; i < l; i++) {
- char c = s[i];
-
- if (!(c >= '0' && c <= '9') &&
- !(c >= 'a' && c <= 'z') &&
- !(c >= 'A' && c <= 'Z'))
- return false;
- }
- } else if (l == 36) {
+ if (l == SD_ID128_STRING_MAX - 1)
+ /* Plain formatted 128-bit hex string */
+ return in_charset(s, HEXDIGITS);
+ if (l == SD_ID128_UUID_STRING_MAX - 1) {
/* Formatted UUID */
-
- for (i = 0; i < l; i++) {
+ for (size_t i = 0; i < l; i++) {
char c = s[i];
if (IN_SET(i, 8, 13, 18, 23)) {
if (c != '-')
return false;
- } else {
- if (!(c >= '0' && c <= '9') &&
- !(c >= 'a' && c <= 'z') &&
- !(c >= 'A' && c <= 'Z'))
- return false;
- }
+ } else if (!ascii_ishex(c))
+ return false;
}
+ return true;
+ }
- } else
- return false;
-
- return true;
+ return false;
}
-int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) {
- char buffer[36 + 2];
+int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret) {
+ char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */
+ sd_id128_t id;
ssize_t l;
+ int r;
assert(fd >= 0);
- assert(f < _ID128_FORMAT_MAX);
- /* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both
+ /* Reads an 128-bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both
* optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they
* aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you
- * accept". */
+ * accept".
+ *
+ * This returns the following:
+ * -ENOMEDIUM: an empty string,
+ * -ENOPKG: "uninitialized" or "uninitialized\n",
+ * -EUCLEAN: other invalid strings. */
l = loop_read(fd, buffer, sizeof(buffer), false); /* we expect a short read of either 32/33 or 36/37 chars */
if (l < 0)
switch (l) {
- case 13:
- case 14:
- /* Treat an "uninitialized" id file like an empty one */
- return f == ID128_PLAIN_OR_UNINIT && strneq(buffer, "uninitialized\n", l) ? -ENOMEDIUM : -EINVAL;
+ case STRLEN("uninitialized"):
+ case STRLEN("uninitialized\n"):
+ return strneq(buffer, "uninitialized\n", l) ? -ENOPKG : -EINVAL;
- case 33: /* plain UUID with trailing newline */
- if (buffer[32] != '\n')
- return -EINVAL;
+ case SD_ID128_STRING_MAX: /* plain UUID with trailing newline */
+ if (buffer[SD_ID128_STRING_MAX-1] != '\n')
+ return -EUCLEAN;
_fallthrough_;
- case 32: /* plain UUID without trailing newline */
- if (f == ID128_UUID)
- return -EINVAL;
+ case SD_ID128_STRING_MAX-1: /* plain UUID without trailing newline */
+ if (!FLAGS_SET(f, ID128_FORMAT_PLAIN))
+ return -EUCLEAN;
- buffer[32] = 0;
+ buffer[SD_ID128_STRING_MAX-1] = 0;
break;
- case 37: /* RFC UUID with trailing newline */
- if (buffer[36] != '\n')
- return -EINVAL;
+ case SD_ID128_UUID_STRING_MAX: /* RFC UUID with trailing newline */
+ if (buffer[SD_ID128_UUID_STRING_MAX-1] != '\n')
+ return -EUCLEAN;
_fallthrough_;
- case 36: /* RFC UUID without trailing newline */
- if (IN_SET(f, ID128_PLAIN, ID128_PLAIN_OR_UNINIT))
- return -EINVAL;
+ case SD_ID128_UUID_STRING_MAX-1: /* RFC UUID without trailing newline */
+ if (!FLAGS_SET(f, ID128_FORMAT_UUID))
+ return -EUCLEAN;
- buffer[36] = 0;
+ buffer[SD_ID128_UUID_STRING_MAX-1] = 0;
break;
default:
- return -EINVAL;
+ return -EUCLEAN;
}
- return sd_id128_from_string(buffer, ret);
+ r = sd_id128_from_string(buffer, &id);
+ if (r == -EINVAL)
+ return -EUCLEAN;
+ if (r < 0)
+ return r;
+
+ if (FLAGS_SET(f, ID128_REFUSE_NULL) && sd_id128_is_null(id))
+ return -ENOMEDIUM;
+
+ if (ret)
+ *ret = id;
+ return 0;
}
-int id128_read(const char *p, Id128Format f, sd_id128_t *ret) {
- _cleanup_close_ int fd = -1;
+int id128_read_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t *ret) {
+ _cleanup_close_ int fd = -EBADF;
- fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+ assert(path);
+
+ fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY, /* xopen_flags = */ 0, /* mode = */ 0);
if (fd < 0)
- return -errno;
+ return fd;
return id128_read_fd(fd, f, ret);
}
-int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) {
- char buffer[36 + 2];
+int id128_write_fd(int fd, Id128Flag f, sd_id128_t id) {
+ char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */
size_t sz;
int r;
assert(fd >= 0);
- assert(f < _ID128_FORMAT_MAX);
+ assert(IN_SET((f & ID128_FORMAT_ANY), ID128_FORMAT_PLAIN, ID128_FORMAT_UUID));
+
+ if (FLAGS_SET(f, ID128_REFUSE_NULL) && sd_id128_is_null(id))
+ return -ENOMEDIUM;
- if (f != ID128_UUID) {
- sd_id128_to_string(id, buffer);
- buffer[32] = '\n';
- sz = 33;
+ if (FLAGS_SET(f, ID128_FORMAT_PLAIN)) {
+ assert_se(sd_id128_to_string(id, buffer));
+ sz = SD_ID128_STRING_MAX;
} else {
- id128_to_uuid_string(id, buffer);
- buffer[36] = '\n';
- sz = 37;
+ assert_se(sd_id128_to_uuid_string(id, buffer));
+ sz = SD_ID128_UUID_STRING_MAX;
}
+ buffer[sz - 1] = '\n';
r = loop_write(fd, buffer, sz, false);
if (r < 0)
return r;
- if (do_sync) {
+ if (FLAGS_SET(f, ID128_SYNC_ON_WRITE)) {
r = fsync_full(fd);
if (r < 0)
return r;
return 0;
}
-int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) {
- _cleanup_close_ int fd = -1;
+int id128_write_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t id) {
+ _cleanup_close_ int fd = -EBADF;
+
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+ assert(path);
- fd = open(p, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, 0444);
+ fd = xopenat(dir_fd, path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, /* xopen_flags = */ 0, 0444);
if (fd < 0)
- return -errno;
+ return fd;
- return id128_write_fd(fd, f, id, do_sync);
+ return id128_write_fd(fd, f, id);
}
void id128_hash_func(const sd_id128_t *p, struct siphash *state) {
}
DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(id128_hash_ops_free, sd_id128_t, id128_hash_func, id128_compare_func, free);
int id128_get_product(sd_id128_t *ret) {
sd_id128_t uuid;
/* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is
* particularly relevant in VM environments, where VM managers typically place a VM uuid there. */
- r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid);
+ r = id128_read("/sys/class/dmi/id/product_uuid", ID128_FORMAT_UUID, &uuid);
if (r == -ENOENT)
- r = id128_read("/proc/device-tree/vm,uuid", ID128_UUID, &uuid);
+ r = id128_read("/proc/device-tree/vm,uuid", ID128_FORMAT_UUID, &uuid);
if (r < 0)
return r;