• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }