Skip to content

Commit 91c3e00

Browse files
authored
Merge pull request #8393 from romayalon/romy-copy-link-xattr-skip-fix
NC | Copy based on link/same inode overrides existing xattr
2 parents 4b1fdab + d875871 commit 91c3e00

File tree

2 files changed

+73
-5
lines changed

2 files changed

+73
-5
lines changed

src/sdk/namespace_fs.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,7 @@ class NamespaceFS {
12881288
copy_res = undefined, offset }) {
12891289
const part_upload = file_path === upload_path;
12901290
const same_inode = params.copy_source && copy_res === copy_status_enum.SAME_INODE;
1291+
const should_replace_xattr = params.copy_source ? copy_res === copy_status_enum.FALLBACK : true;
12911292
const is_dir_content = this._is_directory_content(file_path, params.key);
12921293

12931294
const stat = await target_file.stat(fs_context);
@@ -1324,7 +1325,7 @@ class NamespaceFS {
13241325
await this.append_to_migrate_wal(file_path);
13251326
}
13261327
}
1327-
if (fs_xattr && !is_dir_content) await target_file.replacexattr(fs_context, fs_xattr);
1328+
if (fs_xattr && !is_dir_content && should_replace_xattr) await target_file.replacexattr(fs_context, fs_xattr);
13281329
// fsync
13291330
if (config.NSFS_TRIGGER_FSYNC) await target_file.fsync(fs_context);
13301331
dbg.log1('NamespaceFS._finish_upload:', open_mode, file_path, upload_path, fs_xattr);
@@ -1334,6 +1335,7 @@ class NamespaceFS {
13341335
}
13351336

13361337
// when object is a dir, xattr are set on the folder itself and the content is in .folder file
1338+
// we still should put the xattr if copy is link/same inode because we put the xattr on the directory
13371339
if (is_dir_content) {
13381340
await this._assign_dir_content_to_xattr(fs_context, fs_xattr, { ...params, size: stat.size }, copy_xattr);
13391341
}

src/test/unit_tests/test_namespace_fs.js

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ const dir_content_type = 'application/x-directory';
3838
const stream_content_type = 'application/octet-stream';
3939

4040
const DEFAULT_FS_CONFIG = get_process_fs_context();
41+
const empty_data = crypto.randomBytes(0);
42+
const empty_stream = () => buffer_utils.buffer_to_read_stream(empty_data);
4143

