Skip to content

Commit b922ee8

Browse files
author
yongqingliu
committed
support ftruncate file
1 parent 8eeea3d commit b922ee8

File tree

7 files changed

+334
-20
lines changed

7 files changed

+334
-20
lines changed

src/fdcache.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,6 +1413,7 @@ int FdEntity::RowFlush(const char* tpath, bool force_sync)
14131413
}
14141414

14151415
}else{
1416+
S3FS_PRN_INFO3("[path=%s][fd=%d] already being upload", path.c_str(), fd);
14161417
// upload rest data
14171418
if(0 < mp_size){
14181419
if(0 != (result = NoCacheMultipartPost(fd, mp_start, mp_size))){
@@ -1591,6 +1592,42 @@ ssize_t FdEntity::Write(const char* bytes, off_t start, size_t size)
15911592
return wsize;
15921593
}
15931594

1595+
int FdEntity::GetRefCount() {
1596+
AutoLock auto_lock(&fdent_lock);
1597+
return refcnt;
1598+
}
1599+
1600+
// this function only truncate file size in local, not flush to cos
1601+
// the refcnt must bigger than 1, in order to flush the truncate result later
1602+
// the flush trigger after user call close
1603+
int FdEntity::Ftruncate(ssize_t size) {
1604+
AutoLock auto_lock(&fdent_lock);
1605+
// if size is equal, do nothing
1606+
if (static_cast<size_t>(size) == pagelist.Size()) {
1607+
return 0;
1608+
}
1609+
if (refcnt <= 1) {
1610+
S3FS_PRN_ERR("failed to truncate file %s, refcnt(%d)", path.c_str(), refcnt);
1611+
return -EIO;
1612+
}
1613+
// file being upload, this is due to space not enough, we not support truncate in this situation
1614+
if (!upload_id.empty()) {
1615+
S3FS_PRN_ERR("failed to truncate file %s, because not enough space.", path.c_str());
1616+
return -ENOSPC;
1617+
}
1618+
// truncate temporary file size
1619+
if(-1 == ftruncate(fd, size) || -1 == fsync(fd)){
1620+
S3FS_PRN_ERR("failed to truncate temporary file %s by errno(%d).", path.c_str(), errno);
1621+
return -errno;
1622+
}
1623+
if(!is_modify){
1624+
is_modify = true;
1625+
}
1626+
// resize pagelist
1627+
pagelist.Resize(static_cast<size_t>(size), false);
1628+
return 0;
1629+
}
1630+
15941631

15951632
// [NOTE]
15961633
// Returns true if merged to orgmeta.
@@ -1649,6 +1686,7 @@ int FdEntity::UploadPendingMeta()
16491686

16501687
return result;
16511688
}
1689+
16521690
//------------------------------------------------
16531691
// FdManager symbol
16541692
//------------------------------------------------

src/fdcache.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ class FdEntity
178178

179179
ssize_t Read(char* bytes, off_t start, size_t size, bool force_load = false);
180180
ssize_t Write(const char* bytes, off_t start, size_t size);
181+
int GetRefCount();
182+
int Ftruncate(ssize_t size);
181183
};
182184
typedef std::map<std::string, class FdEntity*> fdent_map_t; // key=path, value=FdEntity*
183185

src/s3fs.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2093,7 +2093,16 @@ static int s3fs_truncate(const char* path, off_t size)
20932093
FdManager::get()->Close(ent);
20942094
return result;
20952095
}
2096-
2096+
// the file already opened for write, for exmaple open->ftruncate
2097+
// in this case we truncate in local disk, the flush will be delay to nearest flush
2098+
if (ent->GetRefCount() > 1) {
2099+
S3FS_PRN_DBG("[path=%s] already opened for writing, truncate it in local", path);
2100+
result = ent->Ftruncate(size);
2101+
FdManager::get()->Close(ent);
2102+
return result;
2103+
}
2104+
// the file not writing for others, we can safe flush to cos
2105+
S3FS_PRN_DBG("[path=%s] not being written, ready flush to cos", path);
20972106
}else{
20982107
// Not found -> Make tmpfile(with size)
20992108

test/cos.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
cos:
2+
base:
3+
secretid: xxxxx
4+
secretkey: yyyyy
5+
sessiontoken: zzzz
6+
protocol: https
7+
buckets:
8+
- name: ""
9+
alias: ""
10+
region: ""
11+
endpoint: ""

test/cos_utils.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22
from qcloud_cos import CosS3Client
33
import sys
44
import logging
5-
import hashlib
65
import crcmod
76
import os
7+
import subprocess
88

99
logging.basicConfig(level=logging.ERROR, stream=sys.stdout)
1010

1111
cos_client = None
12+
run_dir = ""
1213

1314

14-
def init_cos_client(region, bucket_name):
15+
def init_cos_client(region, bucket_name, mnt_point):
1516
global cos_client
17+
global run_dir
1618
secret_id = ''
1719
secret_key = ''
1820
with open('/etc/passwd-cosfs') as fl:
@@ -21,12 +23,15 @@ def init_cos_client(region, bucket_name):
2123
parts = line.split(":")
2224
if len(parts) < 3:
2325
raise ValueError("unexpected secret info in /etc/passwd-cosfs")
24-
secret_id = parts[1]
25-
secret_key = parts[2]
26+
secret_id = parts[1].strip()
27+
secret_key = parts[2].strip()
2628
if len(region) == 0 or len(secret_id) == 0 or len(secret_key) == 0:
2729
raise ValueError("Unexpected region or secret_id or secret_key")
30+
print("init cos client")
2831
config = CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key, Token=None, Scheme='http')
2932
cos_client = CosS3Client(config)
33+
run_dir = os.path.dirname(os.path.normpath(mnt_point))
34+
print("run_dir:" + run_dir)
3035

3136

3237
def get_cos_file_crc64(bucket, key):
@@ -36,13 +41,34 @@ def get_cos_file_crc64(bucket, key):
3641
return str(c64(body))
3742

3843

44+
def get_local_file_crc64(file_path):
45+
result = subprocess.run([os.path.join(run_dir, "coscli-linux"), '-c',
46+
os.path.join(run_dir, 'test/cos.yaml'), 'hash', file_path],
47+
stdout=subprocess.PIPE)
48+
return str(result.stdout).split(':')[-1].strip("'n\\ ")
49+
50+
3951
def verify_file_checksum_length_with_local(bucket, key, local_file_path):
4052
object_attr = cos_client.head_object(Bucket=bucket, Key=key)
41-
c64 = crcmod.mkCrcFun(0x142F0E1EBA9EA3693, initCrc=0, xorOut=0xffffffffffffffff, rev=True)
53+
test_crc64 = crcmod.Crc(0x142F0E1EBA9EA3693, initCrc=0, xorOut=0xffffffffffffffff, rev=True)
54+
# c64 = crcmod.mkCrcFun(0x142F0E1EBA9EA3693, initCrc=0, xorOut=0xffffffffffffffff, rev=True)
4255
cos_len = int(object_attr['Content-Length'])
4356
cos_crc64 = object_attr['x-cos-hash-crc64ecma']
4457
local_len = os.stat(local_file_path).st_size
45-
local_crc64 =str(c64(open(local_file_path, 'rb').read()))
58+
# read_bytes = 0
59+
# buffer_size = 128 * 1024
60+
# with open(local_file_path, 'rb') as fl:
61+
# byte_array = fl.read(buffer_size)
62+
# read_bytes = read_bytes + len(byte_array)
63+
# while len(byte_array) > 0:
64+
# test_crc64.update(byte_array)
65+
# byte_array = fl.read(buffer_size)
66+
# read_bytes = read_bytes + len(byte_array)
67+
# if read_bytes % (100 * 1024 * 1024) == 0:
68+
# print('read bytes ' + str(read_bytes))
69+
local_crc64 = get_local_file_crc64(local_file_path)
70+
# local_crc64 = str(int(test_crc64.hexdigest(), 16))
71+
# local_crc64 = str(c64(open(local_file_path, 'rb').read()))
4672
if local_len > 0 and cos_crc64 == '':
4773
cos_crc64 = get_cos_file_crc64(bucket, key)
4874
print('key' + key + " cos crc64:" + cos_crc64)
@@ -63,4 +89,4 @@ def verify_file_checksum_length(bucket, key, local_len, local_crc64):
6389
if cos_len != local_len or (cos_len != 0 and cos_crc64 != local_crc64):
6490
raise ValueError('file diff, local len:' + str(local_len) +
6591
', crc64:' + local_crc64 + '; cos file:' + key + ', len:' + str(cos_len) +
66-
', crc64:' + cos_crc64)
92+
', crc64:' + cos_crc64)

test/integration-test-main.sh

