summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Walters <walters@verbum.org>2017-01-10 22:04:51 -0500
committerColin Walters <walters@verbum.org>2017-01-29 03:23:43 -0500
commitafd178fb521a7373930dc8973b115c7ff62f6ee0 (patch)
treea58e06bcffca8ce73e06d50a2f78438da1f4276f
parent6bf55255e8a688997973d9d40755fa999eda3791 (diff)
downloadlibglnx-afd178fb521a7373930dc8973b115c7ff62f6ee0.tar.gz
xattrs: Handle xattrs changing size concurrently
We should be robust in the face of this and return a snapshot of the current value we saw, not transiently fail. This is the semantics we expect with ostree upgrades for `/etc` for example.
-rw-r--r--glnx-xattrs.c29
1 files changed, 18 insertions, 11 deletions
diff --git a/glnx-xattrs.c b/glnx-xattrs.c
index d50b3c2..e535b18 100644
--- a/glnx-xattrs.c
+++ b/glnx-xattrs.c
@@ -83,19 +83,22 @@ read_xattr_name_array (const char *path,
funcstr = fd != -1 ? "fgetxattr" : "lgetxattr";
- p = xattrs;
- while (p < xattrs+len)
+ for (p = xattrs; p < xattrs+len; p = p + strlen (p) + 1)
{
ssize_t bytes_read;
- char *buf;
- GBytes *bytes = NULL;
+ g_autofree char *buf = NULL;
+ g_autoptr(GBytes) bytes = NULL;
+ again:
if (fd != -1)
bytes_read = fgetxattr (fd, p, NULL, 0);
else
bytes_read = lgetxattr (path, p, NULL, 0);
if (bytes_read < 0)
{
+ if (errno == ENODATA)
+ continue;
+
glnx_set_prefix_error_from_errno (error, "%s", funcstr);
goto out;
}
@@ -103,26 +106,30 @@ read_xattr_name_array (const char *path,
continue;
buf = g_malloc (bytes_read);
- bytes = g_bytes_new_take (buf, bytes_read);
if (fd != -1)
r = fgetxattr (fd, p, buf, bytes_read);
else
r = lgetxattr (path, p, buf, bytes_read);
if (r < 0)
{
- g_bytes_unref (bytes);
+ if (errno == ERANGE)
+ {
+ g_free (g_steal_pointer (&buf));
+ goto again;
+ }
+ else if (errno == ENODATA)
+ continue;
+
glnx_set_prefix_error_from_errno (error, "%s", funcstr);
goto out;
}
-
+
+ bytes = g_bytes_new_take (g_steal_pointer (&buf), bytes_read);
g_variant_builder_add (builder, "(@ay@ay)",
g_variant_new_bytestring (p),
variant_new_ay_bytes (bytes));
-
- p = p + strlen (p) + 1;
- g_bytes_unref (bytes);
}
-
+
ret = TRUE;
out:
return ret;