4244
function make_dummy_object_sdk(config_root) {
4345
return {
@@ -1357,7 +1359,6 @@ mocha.describe('namespace_fs copy object', function() {
13571359
const copy_xattr = {};
13581360
const copy_key_1 = 'copy_key_1';
13591361
const data = crypto.randomBytes(100);
1360-
const empty_data = crypto.randomBytes(0);
13611362

13621363
mocha.before(async function() {
13631364
const upload_res = await ns_tmp.upload_object({
@@ -1497,7 +1498,7 @@ mocha.describe('namespace_fs copy object', function() {
14971498
res = await ns_tmp.upload_object({
14981499
bucket: upload_bkt,
14991500
key,
1500-
source_stream: buffer_utils.buffer_to_read_stream(empty_data),
1501+
source_stream: empty_stream(),
15011502
size: 0,
15021503
}, dummy_object_sdk);
15031504
xattr = await get_xattr(file_path);
@@ -1524,7 +1525,7 @@ mocha.describe('namespace_fs copy object', function() {
15241525
bucket: upload_bkt,
15251526
key: key,
15261527
xattr: md1,
1527-
source_stream: buffer_utils.buffer_to_read_stream(empty_data),
1528+
source_stream: empty_stream(),
15281529
size: 0
15291530
}, dummy_object_sdk);
15301531
const file_path = ns_tmp_bucket_path + '/' + key;
@@ -1652,7 +1653,7 @@ mocha.describe('namespace_fs copy object', function() {
16521653
await ns_tmp.upload_object({
16531654
bucket: upload_bkt,
16541655
key: src_key,
1655-
source_stream: buffer_utils.buffer_to_read_stream(empty_data),
1656+
source_stream: empty_stream(),
16561657
size: 0,
16571658
xattr: md1
16581659
}, dummy_object_sdk);
@@ -1686,7 +1687,72 @@ mocha.describe('namespace_fs copy object', function() {
16861687
assert.deepStrictEqual(xattr, { ...add_user_prefix(copy_read_md_res.xattr), [XATTR_DIR_CONTENT]: `${copy_read_md_res.size}` });
16871688
assert.deepStrictEqual(md1, copy_read_md_res.xattr);
16881689
assert.equal(dir_content_type, copy_read_md_res.content_type);
1690+
});
1691+
1692+
mocha.it(`copy object link - with new content type and xattr - should not change`, async function() {
1693+
const bucket = upload_bkt;
1694+
const src_key = 'obj_src1';
1695+
const dst_key = 'obj_dst_link_and_sc';
1696+
1697+
await ns_tmp.upload_object({ bucket, key: src_key, source_stream: empty_stream(), size: 0, xattr: md1 }, dummy_object_sdk);
1698+
const read_md_res = await ns_tmp.read_object_md({ bucket, key: src_key }, dummy_object_sdk);
1699+
1700+
assert.deepStrictEqual(md1, read_md_res.xattr);
1701+
assert.equal(stream_content_type, read_md_res.content_type);
1702+
const new_xattr = { 'copy_new_xattr_key': 'copy_new_xattr_val' };
1703+
1704+
const copy_source = { bucket, key: src_key };
1705+
await ns_tmp.upload_object({
1706+
bucket,
1707+
key: dst_key,
1708+
copy_source,
1709+
size: 0,
1710+
xattr_copy: true,
1711+
content_type: dir_content_type,
1712+
xattr: new_xattr
1713+
}, dummy_object_sdk);
16891714

1715+
const dst_md_res = await ns_tmp.read_object_md({ bucket, key: dst_key }, dummy_object_sdk);
1716+
const src_md_res = await ns_tmp.read_object_md({ bucket, key: src_key }, dummy_object_sdk);
1717+
1718+
// on link - content type and xattr should not be changed on src and dst
1719+
assert.deepStrictEqual(md1, src_md_res.xattr);
1720+
assert.equal(stream_content_type, src_md_res.content_type);
1721+
assert.deepStrictEqual(md1, dst_md_res.xattr);
1722+
assert.equal(stream_content_type, dst_md_res.content_type);
1723+
1724+
});
1725+
1726+
mocha.it(`copy object fallback - with new content type and xattr`, async function() {
1727+
const bucket = upload_bkt;
1728+
const src_key = 'obj_src1';
1729+
const dst_key = 'obj_dst_fallback_and_sc';
1730+
// force fallback copy using versioning enabled/suspended
1731+
ns_tmp.versioning = 'SUSPENDED';
1732+
await ns_tmp.upload_object({ bucket, key: src_key, source_stream: empty_stream(), size: 0, xattr: md1 }, dummy_object_sdk);
1733+
const read_md_res = await ns_tmp.read_object_md({ bucket, key: src_key }, dummy_object_sdk);
1734+
1735+
assert.deepStrictEqual(md1, read_md_res.xattr);
1736+
assert.equal(stream_content_type, read_md_res.content_type);
1737+
1738+
const copy_source = { bucket, key: src_key };
1739+
const new_xattr = { 'copy_new_xattr_key': 'copy_new_xattr_val' };
1740+
await ns_tmp.upload_object({ bucket, key: dst_key, copy_source: copy_source, size: 0,
1741+
xattr_copy: false,
1742+
content_type: dir_content_type,
1743+
xattr: new_xattr,
1744+
}, dummy_object_sdk);
1745+
1746+
const dst_md_res = await ns_tmp.read_object_md({ bucket, key: dst_key }, dummy_object_sdk);
1747+
const src_md_res = await ns_tmp.read_object_md({ bucket, key: src_key }, dummy_object_sdk);
1748+
1749+
// on fallback - content type and xattr should be changed on dst but not on src
1750+
new_xattr[s3_utils.XATTR_SORT_SYMBOL] = true;
1751+
assert.deepStrictEqual(md1, src_md_res.xattr);
1752+
assert.equal(stream_content_type, src_md_res.content_type);
1753+
assert.deepStrictEqual(new_xattr, dst_md_res.xattr);
1754+
assert.equal(dir_content_type, dst_md_res.content_type);
1755+
assert.deepStrictEqual(md1, read_md_res.xattr);
16901756
});
16911757

16921758
mocha.after(async function() {

0 commit comments

Comments
 (0)