Skip to content

Commit eefae4d

Browse files
author
yongqingliu
committed
Merge branch 'master' into 'master' (merge request !1)
support ftruncate file and fix mv bug
2 parents 74fc771 + 26a9148 commit eefae4d

File tree

11 files changed

+532
-12
lines changed

11 files changed

+532
-12
lines changed

configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
dnl Process this file with autoconf to produce a configure script.
2222

2323
AC_PREREQ(2.59)
24-
AC_INIT(cosfs, 1.0.20)
24+
AC_INIT(cosfs, 1.0.21)
2525
AC_CONFIG_HEADER([config.h])
2626

2727
AC_CANONICAL_SYSTEM

src/curl.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ mimes_t S3fsCurl::mimeTypes;
309309
int S3fsCurl::max_parallel_cnt = 10; // default
310310
off_t S3fsCurl::multipart_size = MULTIPART_SIZE; // default
311311
bool S3fsCurl::is_sigv4 = true; // default
312-
const string S3fsCurl::skUserAgent = "tencentyun-cosfs-v5-" + string(VERSION);
312+
string S3fsCurl::skUserAgent = "tencentyun-cosfs-v5-" + string(VERSION);
313313

314314
//-------------------------------------------------------------------
315315
// Class methods for S3fsCurl
@@ -1413,6 +1413,12 @@ bool S3fsCurl::SetRAMCredentials(const char* response)
14131413
return true;
14141414
}
14151415

1416+
bool S3fsCurl::SetUserAgentSuffix(const std::string& suffix) {
1417+
skUserAgent = "tencentyun-cosfs-v5-";
1418+
skUserAgent += suffix + "-" + VERSION;
1419+
return true;
1420+
}
1421+
14161422
bool S3fsCurl::CheckRAMCredentialUpdate(void)
14171423
{
14181424
if(0 == S3fsCurl::CAM_role.size()){

src/curl.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ class S3fsCurl
237237
static int max_parallel_cnt;
238238
static off_t multipart_size;
239239
static bool is_sigv4;
240-
static const std::string skUserAgent;
240+
static std::string skUserAgent;
241241

242242
// variables
243243
CURL* hCurl;
@@ -320,6 +320,7 @@ class S3fsCurl
320320
static int ParallelMultipartUploadRequest(const char* tpath, headers_t& meta, int fd);
321321
static int ParallelGetObjectRequest(const char* tpath, int fd, off_t start, ssize_t size);
322322
static bool CheckRAMCredentialUpdate(void);
323+
static bool SetUserAgentSuffix(const std::string& suffix);
323324

324325
// class methods(valiables)
325326
static std::string LookupMimeType(std::string name);

src/fdcache.cpp

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,7 @@ int FdEntity::Open(headers_t* pmeta, ssize_t size, time_t time)
780780
// not using cache
781781

782782
// open temporary file
783-
if(NULL == (pfile = tmpfile()) || -1 ==(fd = fileno(pfile))){
783+
if(NULL == (pfile = FdManager::MakeTempFile()) || -1 ==(fd = fileno(pfile))){
784784
S3FS_PRN_ERR("failed to open tmp file. err(%d)", errno);
785785
if(pfile){
786786
fclose(pfile);
@@ -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.
@@ -1622,6 +1659,7 @@ bool FdEntity::MergeOrgMeta(headers_t& updatemeta)
16221659
SetMtime(updatetime, true);
16231660
}
16241661
is_meta_pending |= !upload_id.empty();
1662+
is_meta_pending |= is_modify;
16251663

16261664
return is_meta_pending;
16271665
}
@@ -1648,6 +1686,7 @@ int FdEntity::UploadPendingMeta()
16481686

16491687
return result;
16501688
}
1689+
16511690
//------------------------------------------------
16521691
// FdManager symbol
16531692
//------------------------------------------------
@@ -1674,6 +1713,7 @@ pthread_mutex_t FdManager::fd_manager_lock;
16741713
bool FdManager::is_lock_init(false);
16751714
string FdManager::cache_dir("");
16761715
size_t FdManager::free_disk_space = 0;
1716+
std::string FdManager::tmp_dir = "";
16771717

16781718
//------------------------------------------------
16791719
// FdManager class methods
@@ -1820,6 +1860,28 @@ fsblkcnt_t FdManager::GetFreeDiskSpace(const char* path)
18201860
return (vfsbuf.f_bavail * vfsbuf.f_bsize);
18211861
}
18221862

1863+
FILE* FdManager::MakeTempFile() {
1864+
if (tmp_dir.empty()) {
1865+
return tmpfile();
1866+
}
1867+
int fd;
1868+
char cfn[PATH_MAX];
1869+
std::string fn = tmp_dir + "/cosfstmp.XXXXXX";
1870+
strncpy(cfn, fn.c_str(), sizeof(cfn) - 1);
1871+
cfn[sizeof(cfn) - 1] = '\0';
1872+
1873+
fd = mkstemp(cfn);
1874+
if (-1 == fd) {
1875+
S3FS_PRN_ERR("failed to create tmp file. errno(%d)", errno);
1876+
return NULL;
1877+
}
1878+
if (-1 == unlink(cfn)) {
1879+
S3FS_PRN_ERR("failed to delete tmp file. errno(%d)", errno);
1880+
return NULL;
1881+
}
1882+
return fdopen(fd, "rb+");
1883+
}
1884+
18231885
bool FdManager::IsSafeDiskSpace(const char* path, size_t size)
18241886
{
18251887
fsblkcnt_t fsize = FdManager::GetFreeDiskSpace(path);
@@ -2022,6 +2084,16 @@ bool FdManager::Close(FdEntity* ent)
20222084
return false;
20232085
}
20242086

2087+
bool FdManager::SetTmpDir(const char *dir)
2088+
{
2089+
if(!dir || '\0' == dir[0]){
2090+
tmp_dir = "/tmp";
2091+
}else{
2092+
tmp_dir = dir;
2093+
}
2094+
return true;
2095+
}
2096+
20252097
bool FdManager::ChangeEntityToTempPath(FdEntity* ent, const char* path)
20262098
{
20272099
AutoLock auto_lock(&FdManager::fd_manager_lock);

src/fdcache.h

Lines changed: 5 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

@@ -194,6 +196,7 @@ class FdManager
194196
static size_t free_disk_space; // limit free disk space
195197

196198
fdent_map_t fent;
199+
static std::string tmp_dir;
197200

198201
private:
199202
static fsblkcnt_t GetFreeDiskSpace(const char* path);
@@ -225,6 +228,8 @@ class FdManager
225228
void Rename(const std::string &from, const std::string &to);
226229
bool Close(FdEntity* ent);
227230
bool ChangeEntityToTempPath(FdEntity* ent, const char* path);
231+
static bool SetTmpDir(const char* dir);
232+
static FILE* MakeTempFile();
228233
};
229234

230235
#endif // FD_CACHE_H_

src/s3fs.cpp

Lines changed: 20 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

@@ -4574,6 +4583,15 @@ static int my_fuse_opt_proc(void* data, const char* arg, int key, struct fuse_ar
45744583
noxattr = true;
45754584
return 0;
45764585
}
4586+
if(0 == STR2NCMP(arg, "user_agent_suffix=")){
4587+
std::string user_agent_suffix = string(strchr(arg, '=') + sizeof(char));
4588+
S3fsCurl::SetUserAgentSuffix(user_agent_suffix);
4589+
return 0;
4590+
}
4591+
if(0 == STR2NCMP(arg, "tmpdir=")){
4592+
FdManager::SetTmpDir(strchr(arg, '=') + sizeof(char));
4593+
return 0;
4594+
}
45774595
// old format for storage_class
45784596
if(0 == strcmp(arg, "use_rrs") || 0 == STR2NCMP(arg, "use_rrs=")){
45794597
off_t rrs = 1;
@@ -4902,6 +4920,7 @@ static int my_fuse_opt_proc(void* data, const char* arg, int key, struct fuse_ar
49024920
is_specified_endpoint = true;
49034921
return 0;
49044922
}
4923+
49054924
if(0 == strcmp(arg, "use_path_request_style")){
49064925
pathrequeststyle = true;
49074926
return 0;

src/s3fs_util.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,12 @@ void show_help (void)
929929
" del_cache (delete local file cache)\n"
930930
" - delete local file cache when cosfs starts and exits.\n"
931931
"\n"
932+
" tmpdir (default=\"/tmp\")\n"
933+
" - local folder for temporary files.\n"
934+
"\n"
935+
" user_agent_suffix (http request user agent suffix)\n"
936+
" - specify http request user agent.\n"
937+
"\n"
932938
" storage_class (default=\"standard\")\n"
933939
" - store object with specified storage class. Possible values:\n"
934940
" standard, standard_ia, and reduced_redundancy.\n"

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: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
from qcloud_cos import CosConfig
2+
from qcloud_cos import CosS3Client
3+
import sys
4+
import logging
5+
import crcmod
6+
import os
7+
import subprocess
8+
9+
logging.basicConfig(level=logging.ERROR, stream=sys.stdout)
10+
11+
cos_client = None
12+
run_dir = ""
13+
14+
15+
def init_cos_client(region, bucket_name, mnt_point):
16+
global cos_client
17+
global run_dir
18+
secret_id = ''
19+
secret_key = ''
20+
with open('/etc/passwd-cosfs') as fl:
21+
for line in fl.readlines():
22+
if bucket_name in line:
23+
parts = line.split(":")
24+
if len(parts) < 3:
25+
raise ValueError("unexpected secret info in /etc/passwd-cosfs")
26+
secret_id = parts[1].strip()
27+
secret_key = parts[2].strip()
28+
if len(region) == 0 or len(secret_id) == 0 or len(secret_key) == 0:
29+
raise ValueError("Unexpected region or secret_id or secret_key")
30+
print("init cos client")
31+
config = CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key, Token=None, Scheme='http')
32+
cos_client = CosS3Client(config)
33+
run_dir = os.path.dirname(os.path.normpath(mnt_point))
34+
print("run_dir:" + run_dir)
35+
36+
37+
def get_cos_file_crc64(bucket, key):
38+
response = cos_client.get_object(Bucket=bucket, Key=key)
39+
c64 = crcmod.mkCrcFun(0x142F0E1EBA9EA3693, initCrc=0, xorOut=0xffffffffffffffff, rev=True)
40+
body = response['Body'].read(1024*1024*100)
41+
return str(c64(body))
42+
43+
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+
51+
def verify_file_checksum_length_with_local(bucket, key, local_file_path):
52+
object_attr = cos_client.head_object(Bucket=bucket, Key=key)
53+
test_crc64 = crcmod.Crc(0x142F0E1EBA9EA3693, initCrc=0, xorOut=0xffffffffffffffff, rev=True)
54+
# c64 = crcmod.mkCrcFun(0x142F0E1EBA9EA3693, initCrc=0, xorOut=0xffffffffffffffff, rev=True)
55+
cos_len = int(object_attr['Content-Length'])
56+
cos_crc64 = object_attr['x-cos-hash-crc64ecma']
57+
local_len = os.stat(local_file_path).st_size
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()))
72+
if local_len > 0 and cos_crc64 == '':
73+
cos_crc64 = get_cos_file_crc64(bucket, key)
74+
print('key' + key + " cos crc64:" + cos_crc64)
75+
if cos_len != local_len or (cos_len != 0 and cos_crc64 != local_crc64):
76+
print(object_attr)
77+
raise ValueError('file diff, local file:' + local_file_path + ', len:' + str(local_len) +
78+
', crc64:' + local_crc64 + '; cos file:' + key + ', len:' + str(cos_len) +
79+
', crc64:' + cos_crc64)
80+
81+
82+
def verify_file_checksum_length(bucket, key, local_len, local_crc64):
83+
object_attr = cos_client.head_object(Bucket=bucket, Key=key)
84+
cos_len = int(object_attr['Content-Length'])
85+
cos_crc64 = object_attr['x-cos-hash-crc64ecma']
86+
if local_len > 0 and cos_crc64 == '':
87+
cos_crc64 = get_cos_file_crc64(bucket, key)
88+
print('key' + key + " cos crc64:" + cos_crc64)
89+
if cos_len != local_len or (cos_len != 0 and cos_crc64 != local_crc64):
90+
raise ValueError('file diff, local len:' + str(local_len) +
91+
', crc64:' + local_crc64 + '; cos file:' + key + ', len:' + str(cos_len) +
92+
', crc64:' + cos_crc64)

0 commit comments

Comments
 (0)