100755100644
Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/bin/bash
22
#Usage:
33
#1. Mount bucket: cosfs test-12500000 mnt_dir -ourl=http://cos.ap-shanghai.myqcloud.com -odbglevel=dbg -ocurldbg -oallow_other
4-
#2. Run the script with mount point parameter: ./integration-test-main.sh mnt_dir
4+
#2. Run the script with mount point parameter:bash test/integration-test-main.sh /root/cosfs-yongqing/mnt/ ap-guangzhou test-125000000
55
#3. If all test cases pass, "All tests complete" will be output, otherwise the corresponding error message will be output.
66

77
set -o xtrace
@@ -21,6 +21,8 @@ BIG_FILE=big-file-s3fs.txt
2121
BIG_FILE_LENGTH=$((25 * 1024 * 1024))
2222
CUR_DIR=`pwd`
2323
TEST_BUCKET_MOUNT_POINT_1=$1
24+
TEST_BUCKET_REGION=$2
25+
TEST_BUCKET_NAME=$3
2426

2527
function mk_test_file {
2628
if [ $# == 0 ]; then
@@ -120,7 +122,7 @@ function test_truncate_file {
120122
function test_truncate_empty_file {
121123
echo "Testing truncate empty file ..."
122124
# Write an empty test file
123-
touch ${TEST_TEXT_FILE}
125+
echo "data" > ${TEST_TEXT_FILE}
124126

125127
# Truncate the file to 1024 length
126128
t_size=1024
@@ -133,6 +135,30 @@ function test_truncate_empty_file {
133135
echo "error: expected ${TEST_TEXT_FILE} to be $t_size length, got $size"
134136
exit 1
135137
fi
138+
first_chars=`head -c 4 ${TEST_TEXT_FILE}`
139+
if [ "$first_chars" != "data" ]
140+
then
141+
echo "error: expected first 4 byte is 'data' got $first_chars"
142+
exit 1
143+
fi
144+
# Truncate the file to 1024 length
145+
t_size=3
146+
truncate ${TEST_TEXT_FILE} -s $t_size
147+
148+
# Verify file is zero length
149+
size=$(stat -c %s ${TEST_TEXT_FILE})
150+
if [ $t_size -ne $size ]
151+
then
152+
echo "error: expected ${TEST_TEXT_FILE} to be $t_size length, got $size"
153+
exit 1
154+
fi
155+
first_chars=`head -c 4 ${TEST_TEXT_FILE}`
156+
if [ "$first_chars" != "dat" ]
157+
then
158+
echo "error: expected first 4 byte is 'dat' got $first_chars"
159+
exit 1
160+
fi
161+
136162
rm_test_file
137163
}
138164

@@ -522,10 +548,16 @@ function test_file_size_in_stat_cache {
522548
python3 $CUR_DIR/test/stat_cache_test.py $TEST_BUCKET_MOUNT_POINT_1
523549
}
524550

551+
function test_integration_test {
552+
echo "Testing integration test..."
553+
python3 $CUR_DIR/test/integration-test.py $TEST_BUCKET_MOUNT_POINT_1 $TEST_BUCKET_REGION $TEST_BUCKET_NAME
554+
}
555+
525556
function run_all_tests {
557+
test_integration_test
526558
test_append_file
527-
# test_truncate_file
528-
# test_truncate_empty_file
559+
test_truncate_file
560+
test_truncate_empty_file
529561
test_mv_file
530562
test_mv_bigfile
531563
test_mv_directory
@@ -548,21 +580,24 @@ function run_all_tests {
548580
test_mtime_file
549581
test_file_size_in_stat_cache
550582
}
551-
583+
pip install -U cos-python-sdk-v5
584+
if [ ! -f coscli-linux ]; then
585+
wget "https://cosbrowser.cloud.tencent.com/software/coscli/coscli-linux"
586+
chmod a+x coscli-linux
587+
fi
552588
# Mount the bucket
553589
if [ "$TEST_BUCKET_MOUNT_POINT_1" == "" ]; then
554590
echo "Mountpoint missing"
555591
exit 1
556592
fi
557593
cd $TEST_BUCKET_MOUNT_POINT_1
594+
rm -rf *
558595

559596
if [ -e $TEST_TEXT_FILE ]
560597
then
561598
rm -f $TEST_TEXT_FILE
562599
fi
563600

564-
rm -rf *
565-
pip install -U cos-python-sdk-v5
566601
SECONDS=0
567602
run_all_tests
568603
duration=$SECONDS

0 commit comments

Comments
 (0)