1 /*
2 * Copyright (c) 2024-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "mtpfs_fuse.h"
17
18 #include <fuse_opt.h>
19 #include <unistd.h>
20
21 #include "mtpfs_util.h"
22 #include "storage_service_log.h"
23
24 #define PERMISSION_ONE 0775
25 #define PERMISSION_TWO 0644
26
27 constexpr int UPLOAD_RECORD_FALSE_LEN = 5;
28 constexpr int UPLOAD_RECORD_TRUE_LEN = 4;
29
30 constexpr int32_t ST_NLINK_TWO = 2;
31 constexpr int32_t FILE_SIZE = 512;
32 constexpr int32_t BS_SIZE = 1024;
33 constexpr int32_t ARG_SIZE = 2;
34
WrapGetattr(const char * path,struct stat * buf,struct fuse_file_info * fi)35 int WrapGetattr(const char *path, struct stat *buf, struct fuse_file_info *fi)
36 {
37 LOGI("mtp WrapGetattr, path=%{public}s", path);
38 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->GetAttr(path, buf, fi);
39 LOGI("GetAttr ret = %{public}d.", ret);
40 return ret;
41 }
42
WrapMkNod(const char * path,mode_t mode,dev_t dev)43 int WrapMkNod(const char *path, mode_t mode, dev_t dev)
44 {
45 LOGI("mtp WrapMkNod, path=%{public}s", path);
46 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->MkNod(path, mode, dev);
47 LOGI("MkNod ret = %{public}d.", ret);
48 return ret;
49 }
50
WrapMkDir(const char * path,mode_t mode)51 int WrapMkDir(const char *path, mode_t mode)
52 {
53 LOGI("mtp WrapMkDir, path=%{public}s", path);
54 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->MkDir(path, mode);
55 LOGI("MkDir ret = %{public}d.", ret);
56 return ret;
57 }
58
WrapUnLink(const char * path)59 int WrapUnLink(const char *path)
60 {
61 LOGI("mtp WrapUnLink, path=%{public}s", path);
62 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->UnLink(path);
63 LOGI("UnLink ret = %{public}d.", ret);
64 return ret;
65 }
66
WrapRmDir(const char * path)67 int WrapRmDir(const char *path)
68 {
69 LOGI("mtp WrapRmDir, path=%{public}s", path);
70 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->RmDir(path);
71 LOGI("RmDir ret = %{public}d.", ret);
72 return ret;
73 }
74
WrapReName(const char * path,const char * newpath,unsigned int flags)75 int WrapReName(const char *path, const char *newpath, unsigned int flags)
76 {
77 LOGI("mtp WrapReName, path=%{public}s", path);
78 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->ReName(path, newpath, flags);
79 LOGI("ReName ret = %{public}d.", ret);
80 return ret;
81 }
82
WrapChMod(const char * path,mode_t mode,struct fuse_file_info * fi)83 int WrapChMod(const char *path, mode_t mode, struct fuse_file_info *fi)
84 {
85 LOGI("mtp WrapChMod, path=%{public}s", path);
86 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->ChMods(path, mode, fi);
87 LOGI("ChMods ret = %{public}d.", ret);
88 return ret;
89 }
90
WrapChown(const char * path,uid_t uid,gid_t gid,struct fuse_file_info * fi)91 int WrapChown(const char *path, uid_t uid, gid_t gid, struct fuse_file_info *fi)
92 {
93 LOGE("mtp WrapChown path:%{public}s ,uid:%{public}lu, gid:%{public}lu", path, uid, gid);
94 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->Chown(path, uid, gid, fi);
95 LOGI("Chown ret = %{public}d.", ret);
96 return ret;
97 }
98
99
WrapUTimens(const char * path,const struct timespec tv[2],struct fuse_file_info * fi)100 int WrapUTimens(const char *path, const struct timespec tv[2], struct fuse_file_info *fi)
101 {
102 LOGI("mtp WrapUTimens, path=%{public}s", path);
103 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->UTimens(path, tv, fi);
104 LOGI("UTimens ret = %{public}d.", ret);
105 return ret;
106 }
107
WrapOpen(const char * path,struct fuse_file_info * fileInfo)108 int WrapOpen(const char *path, struct fuse_file_info *fileInfo)
109 {
110 LOGI("mtp WrapOpen, path=%{public}s", path);
111 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->Open(path, fileInfo);
112 LOGI("Open ret = %{public}d.", ret);
113 return ret;
114 }
115
WrapRead(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fileInfo)116 int WrapRead(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fileInfo)
117 {
118 LOGI("mtp WrapRead, path=%{public}s", path);
119 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->Read(path, buf, size, offset, fileInfo);
120 LOGI("Read ret = %{public}d.", ret);
121 return ret;
122 }
123
WrapWrite(const char * path,const char * buf,size_t size,off_t offset,struct fuse_file_info * fileInfo)124 int WrapWrite(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fileInfo)
125 {
126 LOGI("mtp WrapWrite, path=%{public}s", path);
127 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->Write(path, buf, size, offset, fileInfo);
128 LOGI("Write ret = %{public}d.", ret);
129 return ret;
130 }
131
WrapStatfs(const char * path,struct statvfs * statInfo)132 int WrapStatfs(const char *path, struct statvfs *statInfo)
133 {
134 LOGI("mtp WrapStatfs, path=%{public}s", path);
135 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->Statfs(path, statInfo);
136 LOGI("Statfs ret = %{public}d.", ret);
137 return ret;
138 }
139
WrapFlush(const char * path,struct fuse_file_info * fileInfo)140 int WrapFlush(const char *path, struct fuse_file_info *fileInfo)
141 {
142 LOGI("mtp WrapFlush, path=%{public}s", path);
143 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->Flush(path, fileInfo);
144 LOGI("Flush ret = %{public}d.", ret);
145 return ret;
146 }
147
WrapRelease(const char * path,struct fuse_file_info * fileInfo)148 int WrapRelease(const char *path, struct fuse_file_info *fileInfo)
149 {
150 LOGI("mtp WrapRelease, path=%{public}s", path);
151 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->Release(path, fileInfo);
152 LOGI("Release ret = %{public}d.", ret);
153 return ret;
154 }
155
WrapFSync(const char * path,int datasync,struct fuse_file_info * fileInfo)156 int WrapFSync(const char *path, int datasync, struct fuse_file_info *fileInfo)
157 {
158 LOGI("mtp WrapFSync, path=%{public}s", path);
159 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->FSync(path, datasync, fileInfo);
160 LOGI("FSync ret = %{public}d.", ret);
161 return ret;
162 }
163
WrapOpenDir(const char * path,struct fuse_file_info * fileInfo)164 int WrapOpenDir(const char *path, struct fuse_file_info *fileInfo)
165 {
166 LOGI("mtp WrapOpenDir, path=%{public}s", path);
167 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->OpenDir(path, fileInfo);
168 LOGI("OpenDir ret = %{public}d.", ret);
169 return ret;
170 }
171
WrapReadDir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fileInfo,enum fuse_readdir_flags flag)172 int WrapReadDir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fileInfo,
173 enum fuse_readdir_flags flag)
174 {
175 LOGI("mtp WrapReadDir, path=%{public}s", path);
176 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->ReadDir(path, buf, filler, offset, fileInfo, flag);
177 LOGI("ReadDir ret = %{public}d.", ret);
178 return ret;
179 }
180
WrapReleaseDir(const char * path,struct fuse_file_info * fileInfo)181 int WrapReleaseDir(const char *path, struct fuse_file_info *fileInfo)
182 {
183 LOGI("mtp WrapReleaseDir, path=%{public}s", path);
184 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->ReleaseDir(path, fileInfo);
185 LOGI("ReleaseDir ret = %{public}d.", ret);
186 return ret;
187 }
188
WrapFSyncDir(const char * path,int datasync,struct fuse_file_info * fileInfo)189 int WrapFSyncDir(const char *path, int datasync, struct fuse_file_info *fileInfo)
190 {
191 LOGI("mtp WrapFSyncDir, path=%{public}s", path);
192 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->FSyncDir(path, datasync, fileInfo);
193 LOGI("FSyncDir ret = %{public}d.", ret);
194 return ret;
195 }
196
WrapInit(struct fuse_conn_info * conn,struct fuse_config * cfg)197 void *WrapInit(struct fuse_conn_info *conn, struct fuse_config *cfg)
198 {
199 LOGI("mtp WrapInit");
200 return DelayedSingleton<MtpFileSystem>::GetInstance()->Init(conn, cfg);
201 }
202
WrapCreate(const char * path,mode_t mode,fuse_file_info * fileInfo)203 int WrapCreate(const char *path, mode_t mode, fuse_file_info *fileInfo)
204 {
205 LOGI("mtp WrapCreate, path=%{public}s", path);
206 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->Create(path, mode, fileInfo);
207 LOGI("Create ret = %{public}d.", ret);
208 return ret;
209 }
210
WrapTruncate(const char * path,off_t offset,struct fuse_file_info * fileInfo)211 int WrapTruncate(const char *path, off_t offset, struct fuse_file_info *fileInfo)
212 {
213 LOGI("mtp WrapTruncate");
214 return 0;
215 }
216
WrapReadLink(const char * path,char * out,size_t size)217 int WrapReadLink(const char *path, char *out, size_t size)
218 {
219 LOGI("mtp WrapReadLink");
220 return 0;
221 }
222
WrapSymLink(const char * path,const char * mode)223 int WrapSymLink(const char *path, const char * mode)
224 {
225 LOGI("mtp WrapSymLink");
226 return 0;
227 }
228
WrapLink(const char * path,const char * out)229 int WrapLink(const char *path, const char *out)
230 {
231 LOGI("mtp WrapLink");
232 return 0;
233 }
234
WrapSetXAttr(const char * path,const char * in,const char * out,size_t size,int flag)235 int WrapSetXAttr(const char *path, const char *in, const char *out, size_t size, int flag)
236 {
237 LOGI("mtp WrapSetXAttr, path=%{public}s", path);
238 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->SetXAttr(path, in);
239 LOGI("WrapSetXAttr ret = %{public}d.", ret);
240 return ret;
241 }
242
WrapGetXAttr(const char * path,const char * in,char * out,size_t size)243 int WrapGetXAttr(const char *path, const char *in, char *out, size_t size)
244 {
245 LOGI("mtp WrapGetXAttr, path=%{public}s", path);
246 int ret = DelayedSingleton<MtpFileSystem>::GetInstance()->GetXAttr(path, in, out, size);
247 LOGI("WrapGetXAttr ret = %{public}d.", ret);
248 return ret;
249 }
250
WrapListXAttr(const char * path,char * in,size_t size)251 int WrapListXAttr(const char *path, char *in, size_t size)
252 {
253 LOGI("mtp WrapListXAttr");
254 return 0;
255 }
256
WrapRemoveXAttr(const char * path,const char * in)257 int WrapRemoveXAttr(const char *path, const char *in)
258 {
259 LOGI("mtp WrapRemoveXAttr");
260 return 0;
261 }
262
WrapDestroy(void * path)263 void WrapDestroy(void *path)
264 {
265 LOGI("mtp WrapDestroy");
266 return;
267 }
268
WrapAccess(const char * path,int size)269 int WrapAccess(const char *path, int size)
270 {
271 LOGI("mtp WrapAccess");
272 return 0;
273 }
274
MtpFileSystemOptions()275 MtpFileSystem::MtpFileSystemOptions::MtpFileSystemOptions()
276 : good_(false),
277 verBose_(false),
278 enableMove_(false),
279 deviceNo_(1),
280 deviceFile_(nullptr),
281 mountPoint_(nullptr)
282 {}
283
~MtpFileSystemOptions()284 MtpFileSystem::MtpFileSystemOptions::~MtpFileSystemOptions()
285 {
286 free(static_cast<void *>(deviceFile_));
287 free(static_cast<void *>(mountPoint_));
288 }
289
OptProc(void * data,const char * arg,int key,struct fuse_args * outargs)290 int MtpFileSystem::MtpFileSystemOptions::OptProc(void *data, const char *arg, int key, struct fuse_args *outargs)
291 {
292 MtpFileSystemOptions *options = static_cast<MtpFileSystemOptions *>(data);
293
294 if (key == FUSE_OPT_KEY_NONOPT && options != nullptr) {
295 if (options->mountPoint_ && options->deviceFile_) {
296 // Unknown positional argument supplied
297 return -1;
298 }
299 if (options->deviceFile_) {
300 fuse_opt_add_opt(&options->mountPoint_, arg);
301 return 0;
302 }
303
304 fuse_opt_add_opt(&options->deviceFile_, arg);
305 return 0;
306 }
307 return 1;
308 }
309
MtpFileSystem()310 MtpFileSystem::MtpFileSystem() : args_(), tmpFilesPool_(), options_(), device_()
311 {
312 LOGI("mtp MtpFileSystem");
313 fuseOperations_.getattr = WrapGetattr;
314 fuseOperations_.readlink = WrapReadLink;
315 fuseOperations_.mknod = WrapMkNod;
316 fuseOperations_.mkdir = WrapMkDir;
317 fuseOperations_.unlink = WrapUnLink;
318 fuseOperations_.rmdir = WrapRmDir;
319 fuseOperations_.symlink = WrapSymLink;
320 fuseOperations_.rename = WrapReName;
321 fuseOperations_.link = WrapLink;
322 fuseOperations_.chmod = WrapChMod;
323 fuseOperations_.chown = WrapChown;
324 fuseOperations_.truncate = WrapTruncate;
325 fuseOperations_.utimens = WrapUTimens;
326 fuseOperations_.open = WrapOpen;
327 fuseOperations_.read = WrapRead;
328 fuseOperations_.write = WrapWrite;
329 fuseOperations_.statfs = WrapStatfs;
330 fuseOperations_.flush = WrapFlush;
331 fuseOperations_.release = WrapRelease;
332 fuseOperations_.fsync = WrapFSync;
333 fuseOperations_.setxattr = WrapSetXAttr;
334 fuseOperations_.getxattr = WrapGetXAttr;
335 fuseOperations_.listxattr = WrapListXAttr;
336 fuseOperations_.removexattr = WrapRemoveXAttr;
337 fuseOperations_.opendir = WrapOpenDir;
338 fuseOperations_.readdir = WrapReadDir;
339 fuseOperations_.releasedir = WrapReleaseDir;
340 fuseOperations_.fsyncdir = WrapFSyncDir;
341 fuseOperations_.init = WrapInit;
342 fuseOperations_.destroy = WrapDestroy;
343 fuseOperations_.access = WrapAccess;
344 fuseOperations_.create = WrapCreate;
345 fuseOperations_.ioctl = nullptr;
346 fuseOperations_.bmap = nullptr;
347 fuseOperations_.read_buf = nullptr;
348 fuseOperations_.lseek = nullptr;
349 fuseOperations_.copy_file_range = nullptr;
350 fuseOperations_.fallocate = nullptr;
351 fuseOperations_.lock = nullptr;
352 fuseOperations_.flock = nullptr;
353 fuseOperations_.poll = nullptr;
354 fuseOperations_.write_buf = nullptr;
355 }
356
~MtpFileSystem()357 MtpFileSystem::~MtpFileSystem()
358 {
359 fuse_opt_free_args(&args_);
360 }
361
ParseOptionsInner()362 bool MtpFileSystem::ParseOptionsInner()
363 {
364 fuse_opt_add_arg(&args_, options_.mountPoint_);
365
366 if (options_.verBose_) {
367 fuse_opt_add_arg(&args_, "-f");
368 }
369
370 --options_.deviceNo_;
371
372 // device file and -- device are mutually exclusive, fail if both set
373 if (options_.deviceNo_ && options_.deviceFile_) {
374 options_.good_ = false;
375 return false;
376 }
377 options_.good_ = true;
378 return true;
379 }
380
ParseOptions(int argc,char ** argv)381 bool MtpFileSystem::ParseOptions(int argc, char **argv)
382 {
383 LOGI("mtp MtpFileSystem ParseOptions");
384 #define SMTPFS_OPT_KEY(t, p, v) \
385 { \
386 t, offsetof(MtpFileSystemOptions, p), v \
387 }
388
389 static struct fuse_opt smtpfs_opts[] = {
390 SMTPFS_OPT_KEY("enable-move", enableMove_, 1),
391 SMTPFS_OPT_KEY("--device %i", deviceNo_, 0),
392 SMTPFS_OPT_KEY("-v", verBose_, 1),
393 SMTPFS_OPT_KEY("--verbose", verBose_, 1),
394
395 FUSE_OPT_END
396 };
397
398 if (argc < ARG_SIZE) {
399 LOGE("Wrong usage");
400 options_.good_ = false;
401 return false;
402 }
403
404 fuse_opt_free_args(&args_);
405 args_ = FUSE_ARGS_INIT(argc, argv);
406 if (fuse_opt_parse(&args_, &options_, smtpfs_opts, MtpFileSystemOptions::OptProc) == -1) {
407 options_.good_ = false;
408 return false;
409 }
410
411 if (options_.deviceFile_ && !options_.mountPoint_) {
412 options_.mountPoint_ = options_.deviceFile_;
413 options_.deviceFile_ = nullptr;
414 }
415
416 if (!options_.mountPoint_) {
417 LOGE("Mount point missing");
418 options_.good_ = false;
419 return false;
420 }
421 if (!ParseOptionsInner()) {
422 return false;
423 }
424 return true;
425 }
426
Exec()427 bool MtpFileSystem::Exec()
428 {
429 if (!options_.good_) {
430 return false;
431 }
432
433 if (!SmtpfsCheckDir(options_.mountPoint_)) {
434 LOGE("Can not mount the device to %{public}s", options_.mountPoint_);
435 return false;
436 }
437
438 if (!tmpFilesPool_.CreateTmpDir()) {
439 LOGE("Can not create a temporary directory");
440 return false;
441 }
442
443 if (options_.deviceFile_) {
444 // Try to use device file first, if provided
445 if (!device_.ConnectByDevFile(options_.deviceFile_)) {
446 return false;
447 }
448 } else {
449 // Connect to MTP device by order number, if no device file supplied
450 LOGI("Attempting to connect device by order number: %d", options_.deviceNo_);
451 if (!device_.ConnectByDevNo(options_.deviceNo_)) {
452 return false;
453 }
454 }
455 device_.EnableMove(options_.enableMove_);
456 int ret = fuse_main(args_.argc, args_.argv, &fuseOperations_, nullptr);
457 if (ret > 0) {
458 LOGE("fuse_main fail, ret = %{public}d", ret);
459 return false;
460 }
461 device_.Disconnect();
462
463 if (!tmpFilesPool_.RemoveTmpDir()) {
464 LOGE("Can not remove a temporary directory");
465 return false;
466 }
467
468 return true;
469 }
470
Init(struct fuse_conn_info * conn,struct fuse_config * cfg)471 void *MtpFileSystem::Init(struct fuse_conn_info *conn, struct fuse_config *cfg)
472 {
473 return nullptr;
474 }
475
GetAttr(const char * path,struct stat * buf,struct fuse_file_info * fi)476 int MtpFileSystem::GetAttr(const char *path, struct stat *buf, struct fuse_file_info *fi)
477 {
478 LOGI("MtpFileSystem: GetAttr enter, path: %{public}s", path);
479 if (memset_s(buf, sizeof(struct stat), 0, sizeof(struct stat)) != EOK) {
480 LOGE("memset stat fail");
481 }
482 struct fuse_context *fc = fuse_get_context();
483 if (buf == nullptr || fc == nullptr) {
484 LOGE("MtpFileSystem: buf is null or get file contxt failed");
485 return -ENOENT;
486 }
487 buf->st_uid = fc->uid;
488 buf->st_gid = fc->gid;
489 if (path == std::string("/")) {
490 buf->st_mode = S_IFDIR | PERMISSION_ONE;
491 buf->st_nlink = ST_NLINK_TWO;
492 return 0;
493 } else {
494 std::string tmpPath(SmtpfsDirName(path));
495 std::string tmpFile(SmtpfsBaseName(path));
496 const MtpFsTypeDir *content = device_.DirFetchContent(tmpPath);
497 if (content == nullptr) {
498 LOGE("MtpFileSystem: GetAttr error, content is null, path: %{public}s", path);
499 return -ENOENT;
500 } else if (content->Dir(tmpFile) != nullptr) {
501 const MtpFsTypeDir *dir = content->Dir(tmpFile);
502 buf->st_ino = dir->Id();
503 buf->st_mode = S_IFDIR | PERMISSION_ONE;
504 buf->st_nlink = ST_NLINK_TWO;
505 buf->st_mtime = dir->ModificationDate();
506 } else if (content->File(tmpFile) != nullptr) {
507 const MtpFsTypeFile *file = content->File(tmpFile);
508 buf->st_ino = file->Id();
509 buf->st_size = static_cast<ssize_t>(file->Size());
510 buf->st_blocks = static_cast<ssize_t>(file->Size() / FILE_SIZE) + (file->Size() % FILE_SIZE > 0 ? 1 : 0);
511 buf->st_nlink = 1;
512 buf->st_mode = S_IFREG | PERMISSION_TWO;
513 buf->st_mtime = file->ModificationDate();
514 buf->st_ctime = buf->st_mtime;
515 buf->st_atime = buf->st_mtime;
516 } else {
517 LOGE("MtpFileSystem: GetAttr error, content dir is null, path: %{public}s", path);
518 return -ENOENT;
519 }
520 }
521 LOGI("MtpFileSystem: GetAttr success, path: %{public}s", path);
522 return 0;
523 }
524
MkNod(const char * path,mode_t mode,dev_t dev)525 int MtpFileSystem::MkNod(const char *path, mode_t mode, dev_t dev)
526 {
527 if (!S_ISREG(mode)) {
528 return -EINVAL;
529 }
530 std::string tmpPath = tmpFilesPool_.MakeTmpPath(std::string(path));
531 int rval = ::open(tmpPath.c_str(), O_CREAT | O_WRONLY, mode);
532 if (rval < 0) {
533 return -errno;
534 }
535 rval = ::close(rval);
536 if (rval < 0) {
537 return -errno;
538 }
539 rval = device_.FilePush(tmpPath, std::string(path));
540 ::unlink(tmpPath.c_str());
541
542 if (rval != 0) {
543 return rval;
544 }
545 return 0;
546 }
547
MkDir(const char * path,mode_t mode)548 int MtpFileSystem::MkDir(const char *path, mode_t mode)
549 {
550 return device_.DirCreateNew(std::string(path));
551 }
552
UnLink(const char * path)553 int MtpFileSystem::UnLink(const char *path)
554 {
555 return device_.FileRemove(std::string(path));
556 }
557
RmDir(const char * path)558 int MtpFileSystem::RmDir(const char *path)
559 {
560 return device_.DirRemove(std::string(path));
561 }
562
ReName(const char * path,const char * newpath,unsigned int flags)563 int MtpFileSystem::ReName(const char *path, const char *newpath, unsigned int flags)
564 {
565 LOGI("MtpFileSystem: ReName, path=%{public}s, newpath=%{public}s", path, newpath);
566 const std::string tmpOldDirName(SmtpfsDirName(std::string(path)));
567 const std::string tmpNewDirName(SmtpfsDirName(std::string(newpath)));
568 if (tmpOldDirName == tmpNewDirName) {
569 return device_.ReName(std::string(path), std::string(newpath));
570 }
571 if (!options_.enableMove_) {
572 return -EPERM;
573 }
574 const std::string tmpFile = tmpFilesPool_.MakeTmpPath(std::string(newpath));
575 int rval = device_.FilePull(std::string(path), tmpFile);
576 if (rval != 0) {
577 return -rval;
578 }
579 rval = device_.FilePush(tmpFile, std::string(newpath));
580 if (rval != 0) {
581 return -rval;
582 }
583 rval = device_.FileRemove(std::string(path));
584 if (rval != 0) {
585 return -rval;
586 }
587 const std::string tmpBaseName(SmtpfsBaseName(newpath));
588 const std::string tmpDirName(SmtpfsDirName(newpath));
589 const MtpFsTypeDir *dirParent = device_.DirFetchContent(tmpDirName);
590 const int32_t factor = 1000;
591 auto now = std::chrono::system_clock::now();
592 auto millisecs = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
593 time_t time = static_cast<time_t>(millisecs.count() / factor);
594 if (dirParent != nullptr) {
595 LOGI("MtpFileSystem: file cutted, update dirParent mtime");
596 const_cast<MtpFsTypeDir *>(dirParent)->SetModificationDate(time);
597 }
598 return 0;
599 }
600
ChMods(const char * path,mode_t mode,struct fuse_file_info * fi)601 int MtpFileSystem::ChMods(const char *path, mode_t mode, struct fuse_file_info *fi)
602 {
603 int res;
604 if (fi != nullptr) {
605 res = fchmod(fi->fh, mode);
606 } else {
607 res = chmod(path, mode);
608 }
609 if (res == -1) {
610 return -errno;
611 }
612 return 0;
613 }
614
Chown(const char * path,uid_t uid,gid_t gid,struct fuse_file_info * fi)615 int MtpFileSystem::Chown(const char *path, uid_t uid, gid_t gid, struct fuse_file_info *fi)
616 {
617 LOGI("mtp Chown path:%{public}s ,uid:%{public}lu, gid:%{public}lu", path, uid, gid);
618 int res;
619 if (fi != nullptr) {
620 res = fchown(fi->fh, uid, gid);
621 } else {
622 res = lchown(path, uid, gid);
623 }
624 if (res == -1) {
625 return -errno;
626 }
627 return 0;
628 }
629
Truncate(const char * path,off_t new_size,struct fuse_file_info * fileInfo)630 int MtpFileSystem::Truncate(const char *path, off_t new_size, struct fuse_file_info *fileInfo)
631 {
632 const std::string tmpPath = tmpFilesPool_.MakeTmpPath(std::string(path));
633 int rval = device_.FilePull(std::string(path), tmpPath);
634 if (rval != 0) {
635 ::unlink(tmpPath.c_str());
636 return -rval;
637 }
638
639 rval = ::truncate(tmpPath.c_str(), new_size);
640 if (rval != 0) {
641 int errnoTmp = errno;
642 ::unlink(tmpPath.c_str());
643 return -errnoTmp;
644 }
645
646 rval = device_.FileRemove(std::string(path));
647 if (rval != 0) {
648 ::unlink(tmpPath.c_str());
649 return -rval;
650 }
651 rval = device_.FilePush(tmpPath, std::string(path));
652 ::unlink(tmpPath.c_str());
653 if (rval != 0) {
654 return -rval;
655 }
656 return 0;
657 }
658
UTimens(const char * path,const struct timespec tv[2],struct fuse_file_info * fi)659 int MtpFileSystem::UTimens(const char *path, const struct timespec tv[2], struct fuse_file_info *fi)
660 {
661 std::string tmpBaseName(SmtpfsBaseName(std::string(path)));
662 std::string tmpDirName(SmtpfsDirName(std::string(path)));
663 const MtpFsTypeDir *parent = device_.DirFetchContent(tmpDirName);
664 if (parent == nullptr) {
665 LOGE("MtpFileSystem: UTimens parent is nullptr");
666 return -ENOENT;
667 }
668 const MtpFsTypeDir *dir = parent->Dir(tmpBaseName);
669 if (dir == nullptr) {
670 LOGE("MtpFileSystem: UTimens dir is nullptr");
671 return -ENOENT;
672 }
673 const_cast<MtpFsTypeDir *>(dir)->SetModificationDate(tv[1].tv_sec);
674 return 0;
675 }
676
677
Create(const char * path,mode_t mode,fuse_file_info * fileInfo)678 int MtpFileSystem::Create(const char *path, mode_t mode, fuse_file_info *fileInfo)
679 {
680 const std::string tmpPath = tmpFilesPool_.MakeTmpPath(std::string(path));
681 int rval = ::creat(tmpPath.c_str(), mode);
682 if (rval < 0) {
683 return -errno;
684 }
685 if (fileInfo != nullptr) {
686 fileInfo->fh = static_cast<uint32_t>(rval);
687 }
688 tmpFilesPool_.AddFile(MtpFsTypeTmpFile(std::string(path), tmpPath, rval, true));
689 rval = device_.FilePush(tmpPath, std::string(path));
690 if (rval != 0) {
691 LOGE("MtpFileSystem: Create, FilePush path: %{public}s fail", path);
692 return rval;
693 }
694 return 0;
695 }
696
Open(const char * path,struct fuse_file_info * fileInfo)697 int MtpFileSystem::Open(const char *path, struct fuse_file_info *fileInfo)
698 {
699 std::lock_guard<std::mutex>lock(fuseMutex_);
700 LOGI("MtpFileSystem: Open enter, path: %{public}s", path);
701 if (fileInfo == nullptr) {
702 LOGE("Missing FileInfo");
703 return -ENOENT;
704 }
705 unsigned int flags = static_cast<unsigned int>(fileInfo->flags);
706 if (flags & O_WRONLY) {
707 flags |= O_TRUNC;
708 }
709 fileInfo->flags = static_cast<int>(flags);
710 const std::string stdPath(path);
711
712 MtpFsTypeTmpFile *tmpFile = const_cast<MtpFsTypeTmpFile *>(tmpFilesPool_.GetFile(stdPath));
713
714 std::string tmpPath;
715 if (tmpFile != nullptr) {
716 tmpPath = tmpFile->PathTmp();
717 } else {
718 tmpPath = tmpFilesPool_.MakeTmpPath(stdPath);
719 // only copy the file if needed
720 if (!HasPartialObjectSupport()) {
721 int rval = device_.FilePull(stdPath, tmpPath);
722 if (rval != 0) {
723 return -rval;
724 }
725 } else {
726 int fd = ::creat(tmpPath.c_str(), S_IRUSR | S_IWUSR);
727 ::close(fd);
728 }
729 }
730
731 // we create the tmp file even if we can use partial get/send to
732 // have a valid file descriptor
733 int fd = ::open(tmpPath.c_str(), fileInfo->flags);
734 if (fd < 0) {
735 ::unlink(tmpPath.c_str());
736 LOGE("MtpFileSystem: Open error, errno=%{public}d", errno);
737 return -errno;
738 }
739
740 fileInfo->fh = static_cast<uint32_t>(fd);
741
742 if (tmpFile != nullptr) {
743 tmpFile->AddFileDescriptor(fd);
744 } else {
745 tmpFilesPool_.AddFile(MtpFsTypeTmpFile(stdPath, tmpPath, fd));
746 }
747 LOGI("MtpFileSystem: Open success, path: %{public}s", path);
748 return 0;
749 }
750
Read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fileInfo)751 int MtpFileSystem::Read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fileInfo)
752 {
753 LOGI("MtpFileSystem: Read enter, path: %{public}s", path);
754 if (fileInfo == nullptr) {
755 LOGE("Missing FileInfo");
756 return -ENOENT;
757 }
758 int rval = 0;
759 if (HasPartialObjectSupport()) {
760 const std::string stdPath(path);
761 rval = device_.FileRead(stdPath, buf, size, offset);
762 } else {
763 rval = ::pread(fileInfo->fh, buf, size, offset);
764 if (rval < 0) {
765 LOGE("MtpFileSystem: Read error, errno=%{public}d", errno);
766 return -errno;
767 }
768 }
769 LOGI("MtpFileSystem: Open success, path: %{public}s, rval=%{public}d", path, rval);
770 return rval;
771 }
772
Write(const char * path,const char * buf,size_t size,off_t offset,struct fuse_file_info * fileInfo)773 int MtpFileSystem::Write(const char *path, const char *buf, size_t size, off_t offset,
774 struct fuse_file_info *fileInfo)
775 {
776 LOGI("MtpFileSystem: Write enter, path: %{public}s", path);
777 if (fileInfo == nullptr) {
778 LOGE("Missing FileInfo");
779 return -ENOENT;
780 }
781 int rval = 0;
782 if (HasPartialObjectSupport()) {
783 const std::string stdPath(path);
784 rval = device_.FileWrite(stdPath, buf, size, offset);
785 } else {
786 const MtpFsTypeTmpFile *tmpFile = tmpFilesPool_.GetFile(std::string(path));
787 if (tmpFile == nullptr) {
788 LOGE("MtpFileSystem: Write tmpFile error.");
789 return -EINVAL;
790 }
791 rval = ::pwrite(fileInfo->fh, buf, size, offset);
792 if (rval < 0) {
793 LOGE("MtpFileSystem: Write pwrite error, errno=%{public}d", errno);
794 return -errno;
795 }
796 const_cast<MtpFsTypeTmpFile *>(tmpFile)->SetModified();
797 }
798 LOGI("MtpFileSystem: Write success, path: %{public}s, rval=%{public}d", path, rval);
799 return rval;
800 }
801
Release(const char * path,struct fuse_file_info * fileInfo)802 int MtpFileSystem::Release(const char *path, struct fuse_file_info *fileInfo)
803 {
804 std::lock_guard<std::mutex>lock(fuseMutex_);
805 LOGI("MtpFileSystem: Release enter, path: %{public}s", path);
806 if (fileInfo == nullptr) {
807 LOGE("Missing FileInfo");
808 return -ENOENT;
809 }
810 int rval = ::close(fileInfo->fh);
811 if (rval < 0) {
812 LOGE("MtpFileSystem: Release close error, errno=%{public}d", errno);
813 return -errno;
814 }
815 const std::string stdPath(path);
816 if (stdPath == std::string("-")) {
817 return 0;
818 }
819 MtpFsTypeTmpFile *tmpFile = const_cast<MtpFsTypeTmpFile *>(tmpFilesPool_.GetFile(stdPath));
820 if (tmpFile == nullptr) {
821 LOGE("failed to get tmpFile.");
822 return -EINVAL;
823 }
824 tmpFile->RemoveFileDescriptor(fileInfo->fh);
825 if (tmpFile->RefCnt() != 0) {
826 return 0;
827 }
828 const bool modIf = tmpFile->IsModified();
829 const std::string tmpPath = tmpFile->PathTmp();
830 tmpFilesPool_.RemoveFile(stdPath);
831 if (modIf) {
832 device_.SetUploadRecord(stdPath, false);
833 rval = device_.FilePush(tmpPath, stdPath);
834 device_.SetUploadRecord(stdPath, true);
835 if (rval != 0) {
836 LOGE("FilePush %{public}s to mtp device fail", path);
837 ::unlink(tmpPath.c_str());
838 return -rval;
839 }
840 LOGI("FilePush %{public}s to mtp device success", path);
841 } else {
842 LOGI("Release file no modify");
843 device_.SetUploadRecord(stdPath, true);
844 }
845 ::unlink(tmpPath.c_str());
846 LOGI("MtpFileSystem: Release success, path: %{public}s", path);
847 return 0;
848 }
849
Statfs(const char * path,struct statvfs * statInfo)850 int MtpFileSystem::Statfs(const char *path, struct statvfs *statInfo)
851 {
852 uint64_t bs = BS_SIZE;
853 if (statInfo == nullptr) {
854 LOGE("Missing statInfo");
855 return -ENOENT;
856 }
857 // XXX: linux coreutils still use bsize member to calculate free space
858 statInfo->f_bsize = static_cast<unsigned long>(bs);
859 statInfo->f_frsize = static_cast<unsigned long>(bs);
860 statInfo->f_blocks = device_.StorageTotalSize() / bs;
861 statInfo->f_bavail = device_.StorageFreeSize() / bs;
862 statInfo->f_bfree = statInfo->f_bavail;
863 return 0;
864 }
865
Flush(const char * path,struct fuse_file_info * fileInfo)866 int MtpFileSystem::Flush(const char *path, struct fuse_file_info *fileInfo)
867 {
868 return 0;
869 }
870
FSync(const char * path,int datasync,struct fuse_file_info * fi)871 int MtpFileSystem::FSync(const char *path, int datasync, struct fuse_file_info *fi)
872 {
873 int rval = -1;
874 if (fi == nullptr) {
875 LOGE("Missing FileInfo");
876 return -ENOENT;
877 }
878 #ifdef HAVE_FDATASYNC
879 if (datasync) {
880 rval = ::fdatasync(fi->fh);
881 }
882 #else
883 rval = ::fsync(fi->fh);
884 #endif
885 if (rval != 0) {
886 return -errno;
887 }
888 return 0;
889 }
890
OpenDir(const char * path,struct fuse_file_info * fileInfo)891 int MtpFileSystem::OpenDir(const char *path, struct fuse_file_info *fileInfo)
892 {
893 LOGI("MtpFileSystem: OpenDir, path: %{public}s", path);
894 const MtpFsTypeDir *content = device_.DirFetchContent(std::string(path));
895 if (content == nullptr) {
896 return -ENOENT;
897 }
898 return 0;
899 }
900
ReadDir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fileInfo,enum fuse_readdir_flags flag)901 int MtpFileSystem::ReadDir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset,
902 struct fuse_file_info *fileInfo, enum fuse_readdir_flags flag)
903 {
904 LOGI("MtpFileSystem: ReadDir, path: %{public}s", path);
905 enum fuse_fill_dir_flags fillFlags = FUSE_FILL_DIR_PLUS;
906 const MtpFsTypeDir *content = device_.DirFetchContent(std::string(path));
907 if (content == nullptr) {
908 return -ENOENT;
909 }
910 const std::set<MtpFsTypeDir> dirs = content->Dirs();
911 const std::set<MtpFsTypeFile> files = content->Files();
912
913 for (const MtpFsTypeDir &d : dirs) {
914 struct stat st;
915 if (memset_s(&st, sizeof(st), 0, sizeof(st)) != EOK) {
916 LOGE("memset st fail");
917 }
918 st.st_ino = d.Id();
919 st.st_mode = S_IFDIR | PERMISSION_ONE;
920 filler(buf, d.Name().c_str(), &st, 0, fillFlags);
921 }
922
923 for (const MtpFsTypeFile &f : files) {
924 struct stat st;
925 if (memset_s(&st, sizeof(st), 0, sizeof(st)) != EOK) {
926 LOGE("memset st fail");
927 }
928 st.st_ino = f.Id();
929 st.st_mode = S_IFREG | PERMISSION_TWO;
930 filler(buf, f.Name().c_str(), &st, 0, fillFlags);
931 }
932 return 0;
933 }
934
ReleaseDir(const char * path,struct fuse_file_info * fileInfo)935 int MtpFileSystem::ReleaseDir(const char *path, struct fuse_file_info *fileInfo)
936 {
937 return 0;
938 }
939
FSyncDir(const char * path,int datasync,struct fuse_file_info * fileInfo)940 int MtpFileSystem::FSyncDir(const char *path, int datasync, struct fuse_file_info *fileInfo)
941 {
942 return 0;
943 }
944
HasPartialObjectSupport()945 bool MtpFileSystem::HasPartialObjectSupport()
946 {
947 MtpFsDevice::Capabilities caps = device_.GetCapabilities();
948 return (caps.CanGetPartialObject() && caps.CanSendPartialObject());
949 }
950
SetXAttr(const char * path,const char * in)951 int MtpFileSystem::SetXAttr(const char *path, const char *in)
952 {
953 if (path == nullptr || in == nullptr) {
954 LOGE("Param is null.");
955 return -ENOENT;
956 }
957 if (strcmp(in, "user.fetchcontent") == 0) {
958 LOGI("Refresh the mtp dir content, dir=%{public}s", path);
959 device_.RefreshDirContent(std::string(path));
960 return 0;
961 }
962 if (strcmp(in, "user.isUploadCompleted") != 0) {
963 LOGE("attrKey error, attrKey=%{public}s", in);
964 return -ENOENT;
965 }
966 device_.AddUploadRecord(std::string(path), false);
967 return 0;
968 }
969
GetXAttr(const char * path,const char * in,char * out,size_t size)970 int MtpFileSystem::GetXAttr(const char *path, const char *in, char *out, size_t size)
971 {
972 if (path == nullptr || in == nullptr) {
973 LOGE("Param is null.");
974 return 0;
975 }
976 if (strcmp(in, "user.isUploadCompleted") != 0) {
977 LOGE("attrKey error, attrKey=%{public}s", in);
978 return 0;
979 }
980 if (out == nullptr || size <= 0) {
981 return UPLOAD_RECORD_FALSE_LEN;
982 }
983 auto [firstParam, secondParam] = device_.FindUploadRecord(std::string(path));
984 if (firstParam.empty()) {
985 LOGE("No record, path=%{public}s", path);
986 return 0;
987 }
988 int ret;
989 if (secondParam) {
990 ret = memcpy_s(out, size, "true", UPLOAD_RECORD_TRUE_LEN);
991 if (ret != 0) {
992 LOGE("copy fail, ret=%{public}d", ret);
993 return 0;
994 }
995 device_.RemoveUploadRecord(path);
996 return UPLOAD_RECORD_TRUE_LEN;
997 } else {
998 ret = memcpy_s(out, size, "false", UPLOAD_RECORD_FALSE_LEN);
999 if (ret != 0) {
1000 LOGE("copy fail, ret=%{public}d", ret);
1001 return 0;
1002 }
1003 return UPLOAD_RECORD_FALSE_LEN;
1004 }
1005 }