Skip to content

Commit dcfb508

Browse files
committed
Linux: log tmpfile link as new file creation
WIP Signed-off-by: Pavel Snajdr <[email protected]>
1 parent e55225b commit dcfb508

File tree

8 files changed

+412
-12
lines changed

8 files changed

+412
-12
lines changed

include/sys/zfs_znode.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,8 +325,9 @@ extern void zfs_xvattr_set(znode_t *zp, xvattr_t *xvap, dmu_tx_t *tx);
325325
extern void zfs_upgrade(zfsvfs_t *zfsvfs, dmu_tx_t *tx);
326326
extern void zfs_log_setsaxattr(zilog_t *zilog, dmu_tx_t *tx, int txtype,
327327
znode_t *zp, const char *name, const void *value, size_t size);
328-
329328
extern void zfs_znode_update_vfs(struct znode *);
329+
extern void zfs_log_link_tmpfile(zilog_t *zilog, dmu_tx_t *tx,
330+
znode_t *dzp, znode_t *zp, const char *name);
330331

331332
#endif
332333
#ifdef __cplusplus

module/os/linux/zfs/zfs_vnops_os.c

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3645,16 +3645,11 @@ zfs_link(znode_t *tdzp, znode_t *szp, char *name, cred_t *cr,
36453645

36463646
if (error == 0) {
36473647
uint64_t txtype = TX_LINK;
3648-
/*
3649-
* tmpfile is created to be in z_unlinkedobj, so remove it.
3650-
* Also, we don't log in ZIL, because all previous file
3651-
* operation on the tmpfile are ignored by ZIL. Instead we
3652-
* always wait for txg to sync to make sure all previous
3653-
* operation are sync safe.
3654-
*/
36553648
if (is_tmpfile) {
3649+
/* remove from unlinked set */
36563650
VERIFY(zap_remove_int(zfsvfs->z_os,
36573651
zfsvfs->z_unlinkedobj, szp->z_id, tx) == 0);
3652+
zfs_log_link_tmpfile(zilog, tx, tdzp, szp, name);
36583653
} else {
36593654
if (flags & FIGNORECASE)
36603655
txtype |= TX_CI;
@@ -3669,12 +3664,12 @@ zfs_link(znode_t *tdzp, znode_t *szp, char *name, cred_t *cr,
36693664

36703665
zfs_dirent_unlock(dl);
36713666

3672-
if (!is_tmpfile && zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
3667+
if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
36733668
zil_commit(zilog, 0);
36743669

3675-
if (is_tmpfile && zfsvfs->z_os->os_sync != ZFS_SYNC_DISABLED)
3670+
if (is_tmpfile && !zfsvfs->z_xattr_sa &&
3671+
zfsvfs->z_os->os_sync != ZFS_SYNC_DISABLED)
36763672
txg_wait_synced(dmu_objset_pool(zfsvfs->z_os), txg);
3677-
36783673
zfs_znode_update_vfs(tdzp);
36793674
zfs_znode_update_vfs(szp);
36803675
zfs_exit(zfsvfs, FTAG);

module/zfs/zfs_log.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -935,5 +935,74 @@ zfs_log_clone_range(zilog_t *zilog, dmu_tx_t *tx, int txtype, znode_t *zp,
935935
}
936936
}
937937

938+
void
939+
zfs_log_link_tmpfile(zilog_t *zilog, dmu_tx_t *tx, znode_t *dzp, znode_t *zp,
940+
const char *name)
941+
{
942+
zfsvfs_t *zfsvfs = ZTOZSB(dzp);
943+
944+
if (zil_replaying(zilog, tx))
945+
return;
946+
947+
vattr_t va = { 0 };
948+
va.va_mask = ATTR_MODE | ATTR_UID | ATTR_GID;
949+
va.va_mode = zp->z_mode;
950+
va.va_uid = (uid_t)KUID_TO_SUID(ZTOUID(zp));
951+
va.va_gid = (gid_t)KGID_TO_SGID(ZTOGID(zp));
952+
953+
zfs_log_create(zilog, tx, TX_CREATE, dzp, zp, name, NULL, NULL, &va);
954+
955+
uint64_t size = zp->z_size;
956+
uint64_t off = 0;
957+
uint64_t max_chunk = (uint64_t)zfs_immediate_write_sz;
958+
959+
/* TODO: Reflect the file sync open flags here */
960+
boolean_t commit =
961+
(zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS);
962+
while (off < size) {
963+
uint64_t len = size - off;
964+
if (len > max_chunk)
965+
len = max_chunk;
966+
zfs_log_write(zilog, tx, TX_WRITE, zp, off, len, commit,
967+
B_FALSE, NULL, NULL);
968+
off += len;
969+
}
970+
971+
if (!zp->z_xattr_cached)
972+
return;
973+
974+
/* Haven't thought about xattr=dir tbh, is there a way to log those? */
975+
ASSERT3P(zfsvfs->z_xattr_sa, !=, NULL);
976+
977+
nvpair_t *nvp = NULL;
978+
nvlist_t *nvl = zp->z_xattr_cached;
979+
980+
while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
981+
const char *xname = nvpair_name(nvp);
982+
const void *val = NULL;
983+
size_t vlen = 0;
984+
985+
if (nvpair_type(nvp) == DATA_TYPE_STRING) {
986+
const char *s;
987+
VERIFY0(nvpair_value_string(nvp, &s));
988+
val = s;
989+
vlen = strlen(s);
990+
} else if (nvpair_type(nvp) == DATA_TYPE_BYTE_ARRAY) {
991+
const uchar_t *buf;
992+
uint_t n;
993+
VERIFY0(nvpair_value_byte_array(nvp,
994+
(uchar_t **)&buf, &n));
995+
val = buf;
996+
vlen = n;
997+
} else
998+
continue;
999+
1000+
1001+
zfs_log_setsaxattr(zilog, tx, TX_SETSAXATTR,
1002+
zp, xname, val, vlen);
1003+
}
1004+
}
1005+
1006+
9381007
ZFS_MODULE_PARAM(zfs, zfs_, immediate_write_sz, S64, ZMOD_RW,
9391008
"Largest data block to write to zil");

tests/runfiles/linux.run

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ tags = ['functional', 'snapshot']
216216

217217
[tests/functional/tmpfile:Linux]
218218
tests = ['tmpfile_001_pos', 'tmpfile_002_pos', 'tmpfile_003_pos',
219-
'tmpfile_stat_mode']
219+
'tmpfile_004_pos', 'tmpfile_stat_mode']
220220
tags = ['functional', 'tmpfile']
221221

222222
[tests/functional/upgrade:Linux]

tests/zfs-tests/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ scripts_zfs_tests_functional_tmpfile_PROGRAMS = \
2525
%D%/tests/functional/tmpfile/tmpfile_001_pos \
2626
%D%/tests/functional/tmpfile/tmpfile_002_pos \
2727
%D%/tests/functional/tmpfile/tmpfile_003_pos \
28+
%D%/tests/functional/tmpfile/tmpfile_replay \
2829
%D%/tests/functional/tmpfile/tmpfile_stat_mode \
2930
%D%/tests/functional/tmpfile/tmpfile_test
3031
endif

tests/zfs-tests/tests/functional/tmpfile/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
/tmpfile_001_pos
33
/tmpfile_002_pos
44
/tmpfile_003_pos
5+
/tmpfile_replay
56
/tmpfile_stat_mode
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/bin/ksh -p
2+
# SPDX-License-Identifier: CDDL-1.0
3+
4+
. "$STF_SUITE/include/libtest.shlib"
5+
6+
if [ "$(uname -s)" != "Linux" ]; then
7+
log_skip "O_TMPFILE is not supported on this platform."
8+
fi
9+
10+
verify_runnable "global"
11+
12+
log_assert "O_TMPFILE linking with xattr=sa should survive replay"
13+
14+
log_must zfs set xattr=sa "$TESTPOOL/$TESTFS"
15+
16+
typeset target_uid target_gid
17+
if [ "$(id -u)" -eq 0 ]; then
18+
target_uid=1000
19+
target_gid=1001
20+
else
21+
target_uid=$(id -u)
22+
target_gid=$(id -g)
23+
fi
24+
25+
log_must test -n "$target_uid" -a -n "$target_gid"
26+
27+
export TARGET_UID="$target_uid"
28+
export TARGET_GID="$target_gid"
29+
export TESTFILE="tmpfile_replay.$$"
30+
31+
for sync_mode in standard always; do
32+
log_note "Running O_TMPFILE crash-replay test with sync=$sync_mode"
33+
log_must zfs set sync="$sync_mode" "$TESTPOOL/$TESTFS"
34+
log_must "$STF_SUITE/tests/functional/tmpfile/tmpfile_replay"
35+
done
36+
37+
log_pass "Crash-replay of O_TMPFILE linking (xattr=sa) verified."

0 commit comments

Comments
 (0)