From: Sachin Sant <sachinp@linux.ibm.com>
To: ltp@lists.linux.it
Subject: [LTP] [PATCH v8 7/8] fs/acl: Add extended attributes test
Date: Sat, 13 Jun 2026 14:35:42 +0530 [thread overview]
Message-ID: <20260613090543.78643-8-sachinp@linux.ibm.com> (raw)
In-Reply-To: <20260613090543.78643-1-sachinp@linux.ibm.com>
Add xattr_test01 to validate extended attribute operations on
filesystems.
The test covers:
- Setting and retrieving extended attributes on files and directories
- Removing extended attributes and verifying removal
- Backing up extended attributes to a file
- Restoring extended attributes from backup
- Verifying restored attributes match original values
The test runs on ext2/ext3/ext4 (with user_xattr mount option),
xfs, and btrfs filesystems. It properly handles EOPNOTSUPP for
filesystems without extended attribute support.
Suggested-by: Cyril Hrubis <chrubis@suse.cz>
Signed-off-by: Sachin Sant <sachinp@linux.ibm.com>
---
V8 changes:
- Use reset_test_path() instead of chown variant.
- v7 link https://lore.kernel.org/ltp/20260612171930.11964-1-sachinp@linux.ibm.com/T/#t
V7 changes:
- No change
V6 changes:
- Added HAVE_SYS_XATTR_H guard
- Switched from TST_EXP_PASS_SILENT to TEST() with TTERRNO for better
diagnostics
- Updated algorithm documentation with correct format
- Removed unnecessary .forks_child flag
- v5 link https://lore.kernel.org/ltp/20260608092200.92827-1-sachinp@linux.ibm.com/T/#t
V5 changes:
- Switch to kernel only test validation to remove dependency on libacl
and useradd/del commands.
- v3 link https://lore.kernel.org/ltp/20260604065417.25924-1-sachinp@linux.ibm.com/T/#t
V4 changes:
- No change
V3 changes:
- Updated copyright header as per LTP format.
- Updated commit message as per review comments.
- v2 link https://lore.kernel.org/ltp/20260603065744.47106-1-sachinp@linux.ibm.com/T/#t
V2 changes:
- Use reset_test_path_no_chown variant to skip chown step
and removed needs_cmd tag to avoid useradd/userdel dependency
- v1 link https://lore.kernel.org/ltp/20260602121958.27494-1-sachinp@linux.ibm.com/T/#t
V1 changes:
- Use HAVE_LIBACL guards in .c code
- Report TCONF when libacl is not available
- rfc link https://lore.kernel.org/ltp/477836fd-80c8-4168-bfe6-00b374bb2534@linux.ibm.com/T/#t
---
runtest/fs | 1 +
testcases/kernel/fs/acl/.gitignore | 1 +
testcases/kernel/fs/acl/xattr_test01.c | 383 +++++++++++++++++++++++++
3 files changed, 385 insertions(+)
create mode 100644 testcases/kernel/fs/acl/xattr_test01.c
diff --git a/runtest/fs b/runtest/fs
index 64deb56e6..f9acac387 100644
--- a/runtest/fs
+++ b/runtest/fs
@@ -95,3 +95,4 @@ acl_other01 acl_other01
acl_inherit01 acl_inherit01
acl_file_ops01 acl_file_ops01
acl_link01 acl_link01
+xattr_test01 xattr_test01
diff --git a/testcases/kernel/fs/acl/.gitignore b/testcases/kernel/fs/acl/.gitignore
index 4a071d516..62ccd0457 100644
--- a/testcases/kernel/fs/acl/.gitignore
+++ b/testcases/kernel/fs/acl/.gitignore
@@ -4,3 +4,4 @@
/acl_inherit01
/acl_file_ops01
/acl_link01
+/xattr_test01
diff --git a/testcases/kernel/fs/acl/xattr_test01.c b/testcases/kernel/fs/acl/xattr_test01.c
new file mode 100644
index 000000000..cf4ab3d30
--- /dev/null
+++ b/testcases/kernel/fs/acl/xattr_test01.c
@@ -0,0 +1,383 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 IBM
+ *
+ * Original shell test by Kai Zhao (ltcd3@cn.ibm.com)
+ * Converted to C by Sachin Sant <sachinp@linux.ibm.com>
+ */
+
+/*\
+ * Test Extended Attributes (xattr).
+ *
+ * Some filesystems require explicit user_xattr mount options,
+ * while others (e.g. xfs and btrfs) provide these features without
+ * mount options.
+ *
+ * This test validates:
+ * - Extended attributes set/get/remove operations
+ * - Extended attributes backup and restore operations
+ *
+ * [Algorithm]
+ *
+ * - Set extended attributes on directories and files
+ * - Verify attributes can be retrieved correctly
+ * - Test attribute removal
+ * - Create backup of extended attributes to a file
+ * - Remove original attributes
+ * - Restore attributes from backup file
+ * - Verify restored attributes match original values
+ */
+
+#include "acl_lib.h"
+
+#ifdef HAVE_SYS_XATTR_H
+
+/*
+ * Test extended attributes.
+ * Set, get, and remove extended attributes on files and directories.
+ */
+static void test_xattr(void)
+{
+ char value[256];
+ ssize_t size;
+ int fd = -1;
+ int file_created = 0;
+
+ tst_res(TINFO, "Testing extended attributes");
+ reset_test_path();
+
+ TEST(setxattr(TESTDIR, XATTR_TEST_DIR_NAME, XATTR_TEST_DIR_VALUE,
+ XATTR_TEST_DIR_SIZE, 0));
+ if (TST_RET == -1) {
+ if (TST_ERR == EOPNOTSUPP) {
+ tst_res(TCONF, "Extended attributes not supported");
+ return;
+ }
+ tst_res(TFAIL | TTERRNO, "setxattr on directory failed");
+ return;
+ }
+
+ size = getxattr(TESTDIR, XATTR_TEST_DIR_NAME, value, sizeof(value));
+ if (size < 0) {
+ tst_res(TFAIL | TERRNO, "getxattr on directory failed");
+ goto cleanup_dir_xattr;
+ }
+
+ if (size != XATTR_TEST_DIR_SIZE ||
+ memcmp(value, XATTR_TEST_DIR_VALUE, XATTR_TEST_DIR_SIZE) != 0) {
+ tst_res(TFAIL, "getxattr returned wrong directory value");
+ goto cleanup_dir_xattr;
+ }
+
+ fd = SAFE_OPEN(TESTFILE, O_CREAT | O_WRONLY, 0644);
+ SAFE_CLOSE(fd);
+ file_created = 1;
+
+ TEST(setxattr(TESTFILE, XATTR_TEST_FILE_NAME, XATTR_TEST_FILE_VALUE,
+ XATTR_TEST_FILE_SIZE, 0));
+ if (TST_RET == -1) {
+ if (TST_ERR == EOPNOTSUPP) {
+ tst_res(TCONF, "Extended attributes not supported");
+ goto cleanup_file_and_dir;
+ }
+ tst_res(TFAIL | TTERRNO, "setxattr on file failed");
+ goto cleanup_file_and_dir;
+ }
+
+ size = getxattr(TESTFILE, XATTR_TEST_FILE_NAME, value,
+ sizeof(value));
+ if (size < 0) {
+ tst_res(TFAIL | TERRNO, "getxattr on file failed");
+ goto cleanup_file_and_dir;
+ }
+
+ if (size != XATTR_TEST_FILE_SIZE ||
+ memcmp(value, XATTR_TEST_FILE_VALUE, XATTR_TEST_FILE_SIZE) != 0) {
+ tst_res(TFAIL, "getxattr returned wrong file value");
+ goto cleanup_file_and_dir;
+ }
+
+ if (removexattr(TESTFILE, XATTR_TEST_FILE_NAME) == -1) {
+ tst_res(TFAIL | TERRNO, "removexattr failed");
+ goto cleanup_file_and_dir;
+ }
+
+ size = getxattr(TESTFILE, XATTR_TEST_FILE_NAME, value,
+ sizeof(value));
+ if (size >= 0 || errno != ENODATA) {
+ tst_res(TFAIL, "getxattr after removal should fail with ENODATA");
+ goto cleanup_file_and_dir;
+ }
+
+ tst_res(TPASS, "Extended attributes work correctly");
+
+cleanup_file_and_dir:
+ if (file_created)
+ cleanup_testfile();
+cleanup_dir_xattr:
+ if (removexattr(TESTDIR, XATTR_TEST_DIR_NAME) == -1 &&
+ errno != ENODATA)
+ tst_res(TWARN | TERRNO, "removexattr failed");
+}
+
+#define XATTR_BACKUP_TEST_COUNT 2
+
+/*
+ * Helper function to cleanup test xattrs.
+ */
+static inline void cleanup_test_xattrs(void)
+{
+ if (removexattr(TESTFILE, XATTR_TEST1_NAME) == -1 &&
+ errno != ENODATA)
+ tst_res(TWARN | TERRNO, "removexattr XATTR_TEST1_NAME failed");
+ if (removexattr(TESTFILE, XATTR_TEST2_NAME) == -1 &&
+ errno != ENODATA)
+ tst_res(TWARN | TERRNO, "removexattr XATTR_TEST2_NAME failed");
+}
+
+/*
+ * Test extended attributes backup and restore.
+ * Extended attributes should be preserved through backup/restore.
+ */
+static void test_xattr_backup_restore(void)
+{
+ char value[256];
+ char line[512];
+ char attr_name[256];
+ char attr_value[256];
+ char list[512];
+ ssize_t size, list_size;
+ size_t name_len, value_len;
+ int fd = -1;
+ FILE *fp = NULL;
+ int restored_count = 0;
+ int backup_created = 0;
+ char *attr_ptr;
+
+ tst_res(TINFO, "Testing extended attributes backup and restore");
+ reset_test_path();
+
+ fd = SAFE_OPEN(TESTFILE, O_CREAT | O_WRONLY, 0644);
+ SAFE_CLOSE(fd);
+
+ TEST(setxattr(TESTFILE, XATTR_TEST1_NAME, XATTR_TEST1_VALUE,
+ XATTR_TEST1_SIZE, 0));
+ if (TST_RET == -1) {
+ cleanup_testfile();
+ if (TST_ERR == EOPNOTSUPP) {
+ tst_res(TCONF, "Extended attributes not supported");
+ return;
+ }
+ tst_res(TFAIL | TTERRNO, "setxattr failed");
+ return;
+ }
+
+ TEST(setxattr(TESTFILE, XATTR_TEST2_NAME, XATTR_TEST2_VALUE,
+ XATTR_TEST2_SIZE, 0));
+ if (TST_RET == -1) {
+ tst_res(TFAIL | TTERRNO, "setxattr failed");
+ goto cleanup_xattrs;
+ }
+
+ /* Create genuine backup by reading xattrs from filesystem */
+ fp = SAFE_FOPEN(XATTR_BACKUP_FILE, "w");
+ backup_created = 1;
+
+ if (fprintf(fp, "# file: %s\n", TESTFILE) < 0) {
+ tst_res(TFAIL | TERRNO, "fprintf failed");
+ goto cleanup_backup;
+ }
+
+ /* List all extended attributes */
+ list_size = listxattr(TESTFILE, list, sizeof(list));
+ if (list_size < 0) {
+ tst_res(TFAIL | TERRNO, "listxattr failed");
+ goto cleanup_backup;
+ }
+
+ /* Iterate through attribute list and backup each one */
+ attr_ptr = list;
+ while (attr_ptr < list + list_size) {
+ /* Only backup user.* attributes for this test */
+ if (strncmp(attr_ptr, "user.", 5) != 0) {
+ attr_ptr += strlen(attr_ptr) + 1;
+ continue;
+ }
+
+ /* Get attribute value from filesystem */
+ size = getxattr(TESTFILE, attr_ptr, value, sizeof(value));
+ if (size < 0) {
+ tst_res(TFAIL | TERRNO, "getxattr failed for %s",
+ attr_ptr);
+ goto cleanup_backup;
+ }
+
+ /* Write to backup file with null terminator for string values */
+ if (fprintf(fp, "%s=\"%.*s\"\n", attr_ptr, (int)size,
+ value) < 0) {
+ tst_res(TFAIL | TERRNO, "fprintf failed");
+ goto cleanup_backup;
+ }
+
+ /* Move to next attribute name in list */
+ attr_ptr += strlen(attr_ptr) + 1;
+ }
+
+ SAFE_FCLOSE(fp);
+ fp = NULL;
+
+ /* Remove xattrs to simulate loss */
+ cleanup_test_xattrs();
+
+ /* Restore from backup file */
+ fp = SAFE_FOPEN(XATTR_BACKUP_FILE, "r");
+ while (fgets(line, sizeof(line), fp)) {
+ char *p, *q;
+
+ if (line[0] == '#' || line[0] == '\n')
+ continue;
+
+ /* Parse: attr_name="attr_value" */
+ p = strchr(line, '=');
+ if (!p)
+ continue;
+
+ /* Safe copy of attribute name with length check */
+ name_len = p - line;
+
+ if (name_len >= sizeof(attr_name)) {
+ tst_res(TWARN, "Attribute name too long, skipping");
+ continue;
+ }
+ memcpy(attr_name, line, name_len);
+ attr_name[name_len] = '\0';
+
+ /* Only restore user.* attributes for this test */
+ if (strncmp(attr_name, "user.", 5) != 0)
+ continue;
+
+ p++;
+ if (*p != '"')
+ continue;
+ p++;
+
+ q = strchr(p, '"');
+ if (!q)
+ continue;
+
+ /* Safe copy of attribute value with length check */
+ value_len = q - p;
+
+ if (value_len >= sizeof(attr_value)) {
+ tst_res(TWARN, "Attribute value too long, skipping");
+ continue;
+ }
+ memcpy(attr_value, p, value_len);
+ attr_value[value_len] = '\0';
+
+ /* Restore the xattr */
+ if (setxattr(TESTFILE, attr_name, attr_value, value_len,
+ 0) == -1) {
+ tst_res(TFAIL | TERRNO,
+ "setxattr restore failed for %s", attr_name);
+ goto cleanup_backup;
+ }
+ restored_count++;
+ }
+ SAFE_FCLOSE(fp);
+ fp = NULL;
+
+ if (restored_count != XATTR_BACKUP_TEST_COUNT) {
+ tst_res(TFAIL, "Expected %d xattrs restored, got %d",
+ XATTR_BACKUP_TEST_COUNT, restored_count);
+ goto cleanup_backup;
+ }
+
+ /* Verify restored xattrs */
+ size = getxattr(TESTFILE, XATTR_TEST1_NAME, value, sizeof(value));
+ if (size < 0) {
+ tst_res(TFAIL | TERRNO, "getxattr failed for %s",
+ XATTR_TEST1_NAME);
+ goto cleanup_backup;
+ }
+
+ if (size != XATTR_TEST1_SIZE ||
+ memcmp(value, XATTR_TEST1_VALUE, XATTR_TEST1_SIZE) != 0) {
+ tst_res(TFAIL,
+ "Extended attribute %s restore verification failed",
+ XATTR_TEST1_NAME);
+ goto cleanup_backup;
+ }
+
+ size = getxattr(TESTFILE, XATTR_TEST2_NAME, value, sizeof(value));
+ if (size < 0) {
+ tst_res(TFAIL | TERRNO, "getxattr failed for %s",
+ XATTR_TEST2_NAME);
+ goto cleanup_backup;
+ }
+
+ if (size != XATTR_TEST2_SIZE ||
+ memcmp(value, XATTR_TEST2_VALUE, XATTR_TEST2_SIZE) != 0) {
+ tst_res(TFAIL,
+ "Extended attribute %s restore verification failed",
+ XATTR_TEST2_NAME);
+ goto cleanup_backup;
+ }
+
+ tst_res(TPASS, "Extended attributes backup/restore work correctly");
+
+cleanup_backup:
+ if (fp)
+ SAFE_FCLOSE(fp);
+ if (backup_created) {
+ if (unlink(XATTR_BACKUP_FILE) == -1)
+ tst_res(TWARN | TERRNO, "unlink backup file failed");
+ }
+cleanup_xattrs:
+ cleanup_test_xattrs();
+ cleanup_testfile();
+}
+
+static void setup(void)
+{
+ reset_test_path();
+}
+
+static void cleanup(void)
+{
+ cleanup_test_paths();
+}
+
+static void run(unsigned int n)
+{
+ switch (n) {
+ case 0:
+ test_xattr();
+ break;
+ case 1:
+ test_xattr_backup_restore();
+ break;
+ }
+}
+
+static struct tst_test test = {
+ .test = run,
+ .tcnt = 2,
+ .setup = setup,
+ .cleanup = cleanup,
+ .needs_root = 1,
+ .mount_device = 1,
+ .mntpoint = MNTPOINT,
+ .filesystems = (struct tst_fs[]) {
+ {.type = "ext2", .mnt_data = "user_xattr"},
+ {.type = "ext3", .mnt_data = "user_xattr"},
+ {.type = "ext4", .mnt_data = "user_xattr"},
+ {.type = "xfs"},
+ {.type = "btrfs"},
+ {}
+ }
+};
+
+#else
+ TST_TEST_TCONF("sys/xattr.h is not available");
+#endif
--
2.39.1
--
Mailing list info: https://lists.linux.it/listinfo/ltp
next prev parent reply other threads:[~2026-06-13 9:07 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-13 9:05 [LTP] [PATCH v8 0/8] Convert shell-based ACL test (tacl_xattr.sh) to C Sachin Sant
2026-06-13 9:05 ` [LTP] [PATCH v8 1/8] fs/acl: Add ACL_USER_OBJ permission test Sachin Sant
2026-06-13 10:36 ` [LTP] " linuxtestproject.agent
2026-06-13 9:05 ` [LTP] [PATCH v8 2/8] fs/acl: Add ACL mask interaction tests Sachin Sant
2026-06-13 9:05 ` [LTP] [PATCH v8 3/8] fs/acl: Add ACL_OTHER permission test Sachin Sant
2026-06-13 9:05 ` [LTP] [PATCH v8 4/8] fs/acl: Add default ACL inheritance test Sachin Sant
2026-06-13 9:05 ` [LTP] [PATCH v8 5/8] fs/acl: Add chmod/chown ACL interaction tests Sachin Sant
2026-06-13 9:05 ` [LTP] [PATCH v8 6/8] fs/acl: Add ACL symlink operations test Sachin Sant
2026-06-13 9:05 ` Sachin Sant [this message]
2026-06-13 9:05 ` [LTP] [PATCH v8 8/8] fs/acl: Remove old shell-based ACL test Sachin Sant
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260613090543.78643-8-sachinp@linux.ibm.com \
--to=sachinp@linux.ibm.com \
--cc=ltp@lists.linux.it \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.