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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox