1 /*
2  * Copyright (c) 2006-2025 RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2023-05-05     Bernard      Implement file APIs in dfs v2.0
9  */
10 
11 #include "errno.h"
12 #include "fcntl.h"
13 
14 #include <dfs.h>
15 
16 #include "dfs_file.h"
17 #include "dfs_dentry.h"
18 #include "dfs_fs.h"
19 #include "dfs_mnt.h"
20 #include "dfs_private.h"
21 
22 #ifdef RT_USING_PAGECACHE
23 #include "dfs_pcache.h"
24 #endif
25 
26 #define DBG_TAG    "DFS.file"
27 #define DBG_LVL    DBG_WARNING
28 #include <rtdbg.h>
29 
30 #define MAX_RW_COUNT 0xfffc0000
31 
32 /**
33  * @brief Get the length of the first path component
34  *
35  * This function calculates the length of the first path component in a given path string.
36  * For absolute paths (starting with '/'), it returns the length from the first '/' to
37  * the next '/' or end of string. For relative paths, it returns 0.
38  *
39  * @param[in] path The input path string to analyze
40  *
41  * @return int The length of the first path component, or 0 if not found
42  */
_first_path_len(const char * path)43 rt_inline int _first_path_len(const char *path)
44 {
45     int i = 0;
46 
47     if (path[i] == '/')
48     {
49         i++;
50         while (path[i] != '\0' && path[i] != '/')
51         {
52             i++;
53         }
54     }
55 
56     return i;
57 }
58 
59 /**
60  * @brief Get the parent directory path from a given full path
61  *
62  * This function extracts the parent directory path from a given full path string.
63  * It handles paths ending with '/' correctly by skipping the trailing slash.
64  *
65  * @param[in] fullpath The input full path string to analyze
66  * @param[out] path Buffer to store the extracted parent directory path
67  *
68  * @return int Length of the parent directory path, or 0 if no parent found
69  */
_get_parent_path(const char * fullpath,char * path)70 static int _get_parent_path(const char *fullpath, char *path)
71 {
72     int len = 0;
73     char *str = 0;
74 
75     char *full_path = rt_strdup(fullpath);
76     if (full_path == NULL)
77     {
78         rt_set_errno(ENOMEM);
79         return -1;
80     }
81 
82     str = strrchr(full_path, '/');
83 
84     /* skip last '/' */
85     if (str && *(str + 1) == '\0')
86     {
87         *str = '\0';
88         str = strrchr(full_path, '/');
89     }
90 
91     if (str)
92     {
93         len = str - full_path;
94         if (len > 0)
95         {
96             rt_memcpy(path, full_path, len);
97             path[len] = '\0';
98         }
99         else if (len == 0) /* parent path is root path. */
100         {
101             path[0] = '/';
102             path[1] = '\0';
103             len = 1;
104         }
105     }
106 
107     rt_free(full_path);
108     return len;
109 }
110 
111 /**
112  * @brief Attempt to read the target of a symbolic link
113  *
114  * This function tries to read the contents of a symbolic link file. It first looks up
115  * the dentry for the given path, checks if it's a symlink, and then calls the filesystem's
116  * readlink operation if available.
117  *
118  * @param[in] path The path of the symbolic link to read
119  * @param[in] mnt The mount point containing the symbolic link
120  * @param[out] link Buffer to store the link target (contents of the symlink)
121  *
122  * @return int Length of the link target on success, -1 on failure
123  */
_try_readlink(const char * path,struct dfs_mnt * mnt,char * link)124 static int _try_readlink(const char *path, struct dfs_mnt *mnt, char *link)
125 {
126     int ret = -1;
127     struct dfs_dentry *dentry = dfs_dentry_lookup(mnt, path, 0);
128 
129     if (dentry && dentry->vnode->type == FT_SYMLINK)
130     {
131         if (mnt->fs_ops->readlink)
132         {
133             if (dfs_is_mounted(mnt) == 0)
134             {
135                 ret = mnt->fs_ops->readlink(dentry, link, DFS_PATH_MAX);
136             }
137         }
138     }
139     dfs_dentry_unref(dentry);
140 
141     return ret;
142 }
143 
144 /**
145  * @brief Normalize a path by combining base path and link path
146  *
147  * This function creates a temporary path by combining the base path and link path,
148  * then normalizes it using dfs_normalize_path(). It handles memory allocation and
149  * cleanup internally.
150  *
151  * @param[in] path The base path to combine with the link
152  * @param[in] path_len Length of the base path
153  * @param[in] link_fn The link path to combine with the base path
154  * @param[in] link_len Length of the link path
155  *
156  * @return char* Normalized path string on success, RT_NULL on failure
157  *
158  * @note The caller is responsible for freeing the returned path
159  */
_dfs_normalize_path(const char * path,int path_len,const char * link_fn,int link_len)160 static char *_dfs_normalize_path(const char *path, int path_len, const char *link_fn, int link_len)
161 {
162     char *tmp_path, *fp;
163 
164     tmp_path = (char *)rt_malloc(path_len + link_len + 2);
165     if (!tmp_path)
166     {
167         return RT_NULL;
168     }
169 
170     memcpy(tmp_path, path, path_len);
171     tmp_path[path_len] = '/';
172     memcpy(tmp_path + path_len + 1, link_fn, link_len);
173     tmp_path[path_len + 1 + link_len] = '\0';
174 
175     fp = dfs_normalize_path(NULL, tmp_path);
176     rt_free(tmp_path);
177 
178     return fp;
179 }
180 
181 /**
182  * @brief Insert a link path into temporary path buffer
183  *
184  * This function inserts a symbolic link path into a temporary path buffer before
185  * the specified index position. It handles both relative and absolute paths.
186  *
187  * @param[in] link_fn The link path to insert
188  * @param[in] link_len Length of the link path
189  * @param[in,out] tmp_path The temporary path buffer to insert into
190  * @param[in,out] index Pointer to the insertion position index (updated after insertion)
191  *
192  * @return int 0 for relative path, 1 for absolute path, -1 on failure
193  * @note The index is modified to reflect the new insertion position
194  */
_insert_link_path(const char * link_fn,int link_len,char * tmp_path,int * index)195 static int _insert_link_path(const char *link_fn, int link_len, char *tmp_path, int *index)
196 {
197     int ret = -1;
198 
199     if (link_fn[0] != '/')
200     {
201         if (link_len + 1 <= *index)
202         {
203             *index -= link_len;
204             rt_memcpy(tmp_path + *index, link_fn, link_len);
205             *index -= 1;
206             tmp_path[*index] = '/';
207             ret = 0;
208         }
209     }
210     else if (link_len <= *index)
211     {
212         *index -= link_len;
213         rt_memcpy(tmp_path + *index, link_fn, link_len);
214         ret = 1;
215     }
216 
217     return ret;
218 }
219 
220 /**
221  * @brief Verify read/write area parameters and limit count size
222  *
223  * This function checks the validity of read/write parameters and limits the count
224  * to a maximum value (MAX_RW_COUNT) to prevent overflow. It ensures the position
225  * and count values are within valid ranges.
226  *
227  * @param[in] file Pointer to the file structure (unused in current implementation)
228  * @param[in] ppos Pointer to the position offset (input/output)
229  * @param[in] count Requested read/write count (input)
230  *
231  * @return ssize_t The verified count value (limited to MAX_RW_COUNT) or negative error code:
232  *         -EINVAL for invalid parameters
233  *         -EOVERFLOW if position + count would overflow
234  *
235  * @note rw_verify_area doesn't like huge counts. We limit them to something that fits in "int"
236  *       so that others won't have to do range checks all the time.
237  */
rw_verify_area(struct dfs_file * file,off_t * ppos,size_t count)238 ssize_t rw_verify_area(struct dfs_file *file, off_t *ppos, size_t count)
239 {
240     off_t pos;
241     ssize_t retval = -EINVAL;
242 
243     if ((size_t)count < 0)
244         return retval;
245     pos = *ppos;
246     if (pos < 0)
247     {
248         if (count >= -pos) /* both values are in 0..LLONG_MAX */
249             return -EOVERFLOW;
250     }
251 
252     return count > MAX_RW_COUNT ? MAX_RW_COUNT : count;
253 }
254 
255 /**
256  * @brief Get the current file position
257  *
258  * This function retrieves the current file position (offset) from the file structure.
259  * For regular files, it acquires a mutex lock before accessing the position to ensure
260  * thread safety. For other file types, it directly returns the position without locking.
261  *
262  * @param[in] file Pointer to the file structure containing position information
263  *
264  * @return off_t Current file position, or 0 if file pointer is NULL
265  */
dfs_file_get_fpos(struct dfs_file * file)266 off_t dfs_file_get_fpos(struct dfs_file *file)
267 {
268     if (file)
269     {
270         if (file->vnode->type == FT_REGULAR)
271         {
272             rt_mutex_take(&file->pos_lock, RT_WAITING_FOREVER);
273         }
274         return file->fpos;
275     }
276 
277     return 0;
278 }
279 
280 /**
281  * @brief Set the current file position
282  *
283  * This function sets the file position (offset) in the file structure.
284  * It must be used as a pair of dfs_file_get_fpos(). For regular files, pos lock is acquared
285  * in dfs_file_get_fpos(), so it can be released directly after setting the position.
286  * Otherwise, pos lock should be acquired first to avoid releasing it without being acquired.
287  *
288  * @param[in] file Pointer to the file structure to modify
289  * @param[in] fpos The new file position to set
290  */
dfs_file_set_fpos(struct dfs_file * file,off_t fpos)291 void dfs_file_set_fpos(struct dfs_file *file, off_t fpos)
292 {
293     if (file)
294     {
295         if (file->vnode->type != FT_REGULAR)
296         {
297             rt_mutex_take(&file->pos_lock, RT_WAITING_FOREVER);
298         }
299         file->fpos = fpos;
300         rt_mutex_release(&file->pos_lock);
301     }
302 }
303 
304 /**
305  * @brief Initialize a file structure
306  *
307  * @param[in,out] file Pointer to the file structure to be initialized
308  *
309  * @note This function must be called before using any file operations
310  *       on a newly allocated file structure
311  */
dfs_file_init(struct dfs_file * file)312 void dfs_file_init(struct dfs_file *file)
313 {
314     if (file)
315     {
316         rt_memset(file, 0x00, sizeof(struct dfs_file));
317         file->magic = DFS_FD_MAGIC;
318         rt_mutex_init(&file->pos_lock, "fpos", RT_IPC_FLAG_PRIO);
319         rt_atomic_store(&(file->ref_count), 1);
320     }
321 }
322 
323 /**
324  * @brief Deinitialize a file structure
325  *
326  * @param[in,out] file Pointer to the file structure to be deinitialized
327  */
dfs_file_deinit(struct dfs_file * file)328 void dfs_file_deinit(struct dfs_file *file)
329 {
330     if (file)
331     {
332         rt_mutex_detach(&file->pos_lock);
333     }
334 }
335 
336 /**
337  * @brief Decrement reference count and release file resources when count reaches zero
338  *
339  * This function safely decrements the reference count of a file structure and releases
340  * associated resources (dentry or vnode) when the reference count drops to zero.
341  *
342  * @param[in,out] file Pointer to the file structure to be unreferenced
343  */
dfs_file_unref(struct dfs_file * file)344 static void dfs_file_unref(struct dfs_file *file)
345 {
346     rt_err_t ret = RT_EOK;
347 
348     ret = dfs_file_lock();
349     if (ret == RT_EOK)
350     {
351         if (rt_atomic_load(&(file->ref_count)) == 1)
352         {
353             /* should release this file */
354             if (file->dentry)
355             {
356                 DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_unref(dentry(%s))", file->dentry->pathname);
357                 dfs_dentry_unref(file->dentry);
358                 file->dentry = RT_NULL;
359             }
360             else if (file->vnode)
361             {
362                 if (file->vnode->ref_count > 1)
363                 {
364                     rt_atomic_sub(&(file->vnode->ref_count), 1);
365                 }
366                 else if (file->vnode->ref_count == 1)
367                 {
368                     rt_free(file->vnode);
369                     file->vnode = RT_NULL;
370                 }
371             }
372 
373             LOG_I("release a file: %p", file);
374         }
375 
376         dfs_file_unlock();
377     }
378 }
379 
380 /**
381  * @brief Resolve the real path by resolving symbolic links and normalizing the path
382  *
383  * This function resolves the real path of a given file path by handling symbolic links
384  * and normalizing the path components. It supports two modes of operation:
385  * - DFS_REALPATH_EXCEPT_LAST: Resolve all path components except the last one
386  * - DFS_REALPATH_ONLY_LAST: Resolve only the last path component
387  * - DFS_REALPATH_EXCEPT_NONE: Resolve all path components
388  *
389  * @param[in,out] mnt Pointer to the mount point structure (updated if path changes)
390  * @param[in] fullpath The input path to resolve
391  * @param[in] mode Resolution mode (DFS_REALPATH_EXCEPT_LAST or DFS_REALPATH_ONLY_LAST)
392  *
393  * @return char* The resolved real path on success, RT_NULL on failure
394  */
dfs_file_realpath(struct dfs_mnt ** mnt,const char * fullpath,int mode)395 char *dfs_file_realpath(struct dfs_mnt **mnt, const char *fullpath, int mode)
396 {
397     int path_len = 0, index = 0;
398     char *path = RT_NULL, *link_fn, *tmp_path;
399     struct dfs_mnt *tmp_mnt;
400 
401     if (*mnt && fullpath)
402     {
403         int len, link_len;
404 
405         path = (char *)rt_malloc((DFS_PATH_MAX * 3) + 3); /* path + \0 + link_fn + \0 + tmp_path + \0 */
406         if (!path)
407         {
408             return RT_NULL;
409         }
410 
411         link_fn = path + DFS_PATH_MAX + 1;
412         tmp_path = link_fn + (DFS_PATH_MAX + 1);
413 
414         len = rt_strlen(fullpath);
415         if (len > DFS_PATH_MAX)
416         {
417             goto _ERR_RET;
418         }
419 
420         index = (DFS_PATH_MAX - len);
421         rt_strcpy(tmp_path + index, fullpath);
422 
423         if (mode == DFS_REALPATH_ONLY_LAST)
424         {
425             path_len = _get_parent_path(fullpath, path);
426             index += path_len;
427         }
428 
429         while ((len = _first_path_len(tmp_path + index)) > 0)
430         {
431             if (len + path_len > DFS_PATH_MAX)
432             {
433                 goto _ERR_RET;
434             }
435 
436             rt_memcpy(path + path_len, tmp_path + index, len);
437             path[path_len + len] = '\0';
438             index += len;
439 
440             tmp_mnt = dfs_mnt_lookup(path);
441             if (tmp_mnt == RT_NULL)
442             {
443                 goto _ERR_RET;
444             }
445 
446             *mnt = tmp_mnt;
447 
448             /* the last should by mode process. */
449             if ((tmp_path[index] == '\0') && (mode == DFS_REALPATH_EXCEPT_LAST))
450             {
451                 break;
452             }
453 
454             /* Process symbolic links if found */
455             link_len = _try_readlink(path, *mnt, link_fn);
456             if (link_len > 0)
457             {
458                 if (link_fn[0] == '/') /* Handle absolute path symlinks */
459                 {
460                     int ret = _insert_link_path(link_fn, link_len, tmp_path, &index);
461                     if (ret < 0)
462                     {
463                         goto _ERR_RET;
464                     }
465                     path_len = 0;
466                 }
467                 else /* Handle relative path symlinks */
468                 {
469                     char *fp = _dfs_normalize_path(path, path_len, link_fn, link_len);
470                     if (fp)
471                     {
472                         int pos = rt_strncmp(path, fp, path_len);
473                         if (pos == 0)
474                         {
475                             int ret = _insert_link_path(fp + path_len, rt_strlen(fp + path_len), tmp_path, &index);
476                             if (ret < 0)
477                             {
478                                 rt_free(fp);
479                                 goto _ERR_RET;
480                             }
481                         }
482                         else
483                         {
484                             int pos;
485 
486                             while(1)
487                             {
488                                 while(path_len > 0 && path[path_len] != '/')
489                                 {
490                                     path_len--;
491                                 }
492 
493                                 if (path_len > 0)
494                                 {
495                                     pos = rt_strncmp(path, fp, path_len);
496                                 }
497                                 else
498                                 {
499                                     pos = -1;
500                                 }
501 
502                                 if (pos == 0 || path_len == 0)
503                                 {
504                                     int ret;
505 
506                                     ret = _insert_link_path(fp + path_len, rt_strlen(fp + path_len), tmp_path, &index);
507                                     if (ret < 0)
508                                     {
509                                         rt_free(fp);
510                                         goto _ERR_RET;
511                                     }
512                                     else
513                                     {
514                                         break;
515                                     }
516                                 }
517                                 else
518                                 {
519                                     path_len--;
520                                 }
521                             }
522                         }
523                         rt_free(fp);
524                     }
525                 }
526             }
527             else
528             {
529                 path_len += len; /* Not a symlink, just advance path length */
530             }
531         }
532 
533         return path;
534 
535 _ERR_RET:
536         rt_free(path);
537         path = RT_NULL;
538     }
539 
540     return path;
541 }
542 
543 /**
544  * @brief Open a file which specified by path with specified oflags.
545  *
546  * This function opens or creates a file with given path and flags. It handles:
547  * - Path normalization and resolution (including symbolic links)
548  * - File creation when O_CREAT is specified
549  * - Permission checking
550  * - Directory vs regular file validation
551  * - Symbolic link following (unless O_NOFOLLOW is set)
552  *
553  * @param[in,out] file Pointer to file structure to be initialized
554  * @param[in] path the specified file path.
555  * @param[in] oflags the oflags for open operator. (O_RDONLY, O_WRONLY, O_CREAT, etc)
556  * @param[in] mode File permission mode (used when O_CREAT is specified)
557  *
558  * @return 0 on successful, -1 on failure:
559  *         -ENOENT if file doesn't exist and O_CREAT not set
560  *         -EEXIST if file exists and O_EXCL|O_CREAT set
561  *         -EPERM if permission denied
562  *         -ENOTDIR if path is not a directory when O_DIRECTORY set
563  *         -EISDIR if path is directory when opening as regular file
564  */
dfs_file_open(struct dfs_file * file,const char * path,int oflags,mode_t mode)565 int dfs_file_open(struct dfs_file *file, const char *path, int oflags, mode_t mode)
566 {
567     int ret = -RT_ERROR;
568     char *fullpath = RT_NULL;
569     struct dfs_dentry *dentry = RT_NULL;
570     int fflags = dfs_fflags(oflags);
571 
572     if (mode == 0)
573     {
574         mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); /* 0666 */
575     }
576 
577     if (file && path)
578     {
579         fullpath = dfs_normalize_path(NULL, path);
580         if (fullpath)
581         {
582             struct dfs_mnt *mnt = RT_NULL;
583 
584             DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", fullpath);
585             mnt = dfs_mnt_lookup(fullpath);
586             if (mnt)
587             {
588                 char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST);
589                 if (tmp)
590                 {
591                     rt_free(fullpath);
592                     fullpath = tmp;
593                 }
594 
595                 DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_lookup(mnt, %s)", fullpath);
596                 dentry = dfs_dentry_lookup(mnt, fullpath, oflags);
597                 if (dentry && dentry->vnode->type == FT_SYMLINK)
598                 {
599                     /* it's a symbol link but not follow */
600                     if (oflags & O_NOFOLLOW)
601                     {
602                         /* no follow symbol link */
603                         dfs_dentry_unref(dentry);
604                         dentry = RT_NULL;
605                     }
606                     else
607                     {
608                         struct dfs_dentry *target_dentry = RT_NULL;
609                         char *path = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_ONLY_LAST);
610                         if (path)
611                         {
612                             target_dentry = dfs_dentry_lookup(mnt, path, oflags);
613                             rt_free(path);
614                         }
615                         dfs_dentry_unref(dentry);
616                         dentry = target_dentry;
617                     }
618                 }
619 
620                 if (dentry)
621                 {
622                     if (oflags & O_DIRECTORY)
623                     {
624                         if (dentry->vnode->type != FT_DIRECTORY)
625                         {
626                             dfs_dentry_unref(dentry);
627                             dentry = RT_NULL;
628                         }
629                     }
630                     else if (dentry->vnode->type == FT_DIRECTORY)
631                     {
632                         if (fflags & (DFS_F_FWRITE))
633                         {
634                             dfs_dentry_unref(dentry);
635                             dentry = RT_NULL;
636                         }
637                         else
638                         {
639                             oflags |= O_DIRECTORY;
640                         }
641                     }
642                 }
643 
644                 if (oflags & O_CREAT)
645                 {
646                     if (dentry)
647                     {
648                         oflags &= ~O_CREAT;
649 
650                         if (oflags & O_EXCL)
651                         {
652                             oflags &= ~O_EXCL;
653                             /* the dentry already exists */
654                             dfs_dentry_unref(dentry);
655                             ret = -EEXIST;
656                             goto _ERR_RET;
657                         }
658                     }
659                     else
660                     {
661                         /* create file/directory */
662                         if (mnt->fs_ops->create_vnode)
663                         {
664                             struct dfs_vnode *vnode = RT_NULL;
665 
666                             DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_create(%s)", fullpath);
667                             dfs_file_lock();
668                             dentry = dfs_dentry_create(mnt, fullpath);
669                             if (dentry)
670                             {
671                                 DLOG(msg, "dfs_file", mnt->fs_ops->name, DLOG_MSG, "fs_ops->create_vnode");
672 
673                                 if (dfs_is_mounted(mnt) == 0)
674                                 {
675                                     vnode = mnt->fs_ops->create_vnode(dentry, oflags & O_DIRECTORY ? FT_DIRECTORY:FT_REGULAR, mode);
676                                 }
677 
678                                 if (vnode)
679                                 {
680                                     /* set vnode */
681                                     dentry->vnode = vnode;  /* the refcount of created vnode is 1. no need to reference */
682                                     dfs_dentry_insert(dentry);
683                                 }
684                                 else
685                                 {
686                                     DLOG(msg, mnt->fs_ops->name, "dfs_file", DLOG_MSG_RET, "create failed.");
687                                     dfs_dentry_unref(dentry);
688                                     dentry = RT_NULL;
689                                 }
690                             }
691                             dfs_file_unlock();
692                         }
693                     }
694                 }
695 
696                 if (dentry)
697                 {
698                     rt_bool_t permission = RT_TRUE;
699                     file->dentry = dentry;
700                     file->vnode = dentry->vnode;
701                     file->fops  = dentry->mnt->fs_ops->default_fops;
702                     file->flags = oflags;
703 
704                     /* check permission */
705                     if (!(oflags & O_CREAT))
706                     {
707                         if (fflags & DFS_F_FWRITE)
708                         {
709                             if (!(file->vnode->mode & S_IWUSR))
710                             {
711                                 permission = RT_FALSE;
712                             }
713                         }
714 
715                         if (fflags & DFS_F_FREAD)
716                         {
717                             if (!(file->vnode->mode & S_IRUSR))
718                             {
719                                 permission = RT_FALSE;
720                             }
721                         }
722 
723                         if (oflags & O_EXEC)
724                         {
725                             if (!(file->vnode->mode & S_IXUSR))
726                             {
727                                 permission = RT_FALSE;
728                             }
729                         }
730                     }
731 
732                     if (permission && file->fops->open)
733                     {
734                         DLOG(msg, "dfs_file", mnt->fs_ops->name, DLOG_MSG, "fops->open(file)");
735 
736                         if (dfs_is_mounted(file->vnode->mnt) == 0)
737                         {
738                             dfs_file_lock();
739                             ret = file->fops->open(file);
740                             dfs_file_unlock();
741                         }
742                         else
743                         {
744                             ret = -EINVAL;
745                         }
746 
747                         if (ret < 0)
748                         {
749                             LOG_I("open %s failed in file system: %s", path, dentry->mnt->fs_ops->name);
750                             DLOG(msg, mnt->fs_ops->name, "dfs_file", DLOG_MSG_RET, "open failed.");
751                             dfs_file_unref(file);
752                         }
753                         else
754                         {
755                             /* for char/block device */
756                             if ((S_ISCHR(file->vnode->mode)) || (S_ISBLK(file->vnode->mode)))
757                             {
758                                 file->fops = file->vnode->fops;
759                             }
760                         }
761                     }
762                     else
763                     {
764                         DLOG(msg, "dfs_file", mnt->fs_ops->name, DLOG_MSG, "no permission or fops->open");
765                         dfs_file_unref(file);
766                         ret = -EPERM;
767                     }
768                 }
769                 else
770                 {
771                     LOG_I("lookup file:%s failed in file system", path);
772                     ret = -ENOENT;
773                 }
774             }
775         }
776 
777         if (ret >= 0 && (oflags & O_TRUNC))
778         {
779             /* trunc file */
780             if (!(fflags & DFS_F_FWRITE) || file->vnode->type == FT_DIRECTORY)
781             {
782                 /* truncate on read a only file or a directory */
783                 DLOG(msg, "dfs_file", "dfs_file", DLOG_MSG, "dfs_file_unref(file), trunc on RDOnly or directory");
784                 ret = -RT_ERROR;
785             }
786             else
787             {
788                 if (file->fops->truncate)
789                 {
790                     DLOG(msg, "dfs_file", dentry->mnt->fs_ops->name, DLOG_MSG, "fops->truncate(file, 0)");
791 
792                     if (dfs_is_mounted(file->vnode->mnt) == 0)
793                     {
794 #ifdef RT_USING_PAGECACHE
795                         if (file->vnode->aspace)
796                         {
797                             dfs_aspace_clean(file->vnode->aspace);
798                         }
799 #endif
800                         ret = file->fops->truncate(file, 0);
801                     }
802                     else
803                     {
804                         ret = -EINVAL;
805                     }
806                 }
807             }
808 
809             if (ret < 0)
810             {
811                 dfs_file_unref(file);
812             }
813 
814             file->flags &= ~O_TRUNC;
815         }
816     }
817 
818 _ERR_RET:
819     if (fullpath != NULL)
820     {
821         rt_free(fullpath);
822     }
823     return ret;
824 }
825 
826 /**
827  * @brief Close a file and release associated resources
828  *
829  * This function closes a file and performs necessary cleanup operations:
830  * - Flushes page cache if enabled (RT_USING_PAGECACHE)
831  * - Calls filesystem-specific close operation if available
832  * - Decrements reference count and releases resources when count reaches zero
833  *
834  * @param[in,out] file Pointer to the file structure to close
835  *
836  * @return int Operation result:
837  *         - 0 on success
838  *         - Negative error code on failure
839  */
dfs_file_close(struct dfs_file * file)840 int dfs_file_close(struct dfs_file *file)
841 {
842     int ret = -RT_ERROR;
843 
844     if (file)
845     {
846         if (dfs_file_lock() == RT_EOK)
847         {
848             rt_atomic_t ref_count = rt_atomic_load(&(file->ref_count));
849 
850             if (ref_count == 1 && file->fops && file->fops->close)
851             {
852                 DLOG(msg, "dfs_file", file->dentry->mnt->fs_ops->name, DLOG_MSG, "fops->close(file)");
853 #ifdef RT_USING_PAGECACHE
854                 if (file->vnode->aspace)
855                 {
856                     dfs_aspace_flush(file->vnode->aspace);
857                 }
858 #endif
859                 ret = file->fops->close(file);
860 
861                 if (ret == 0) /* close file sucessfully */
862                 {
863                     DLOG(msg, "dfs_file", "dfs_file", DLOG_MSG, "dfs_file_unref(file)");
864                     dfs_file_unref(file);
865                 }
866                 else
867                 {
868                     LOG_W("close file:%s failed on low level file system", file->dentry->pathname);
869                 }
870             }
871             else
872             {
873                 DLOG(msg, "dfs_file", "dfs_file", DLOG_MSG, "dfs_file_unref(file)");
874                 dfs_file_unref(file);
875                 ret = 0;
876             }
877             dfs_file_unlock();
878         }
879     }
880 
881     return ret;
882 }
883 
884 /**
885  * @brief Read data from a file at specified offset
886  *
887  * @param[in] file Pointer to the file structure to read from
888  * @param[out] buf Buffer to store the read data
889  * @param[in] len Number of bytes to read
890  * @param[in] offset Offset in the file to start reading from
891  *
892  * @return ssize_t Number of bytes read on success, or negative error code:
893  *         -EBADF if invalid file descriptor
894  *         -EPERM if read permission denied
895  *         -ENOSYS if read operation not supported
896  *         -EINVAL if invalid parameters or not mounted
897  */
dfs_file_pread(struct dfs_file * file,void * buf,size_t len,off_t offset)898 ssize_t dfs_file_pread(struct dfs_file *file, void *buf, size_t len, off_t offset)
899 {
900     ssize_t ret = -EBADF;
901 
902     if (file)
903     {
904         /* check whether read */
905         if (!(dfs_fflags(file->flags) & DFS_F_FREAD))
906         {
907             ret = -EPERM;
908         }
909         else if (!file->fops || !file->fops->read)
910         {
911             ret = -ENOSYS;
912         }
913         else if (file->vnode && file->vnode->type != FT_DIRECTORY)
914         {
915             off_t pos = offset;
916 
917             ret = rw_verify_area(file, &pos, len);
918             if (ret > 0)
919             {
920                 len = ret;
921 
922                 if (dfs_is_mounted(file->vnode->mnt) == 0)
923                 {
924 #ifdef RT_USING_PAGECACHE
925                     if (file->vnode->aspace && !(file->flags & O_DIRECT))
926                     {
927                         ret = dfs_aspace_read(file, buf, len, &pos);
928                     }
929                     else
930 #endif
931                     {
932                         ret = file->fops->read(file, buf, len, &pos);
933                     }
934                 }
935                 else
936                 {
937                     ret = -EINVAL;
938                 }
939             }
940         }
941     }
942 
943     return ret;
944 }
945 
946 /**
947  * @brief Read data from a file at current position
948  *
949  * @param[in] file Pointer to the file structure to read from
950  * @param[out] buf Buffer to store the read data
951  * @param[in] len Number of bytes to read
952  *
953  * @return ssize_t Number of bytes read on success, or negative error code:
954  *         -EBADF if invalid file descriptor
955  *         -EPERM if read permission denied
956  *         -ENOSYS if read operation not supported
957  *         -EINVAL if invalid parameters or not mounted
958  */
dfs_file_read(struct dfs_file * file,void * buf,size_t len)959 ssize_t dfs_file_read(struct dfs_file *file, void *buf, size_t len)
960 {
961     ssize_t ret = -EBADF;
962 
963     if (file)
964     {
965         /* check whether read */
966         if (!(dfs_fflags(file->flags) & DFS_F_FREAD))
967         {
968             ret = -EPERM;
969         }
970         else if (!file->fops || !file->fops->read)
971         {
972             ret = -ENOSYS;
973         }
974         else if (file->vnode && file->vnode->type != FT_DIRECTORY)
975         {
976             /* fpos lock */
977             off_t pos = dfs_file_get_fpos(file);
978 
979             ret = rw_verify_area(file, &pos, len);
980             if (ret > 0)
981             {
982                 len = ret;
983 
984                 if (dfs_is_mounted(file->vnode->mnt) == 0)
985                 {
986 #ifdef RT_USING_PAGECACHE
987                     if (file->vnode->aspace && !(file->flags & O_DIRECT))
988                     {
989                         ret = dfs_aspace_read(file, buf, len, &pos);
990                     }
991                     else
992 #endif
993                     {
994                         ret = file->fops->read(file, buf, len, &pos);
995                     }
996                 }
997                 else
998                 {
999                     ret = -EINVAL;
1000                 }
1001             }
1002             /* fpos unlock */
1003             dfs_file_set_fpos(file, pos);
1004         }
1005     }
1006 
1007     return ret;
1008 }
1009 
1010 /**
1011  * @brief Write data to a file at specified offset
1012  *
1013  * @param[in] file Pointer to the file structure to write to
1014  * @param[in] buf Buffer containing data to write
1015  * @param[in] len Number of bytes to write
1016  * @param[in] offset Offset in the file to start writing from
1017  *
1018  * @return ssize_t Number of bytes written on success, or negative error code:
1019  *         -EBADF if invalid file descriptor or bad write flags
1020  *         -ENOSYS if write operation not supported
1021  *         -EINVAL if invalid parameters or not mounted
1022  *
1023  * @note If O_SYNC flag is set, the data will be immediately flushed to storage device
1024  *       after write operation.
1025  */
dfs_file_pwrite(struct dfs_file * file,const void * buf,size_t len,off_t offset)1026 ssize_t dfs_file_pwrite(struct dfs_file *file, const void *buf, size_t len, off_t offset)
1027 {
1028     ssize_t ret = -EBADF;
1029 
1030     if (file)
1031     {
1032         if (!(dfs_fflags(file->flags) & DFS_F_FWRITE))
1033         {
1034             LOG_W("bad write flags.");
1035             ret = -EBADF;
1036         }
1037         else if (!file->fops || !file->fops->write)
1038         {
1039             LOG_W("no fops write.");
1040             ret = -ENOSYS;
1041         }
1042         else if (file->vnode && file->vnode->type != FT_DIRECTORY)
1043         {
1044             off_t pos = offset;
1045 
1046             ret = rw_verify_area(file, &pos, len);
1047             if (ret > 0)
1048             {
1049                 len = ret;
1050                 DLOG(msg, "dfs_file", file->dentry->mnt->fs_ops->name, DLOG_MSG,
1051                     "dfs_file_write(fd, buf, %d)", len);
1052 
1053                 if (dfs_is_mounted(file->vnode->mnt) == 0)
1054                 {
1055 #ifdef RT_USING_PAGECACHE
1056                     if (file->vnode->aspace && !(file->flags & O_DIRECT))
1057                     {
1058                         ret = dfs_aspace_write(file, buf, len, &pos);
1059                     }
1060                     else
1061 #endif
1062                     {
1063                         ret = file->fops->write(file, buf, len, &pos);
1064                     }
1065 
1066                     if (file->flags & O_SYNC)
1067                     {
1068                         file->fops->flush(file);
1069                     }
1070                 }
1071                 else
1072                 {
1073                     ret = -EINVAL;
1074                 }
1075             }
1076         }
1077     }
1078 
1079     return ret;
1080 }
1081 
1082 /**
1083  * @brief Write data to a file at current position
1084  *
1085  * This function writes data to a file at the current position or at the end if O_APPEND flag is set.
1086  *
1087  * @param[in,out] file Pointer to the file structure to write to
1088  * @param[in] buf Buffer containing data to write
1089  * @param[in] len Number of bytes to write
1090  *
1091  * @return ssize_t Number of bytes written on success, or negative error code:
1092  *         -EBADF if invalid file descriptor or bad write flags
1093  *         -ENOSYS if write operation not supported
1094  *         -EINVAL if invalid parameters or not mounted
1095  *
1096  * @note If O_SYNC flag is set, the data will be immediately flushed to storage device
1097  * @note In append mode (O_APPEND), data is always written at the end of file
1098  */
dfs_file_write(struct dfs_file * file,const void * buf,size_t len)1099 ssize_t dfs_file_write(struct dfs_file *file, const void *buf, size_t len)
1100 {
1101     ssize_t ret = -EBADF;
1102 
1103     if (file)
1104     {
1105         if (!(dfs_fflags(file->flags) & DFS_F_FWRITE))
1106         {
1107             LOG_W("bad write flags.");
1108             ret = -EBADF;
1109         }
1110         else if (!file->fops || !file->fops->write)
1111         {
1112             LOG_W("no fops write.");
1113             ret = -ENOSYS;
1114         }
1115         else if (file->vnode && file->vnode->type != FT_DIRECTORY)
1116         {
1117             off_t pos;
1118 
1119             if (!(file->flags & O_APPEND))
1120             {
1121                 /* fpos lock */
1122                 pos = dfs_file_get_fpos(file);
1123             }
1124             else
1125             {
1126                 pos = file->vnode->size;
1127             }
1128 
1129             ret = rw_verify_area(file, &pos, len);
1130             if (ret > 0)
1131             {
1132                 len = ret;
1133                 DLOG(msg, "dfs_file", file->dentry->mnt->fs_ops->name, DLOG_MSG,
1134                     "dfs_file_write(fd, buf, %d)", len);
1135 
1136                 if (dfs_is_mounted(file->vnode->mnt) == 0)
1137                 {
1138 #ifdef RT_USING_PAGECACHE
1139                     if (file->vnode->aspace && !(file->flags & O_DIRECT))
1140                     {
1141                         ret = dfs_aspace_write(file, buf, len, &pos);
1142                     }
1143                     else
1144 #endif
1145                     {
1146                         ret = file->fops->write(file, buf, len, &pos);
1147                     }
1148 
1149                     if (file->flags & O_SYNC)
1150                     {
1151                         file->fops->flush(file);
1152                     }
1153                 }
1154                 else
1155                 {
1156                     ret = -EINVAL;
1157                 }
1158             }
1159             if (!(file->flags & O_APPEND))
1160             {
1161                 /* fpos unlock */
1162                 dfs_file_set_fpos(file, pos);
1163             }
1164         }
1165     }
1166 
1167     return ret;
1168 }
1169 
1170 /**
1171  * @brief Generic file seek implementation
1172  *
1173  * This function calculates the new file position based on the specified offset and whence parameter.
1174  * It supports three seek modes:
1175  * - SEEK_SET: Set position relative to start of file
1176  * - SEEK_CUR: Set position relative to current position
1177  * - SEEK_END: Set position relative to end of file
1178  *
1179  * @param[in] file Pointer to the file structure containing current position
1180  * @param[in] offset Offset value to seek
1181  * @param[in] whence Seek mode (SEEK_SET/SEEK_CUR/SEEK_END)
1182  *
1183  * @return off_t The calculated new file position, or -EINVAL for invalid whence
1184  */
generic_dfs_lseek(struct dfs_file * file,off_t offset,int whence)1185 off_t generic_dfs_lseek(struct dfs_file *file, off_t offset, int whence)
1186 {
1187     off_t foffset;
1188 
1189     if (whence == SEEK_SET)
1190         foffset = offset;
1191     else if (whence == SEEK_CUR)
1192         foffset = file->fpos + offset;
1193     else if (whence == SEEK_END)
1194         foffset = file->vnode->size + offset;
1195     else
1196         return -EINVAL;
1197 
1198     return foffset;
1199 }
1200 
1201 /**
1202  * @brief Change the file position indicator
1203  *
1204  * This function sets the file position indicator for the file referenced by the file descriptor
1205  * based on the offset and whence parameters.
1206  *
1207  * @param[in,out] file Pointer to the file structure (position will be modified)
1208  * @param[in] offset Number of bytes to offset from position
1209  * @param[in] whence Reference position (SEEK_SET/SEEK_CUR/SEEK_END)
1210  *
1211  * @return off_t New file position on success, or negative error code:
1212  *         -EINVAL if invalid parameters or not mounted
1213  */
dfs_file_lseek(struct dfs_file * file,off_t offset,int wherece)1214 off_t dfs_file_lseek(struct dfs_file *file, off_t offset, int wherece)
1215 {
1216     off_t retval = -EINVAL;
1217 
1218     if (file && file->fops->lseek)
1219     {
1220         if (dfs_is_mounted(file->vnode->mnt) == 0)
1221         {
1222             /* fpos lock */
1223             off_t pos = dfs_file_get_fpos(file);
1224             retval = file->fops->lseek(file, offset, wherece);
1225             if (retval >= 0)
1226             {
1227                 pos = retval;
1228             }
1229             /* fpos unlock */
1230             dfs_file_set_fpos(file, pos);
1231         }
1232     }
1233 
1234     return retval;
1235 }
1236 
1237 /**
1238  * @brief Get file status information
1239  *
1240  * @param[in] path The file path to get status for
1241  * @param[out] buf Pointer to stat structure to store the status information
1242  *
1243  * @return int Operation result:
1244  *         - 0 on success
1245  *         -ENOENT if file not found
1246  *         -ENOMEM if memory allocation failed
1247  *         Other negative error codes from filesystem operations
1248  */
dfs_file_stat(const char * path,struct stat * buf)1249 int dfs_file_stat(const char *path, struct stat *buf)
1250 {
1251     int ret = -ENOENT;
1252     char *fullpath = RT_NULL;
1253     struct dfs_mnt *mnt = RT_NULL;
1254     struct dfs_dentry *dentry = RT_NULL;
1255 
1256     fullpath = dfs_normalize_path(NULL, path);
1257     if (fullpath)
1258     {
1259         DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", fullpath);
1260         mnt = dfs_mnt_lookup(fullpath);
1261         if (mnt)
1262         {
1263             char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_NONE);
1264             if (tmp)
1265             {
1266                 rt_free(fullpath);
1267                 fullpath = tmp;
1268             }
1269 
1270             DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dentry = dfs_dentry_lookup(mnt, %s)", fullpath);
1271             dentry = dfs_dentry_lookup(mnt, fullpath, 0);
1272             if (dentry)
1273             {
1274                 DLOG(msg, "dentry", "dfs_file", DLOG_MSG_RET, "return dentry");
1275                 if (mnt->fs_ops->stat)
1276                 {
1277                     DLOG(msg, "dfs_file", mnt->fs_ops->name, DLOG_MSG, "fs_ops->stat(dentry, buf)");
1278 
1279                     if (dfs_is_mounted(mnt) == 0)
1280                     {
1281                         ret = mnt->fs_ops->stat(dentry, buf);
1282                     }
1283                 }
1284 
1285                 /* unref dentry */
1286                 DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_unref(dentry)");
1287                 dfs_dentry_unref(dentry);
1288                 dentry = RT_NULL;
1289             }
1290         }
1291 
1292         rt_free(fullpath);
1293         fullpath = RT_NULL;
1294     }
1295     else
1296     {
1297         ret = -ENOMEM;
1298     }
1299 
1300     return ret;
1301 }
1302 
1303 /**
1304  * @brief Get file status information without following symbolic links
1305  *
1306  * @param[in] path The file path to get status for (does not follow symlinks)
1307  * @param[out] buf Pointer to stat structure to store the status information
1308  *
1309  * @return int Operation result:
1310  *         - 0 on success
1311  *         -ENOENT if file not found
1312  *         -ENOMEM if memory allocation failed
1313  *         Other negative error codes from filesystem operations
1314  *
1315  * @note Unlike dfs_file_stat(), this function does not follow symbolic links
1316  * @see dfs_file_stat()
1317  */
dfs_file_lstat(const char * path,struct stat * buf)1318 int dfs_file_lstat(const char *path, struct stat *buf)
1319 {
1320     int ret = -ENOENT;
1321     char *fullpath = RT_NULL;
1322     struct dfs_mnt *mnt = RT_NULL;
1323     struct dfs_dentry *dentry = RT_NULL;
1324 
1325     fullpath = dfs_normalize_path(NULL, path);
1326     if (fullpath)
1327     {
1328         DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", fullpath);
1329         mnt = dfs_mnt_lookup(fullpath);
1330         if (mnt)
1331         {
1332             char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST);
1333             if (tmp)
1334             {
1335                 rt_free(fullpath);
1336                 fullpath = tmp;
1337             }
1338 
1339             DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dentry = dfs_dentry_lookup(mnt, %s)", fullpath);
1340             dentry = dfs_dentry_lookup(mnt, fullpath, 0);
1341             if (dentry)
1342             {
1343                 DLOG(msg, "dentry", "dfs_file", DLOG_MSG_RET, "return dentry");
1344                 if (mnt->fs_ops->stat)
1345                 {
1346                     DLOG(msg, "dfs_file", mnt->fs_ops->name, DLOG_MSG, "fs_ops->stat(dentry, buf)");
1347 
1348                     if (dfs_is_mounted(mnt) == 0)
1349                     {
1350                         ret = mnt->fs_ops->stat(dentry, buf);
1351                     }
1352                 }
1353 
1354                 /* unref dentry */
1355                 DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_unref(dentry)");
1356                 dfs_dentry_unref(dentry);
1357                 dentry = RT_NULL;
1358             }
1359         }
1360 
1361         rt_free(fullpath);
1362         fullpath = RT_NULL;
1363     }
1364     else
1365     {
1366         ret = -ENOMEM;
1367     }
1368 
1369     rt_set_errno(-ret);
1370 
1371     return ret;
1372 }
1373 
1374 /**
1375  * @brief Get file status information using file descriptor
1376  *
1377  * @param[in] file Pointer to the open file structure
1378  * @param[out] buf Pointer to stat structure to store status information
1379  *
1380  * @return int Operation result:
1381  *         - 0 on success
1382  *         -EBADF if invalid file descriptor
1383  *         -ENOSYS if operation not supported
1384  *
1385  * @note Currently unimplemented (returns -ENOSYS)
1386  */
dfs_file_fstat(struct dfs_file * file,struct stat * buf)1387 int dfs_file_fstat(struct dfs_file *file, struct stat *buf)
1388 {
1389     size_t ret = -EBADF;
1390 
1391     if (file)
1392     {
1393         if (file->fops && file->fops->ioctl)
1394         {
1395             /* ret = file->fops->fstat(file, buf); */
1396         }
1397         else
1398         {
1399             ret = -ENOSYS;
1400         }
1401     }
1402     else
1403     {
1404         ret = -EBADF;
1405     }
1406 
1407     return ret;
1408 }
1409 
1410 /**
1411  * @brief Set file attributes for the specified path
1412  *
1413  * This function sets file attributes (permissions, ownership, timestamps, etc.)
1414  * for the file specified by path.
1415  *
1416  * @param[in] path The file path to set attributes for
1417  * @param[in] attr Pointer to attribute structure containing new attributes
1418  *
1419  * @return int Operation result:
1420  *         - 0 on success
1421  *         -RT_ERROR if general error occurred
1422  *         -ENOENT if file not found
1423  *         Other negative error codes from filesystem operations
1424  *
1425  * @note The actual supported attributes depend on the underlying filesystem
1426  */
dfs_file_setattr(const char * path,struct dfs_attr * attr)1427 int dfs_file_setattr(const char *path, struct dfs_attr *attr)
1428 {
1429     int ret = -RT_ERROR;
1430     char *fullpath = RT_NULL;
1431     struct dfs_mnt *mnt = RT_NULL;
1432     struct dfs_dentry *dentry = RT_NULL;
1433 
1434     fullpath = dfs_normalize_path(NULL, path);
1435     if (fullpath)
1436     {
1437         DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", fullpath);
1438         mnt = dfs_mnt_lookup(fullpath);
1439         if (mnt)
1440         {
1441             char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST);
1442             if (tmp)
1443             {
1444                 rt_free(fullpath);
1445                 fullpath = tmp;
1446             }
1447 
1448             DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dentry = dfs_dentry_lookup(mnt, %s)", fullpath);
1449             dentry = dfs_dentry_lookup(mnt, fullpath, 0);
1450             if (dentry)
1451             {
1452                 DLOG(msg, "dentry", "dfs_file", DLOG_MSG_RET, "return dentry");
1453                 if (mnt->fs_ops->setattr)
1454                 {
1455                     DLOG(msg, "dfs_file", mnt->fs_ops->name, DLOG_MSG, "fs_ops->setattr(dentry, attr)");
1456 
1457                     if (dfs_is_mounted(mnt) == 0)
1458                     {
1459                         ret = mnt->fs_ops->setattr(dentry, attr);
1460                     }
1461                 }
1462 
1463                 /* unref dentry */
1464                 DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_unref(dentry)");
1465                 dfs_dentry_unref(dentry);
1466                 dentry = RT_NULL;
1467             }
1468         }
1469 
1470         rt_free(fullpath);
1471         fullpath = RT_NULL;
1472     }
1473 
1474     return ret;
1475 }
1476 
1477 /**
1478  * @brief Perform device-specific control operations
1479  *
1480  * This function performs device-specific control operations on an open file descriptor.
1481  * It is typically used for operations that cannot be expressed by regular file operations.
1482  *
1483  * @param[in] file Pointer to the file structure to perform ioctl on
1484  * @param[in] cmd Device-dependent request code
1485  * @param[in,out] args Pointer to optional argument buffer (input/output depends on cmd)
1486  *
1487  * @return int Operation result:
1488  *         - 0 or positive value on success (meaning depends on cmd)
1489  *         -EBADF if invalid file descriptor
1490  *         -ENOSYS if ioctl operation not supported
1491  *         -EINVAL if invalid parameters or not mounted
1492  *
1493  * @note The actual supported commands and their semantics depend on the underlying device driver
1494  */
dfs_file_ioctl(struct dfs_file * file,int cmd,void * args)1495 int dfs_file_ioctl(struct dfs_file *file, int cmd, void *args)
1496 {
1497     size_t ret = 0;
1498 
1499     if (file)
1500     {
1501         if (file->fops && file->fops->ioctl)
1502         {
1503             if (dfs_is_mounted(file->vnode->mnt) == 0)
1504             {
1505                 ret = file->fops->ioctl(file, cmd, args);
1506             }
1507             else
1508             {
1509                 ret = -EINVAL;
1510             }
1511         }
1512         else
1513         {
1514             ret = -ENOSYS;
1515         }
1516     }
1517     else
1518     {
1519         ret = -EBADF;
1520     }
1521 
1522     return ret;
1523 }
1524 
1525 /**
1526  * @brief Perform file control operations
1527  *
1528  * This function performs various control operations on an open file descriptor.
1529  * It supports the following operations:
1530  * - F_DUPFD: Duplicate file descriptor
1531  * - F_GETFD: Get file descriptor flags
1532  * - F_SETFD: Set file descriptor flags
1533  * - F_GETFL: Get file status flags
1534  * - F_SETFL: Set file status flags
1535  * - F_GETLK/F_SETLK/F_SETLKW: File locking operations (unimplemented)
1536  * - F_DUPFD_CLOEXEC: Duplicate file descriptor with close-on-exec flag (if supported)
1537  *
1538  * @param[in] fd File descriptor to operate on
1539  * @param[in] cmd Control command (F_DUPFD/F_GETFD/F_SETFD/F_GETFL/F_SETFL/etc)
1540  * @param[in,out] arg Command-specific argument (input/output depends on cmd)
1541  *
1542  * @return int Operation result:
1543  *         - For F_DUPFD: new file descriptor on success
1544  *         - For F_GETFD/F_GETFL: current flags on success
1545  *         - 0 on success for other commands
1546  *         -EBADF if invalid file descriptor
1547  *         -EINVAL if invalid command (F_DUPFD_CLOEXEC when not supported)
1548  *         -EPERM for unsupported commands
1549  *
1550  * @note Not all commands may be supported by all filesystems
1551  * @note File locking operations (F_GETLK/F_SETLK/F_SETLKW) are currently unimplemented
1552  */
dfs_file_fcntl(int fd,int cmd,unsigned long arg)1553 int dfs_file_fcntl(int fd, int cmd, unsigned long arg)
1554 {
1555     int ret = 0;
1556     struct dfs_file *file;
1557 
1558     file = fd_get(fd);
1559     if (file)
1560     {
1561         switch (cmd)
1562         {
1563         case F_DUPFD:
1564             ret = dfs_dup(fd, arg);
1565             break;
1566         case F_GETFD:
1567             ret = file->mode;
1568             break;
1569         case F_SETFD:
1570             file->mode = arg;
1571             break;
1572         case F_GETFL:
1573             ret = file->flags;
1574             break;
1575         case F_SETFL:
1576         {
1577             int flags = (int)(rt_base_t)arg;
1578             int mask =
1579 #ifdef O_ASYNC
1580                         O_ASYNC |
1581 #endif
1582 #ifdef O_DIRECT
1583                         O_DIRECT |
1584 #endif
1585 #ifdef O_NOATIME
1586                         O_NOATIME |
1587 #endif
1588                         O_APPEND | O_NONBLOCK;
1589 
1590             flags &= mask;
1591             file->flags &= ~mask;
1592             file->flags |= flags;
1593             break;
1594         }
1595         case F_GETLK:
1596             break;
1597         case F_SETLK:
1598         case F_SETLKW:
1599             break;
1600 #ifdef RT_USING_MUSLLIBC
1601         case F_DUPFD_CLOEXEC:
1602             ret = -EINVAL;
1603             break;
1604 #endif
1605         default:
1606             ret = -EPERM;
1607             break;
1608         }
1609     }
1610     else
1611     {
1612         ret = -EBADF;
1613     }
1614 
1615     return ret;
1616 }
1617 
1618 /**
1619  * @brief Synchronize file data to storage device
1620  *
1621  * This function flushes all modified file data and metadata to the underlying storage device.
1622  * It ensures data integrity by:
1623  * - Flushing page cache if enabled (RT_USING_PAGECACHE)
1624  * - Calling filesystem-specific flush operation
1625  *
1626  * @param[in] file Pointer to the file structure to synchronize
1627  *
1628  * @return int Operation result:
1629  *         - 0 on success
1630  *         -EBADF if invalid file descriptor
1631  *         -EINVAL if not mounted or invalid parameters
1632  *
1633  * @note This function provides stronger guarantees than regular writes
1634  *       about data persistence on storage media
1635  */
dfs_file_fsync(struct dfs_file * file)1636 int dfs_file_fsync(struct dfs_file *file)
1637 {
1638     int ret = -EBADF;
1639 
1640     if (file)
1641     {
1642         if (file->fops->flush)
1643         {
1644             if (dfs_is_mounted(file->vnode->mnt) == 0)
1645             {
1646 #ifdef RT_USING_PAGECACHE
1647                 if (file->vnode->aspace)
1648                 {
1649                     dfs_aspace_flush(file->vnode->aspace);
1650                 }
1651 #endif
1652                 ret = file->fops->flush(file);
1653             }
1654             else
1655             {
1656                 ret = -EINVAL;
1657             }
1658         }
1659     }
1660 
1661     return ret;
1662 }
1663 
1664 /**
1665  * @brief Delete a file or directory entry from the filesystem
1666  *
1667  * This function removes a filesystem entry (file or empty directory) specified by path.
1668  *
1669  * @param[in] path The filesystem path to be deleted
1670  *
1671  * @return int Operation result:
1672  *         - 0 on success
1673  *         -RT_ERROR if general error occurred
1674  *         -ENOENT if file not found
1675  *         -ENOMEM if memory allocation failed
1676  *         -EBUSY if file is in use (mount point or has child mounts)
1677  *         Other negative error codes from filesystem operations
1678  *
1679  * @note This function cannot remove non-empty directories
1680  * @note Mount points cannot be removed while mounted
1681  */
dfs_file_unlink(const char * path)1682 int dfs_file_unlink(const char *path)
1683 {
1684     int ret = -RT_ERROR;
1685     char *fullpath = RT_NULL;
1686     struct dfs_mnt *mnt = RT_NULL;
1687     struct dfs_dentry *dentry = RT_NULL;
1688 
1689     fullpath = dfs_normalize_path(NULL, path);
1690     if (fullpath)
1691     {
1692         DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", fullpath);
1693         mnt = dfs_mnt_lookup(fullpath);
1694         if (mnt)
1695         {
1696             char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST);
1697             if (tmp)
1698             {
1699                 rt_free(fullpath);
1700                 fullpath = tmp;
1701             }
1702 
1703             if (strcmp(mnt->fullpath, fullpath) != 0)
1704             {
1705                 DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_lookup(mnt, %s)", fullpath);
1706                 dentry = dfs_dentry_lookup(mnt, fullpath, 0);
1707                 if (dentry)
1708                 {
1709                     rt_bool_t has_child = RT_FALSE;
1710 
1711                     has_child = dfs_mnt_has_child_mnt(mnt, fullpath);
1712 #ifdef RT_USING_PAGECACHE
1713                     if (dentry->vnode->aspace)
1714                     {
1715                         dfs_aspace_clean(dentry->vnode->aspace);
1716                     }
1717 #endif
1718                     dfs_file_lock();
1719 
1720                     if (has_child == RT_FALSE)
1721                     {
1722                         /* no child mnt point, unlink it */
1723                         ret = -RT_ERROR;
1724 
1725                         if (mnt->fs_ops->unlink)
1726                         {
1727                             if (dfs_is_mounted(mnt) == 0)
1728                             {
1729                                 ret = mnt->fs_ops->unlink(dentry);
1730                             }
1731                         }
1732                     }
1733                     else
1734                     {
1735                         ret = -EBUSY;
1736                     }
1737                     dfs_file_unlock();
1738 
1739                     /* release this dentry */
1740                     dfs_dentry_unref(dentry);
1741                 }
1742                 else
1743                 {
1744                     /* no this entry */
1745                     ret = -ENOENT;
1746                 }
1747             }
1748             else
1749             {
1750                 /* it's a mount point, failed for busy */
1751                 ret = -EBUSY;
1752             }
1753         }
1754         else
1755         {
1756             ret = -ENOENT;
1757         }
1758 
1759         /* release fullpath */
1760         rt_free(fullpath);
1761     }
1762     else
1763     {
1764         ret = -ENOMEM;
1765     }
1766 
1767     return ret;
1768 }
1769 
1770 /**
1771  * @brief Create a hard link between files
1772  *
1773  * This function creates a hard link named 'newname' which refers to the same file as 'oldname'.
1774  *
1775  * @param[in] oldname Path to the existing file to link from
1776  * @param[in] newname Path to the new link to be created
1777  *
1778  * @return int Operation result:
1779  *         - 0 on success
1780  *         -1 on general error
1781  *         -EPERM if oldname is a directory
1782  *         Other negative error codes from filesystem operations
1783  *
1784  * @note Both files must reside on the same filesystem
1785  * @note The function will fail if newname already exists
1786  */
dfs_file_link(const char * oldname,const char * newname)1787 int dfs_file_link(const char *oldname, const char *newname)
1788 {
1789     int ret = -1;
1790     struct stat stat;
1791     struct dfs_mnt *mnt = RT_NULL;
1792     char *old_fullpath, *new_fullpath;
1793 
1794     if (dfs_file_isdir(oldname) == 0)
1795     {
1796         rt_set_errno(-EPERM);
1797         return ret;
1798     }
1799 
1800     if (dfs_file_lstat(newname, &stat) >= 0)
1801     {
1802         return ret;
1803     }
1804 
1805     old_fullpath = dfs_normalize_path(NULL, oldname);
1806     if (old_fullpath)
1807     {
1808         DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", old_fullpath);
1809         mnt = dfs_mnt_lookup(old_fullpath);
1810         if (mnt == RT_NULL)
1811         {
1812             rt_free(old_fullpath);
1813             return -1;
1814         }
1815 
1816         char *tmp = dfs_file_realpath(&mnt, old_fullpath, DFS_REALPATH_EXCEPT_LAST);
1817         if (tmp)
1818         {
1819             rt_free(old_fullpath);
1820             old_fullpath = tmp;
1821         }
1822     }
1823 
1824     new_fullpath = dfs_normalize_path(NULL, newname);
1825     if (new_fullpath)
1826     {
1827         char *tmp = dfs_file_realpath(&mnt, new_fullpath, DFS_REALPATH_EXCEPT_LAST);
1828         if (tmp)
1829         {
1830             rt_free(new_fullpath);
1831             new_fullpath = tmp;
1832         }
1833     }
1834 
1835     if (old_fullpath && new_fullpath)
1836     {
1837         struct dfs_dentry *old_dentry, *new_dentry;
1838 
1839         DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_lookup(mnt, %s)", old_fullpath);
1840         old_dentry = dfs_dentry_lookup(mnt, old_fullpath, 0);
1841         DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_create(%s)", new_fullpath);
1842         new_dentry = dfs_dentry_create(mnt, new_fullpath);
1843 
1844         if (old_dentry && new_dentry)
1845         {
1846             if (mnt->fs_ops->link)
1847             {
1848                 if (dfs_is_mounted(mnt) == 0)
1849                 {
1850                     ret = mnt->fs_ops->link(old_dentry, new_dentry);
1851                 }
1852             }
1853         }
1854 
1855         dfs_dentry_unref(old_dentry);
1856         dfs_dentry_unref(new_dentry);
1857     }
1858 
1859     if (old_fullpath)
1860     {
1861         rt_free(old_fullpath);
1862     }
1863 
1864     if (new_fullpath)
1865     {
1866         rt_free(new_fullpath);
1867     }
1868 
1869     return ret;
1870 }
1871 
1872 /**
1873  * @brief Create a symbolic link named 'linkpath' containing the string 'target'
1874  *
1875  * This function creates a symbolic link which refers to the specified target path.
1876  * The linkpath should not exist before calling this function.
1877  *
1878  * @param[in] target The path string that the symbolic link will point to
1879  * @param[in] linkpath The path where the symbolic link will be created
1880  *
1881  * @return int Operation status:
1882  *         - 0 on success
1883  *         -ENOSYS if symlink operation not supported by filesystem
1884  *         -ENOENT if parent directory doesn't exist
1885  *         -EPERM if linkpath already exists
1886  *         -EINVAL if invalid parameters
1887  *         -RT_ERROR for general errors
1888  */
dfs_file_symlink(const char * target,const char * linkpath)1889 int dfs_file_symlink(const char *target, const char *linkpath)
1890 {
1891     int ret = -RT_ERROR;
1892     char *fullpath = RT_NULL, *parent = RT_NULL;
1893     struct dfs_mnt *mnt = RT_NULL;
1894     struct dfs_dentry *dentry = RT_NULL;
1895 
1896     if (target && linkpath)
1897     {
1898         if (linkpath[0] != '/')
1899         {
1900            fullpath = dfs_normalize_path(NULL, linkpath);
1901         }
1902         else
1903         {
1904             fullpath = (char*)linkpath;
1905         }
1906 
1907         /* linkpath should be not exist */
1908         if (dfs_file_access(fullpath, O_RDONLY) != 0)
1909         {
1910             char *index;
1911 
1912             /* get parent path */
1913             index = strrchr(fullpath, '/');
1914             if (index)
1915             {
1916                 int length = index - fullpath;
1917                 if (length > 0)
1918                 {
1919                     parent = (char*) rt_malloc (length + 1);
1920                     if (parent)
1921                     {
1922                         memcpy(parent, fullpath, length);
1923                         parent[length] = '\0';
1924                     }
1925                 }
1926                 else
1927                 {
1928                     parent = (char*) rt_malloc (1 + 1);
1929                     if (parent)
1930                     {
1931                         parent[0] = '/';
1932                         parent[1] = '\0';
1933                     }
1934                 }
1935             }
1936 
1937             if (parent)
1938             {
1939                 DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", fullpath);
1940                 mnt = dfs_mnt_lookup(parent);
1941                 if (mnt)
1942                 {
1943                     char *tmp = dfs_file_realpath(&mnt, parent, DFS_REALPATH_EXCEPT_LAST);
1944                     if (tmp)
1945                     {
1946                         rt_free(parent);
1947                         parent = tmp;
1948                     }
1949 
1950                     DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_lookup(mnt, %s)", fullpath);
1951                     dentry = dfs_dentry_lookup(mnt, parent, DFS_REALPATH_EXCEPT_LAST);
1952                     if (dentry)
1953                     {
1954                         if (dentry->mnt->fs_ops->symlink)
1955                         {
1956                             char *path = dfs_normalize_path(parent, target);
1957                             if (path)
1958                             {
1959                                 ret = rt_strncmp(parent, path, strlen(parent));
1960                                 if (ret == 0)
1961                                 {
1962                                     tmp = path + strlen(parent);
1963                                     if (*tmp == '/')
1964                                     {
1965                                         tmp ++;
1966                                     }
1967                                 }
1968                                 else
1969                                 {
1970                                     tmp = path;
1971                                 }
1972 
1973                                 if (dfs_is_mounted(mnt) == 0)
1974                                 {
1975                                     ret = mnt->fs_ops->symlink(dentry, tmp, index + 1);
1976                                 }
1977 
1978                                 rt_free(path);
1979                             }
1980                         }
1981                         else
1982                         {
1983                             ret = -ENOSYS;
1984                         }
1985 
1986                         dfs_dentry_unref(dentry);
1987                     }
1988                     else
1989                     {
1990                         ret = -ENOENT;
1991                     }
1992                 }
1993                 else
1994                 {
1995                     ret = -ENOENT;
1996                 }
1997 
1998                 rt_free(parent);
1999             }
2000         }
2001         else
2002         {
2003             rt_set_errno(-EPERM);
2004         }
2005 
2006         if (fullpath != linkpath)
2007             rt_free(fullpath);
2008     }
2009     else
2010     {
2011         ret = -EINVAL;
2012     }
2013 
2014     return ret;
2015 }
2016 
2017 /**
2018  * @brief Read the contents of a symbolic link
2019  *
2020  * This function reads the contents of the symbolic link specified by path into
2021  * the buffer provided.
2022  *
2023  * @param[in] path The path to the symbolic link to be read
2024  * @param[out] buf Buffer to store the link contents
2025  * @param[in] bufsize Size of the buffer in bytes
2026  *
2027  * @return int Number of bytes placed in buffer on success, or negative error code:
2028  *         -ENOSYS if readlink operation not supported by filesystem
2029  *         -ENOENT if symbolic link does not exist
2030  *         -ENOMEM if memory allocation failed
2031  *         -EINVAL if invalid parameters
2032  *         -RT_ERROR for general errors
2033  */
dfs_file_readlink(const char * path,char * buf,int bufsize)2034 int dfs_file_readlink(const char *path, char *buf, int bufsize)
2035 {
2036     int ret = -RT_ERROR;
2037     char *fullpath = RT_NULL;
2038     struct dfs_mnt *mnt = RT_NULL;
2039     struct dfs_dentry *dentry = RT_NULL;
2040 
2041     fullpath = dfs_normalize_path(NULL, path);
2042     if (fullpath)
2043     {
2044         DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", fullpath);
2045         mnt = dfs_mnt_lookup(fullpath);
2046         if (mnt)
2047         {
2048             char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST);
2049             if (tmp)
2050             {
2051                 rt_free(fullpath);
2052                 fullpath = tmp;
2053             }
2054 
2055             DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_lookup(mnt, %s)", fullpath);
2056             dentry = dfs_dentry_lookup(mnt, fullpath, 0);
2057             if (dentry)
2058             {
2059                 if (mnt->fs_ops->readlink)
2060                 {
2061                     if (dfs_is_mounted(mnt) == 0)
2062                     {
2063                         ret = mnt->fs_ops->readlink(dentry, buf, bufsize);
2064                     }
2065                 }
2066                 else
2067                 {
2068                     ret = -ENOSYS;
2069                 }
2070 
2071                 /* release this dentry */
2072                 dfs_dentry_unref(dentry);
2073             }
2074             else
2075             {
2076                 /* no this entry */
2077                 ret = -ENOENT;
2078             }
2079         }
2080         else
2081         {
2082             ret = -ENOENT;
2083         }
2084 
2085         /* release fullpath */
2086         rt_free(fullpath);
2087     }
2088     else
2089     {
2090         ret = -ENOMEM;
2091     }
2092 
2093     return ret;
2094 }
2095 
2096 /**
2097  * @brief Rename a file/directory
2098  *
2099  * This function renames a filesystem entry from old_file to new_file.
2100  *
2101  * @param[in] old_file Path to the existing file/directory to be renamed
2102  * @param[in] new_file New path for the file/directory
2103  *
2104  * @return int Operation result:
2105  *         - 0 on success
2106  *         -1 on general error
2107  *         -ENOMEM if memory allocation failed
2108  *         -ENOSYS if rename operation not supported
2109  *         -EINVAL if invalid parameters or not mounted
2110  *
2111  * @note Page cache will be cleaned if RT_USING_PAGECACHE is enabled
2112  */
dfs_file_rename(const char * old_file,const char * new_file)2113 int dfs_file_rename(const char *old_file, const char *new_file)
2114 {
2115     int ret = -1;
2116     struct dfs_mnt *mnt = RT_NULL;
2117     char *old_fullpath, *new_fullpath;
2118 
2119     old_fullpath = dfs_normalize_path(NULL, old_file);
2120     if (old_fullpath)
2121     {
2122         DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", old_fullpath);
2123         mnt = dfs_mnt_lookup(old_fullpath);
2124         if (mnt == RT_NULL)
2125         {
2126             rt_free(old_fullpath);
2127             return -1;
2128         }
2129 
2130         char *tmp = dfs_file_realpath(&mnt, old_fullpath, DFS_REALPATH_EXCEPT_LAST);
2131         if (tmp)
2132         {
2133             rt_free(old_fullpath);
2134             old_fullpath = tmp;
2135         }
2136     }
2137 
2138     new_fullpath = dfs_normalize_path(NULL, new_file);
2139     if (new_fullpath)
2140     {
2141         char *tmp = dfs_file_realpath(&mnt, new_fullpath, DFS_REALPATH_EXCEPT_LAST);
2142         if (tmp)
2143         {
2144             rt_free(new_fullpath);
2145             new_fullpath = tmp;
2146         }
2147     }
2148 
2149     if (old_fullpath && new_fullpath)
2150     {
2151         struct dfs_dentry *old_dentry, *new_dentry;
2152 
2153         DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_lookup(mnt, %s)", old_fullpath);
2154         old_dentry = dfs_dentry_lookup(mnt, old_fullpath, 0);
2155         DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_create(%s)", new_fullpath);
2156         new_dentry = dfs_dentry_create(mnt, new_fullpath);
2157 
2158         if (old_dentry && new_dentry)
2159         {
2160             if (mnt->fs_ops->rename)
2161             {
2162                 if (dfs_is_mounted(mnt) == 0)
2163                 {
2164 #ifdef RT_USING_PAGECACHE
2165                     if (old_dentry->vnode->aspace)
2166                     {
2167                         dfs_aspace_clean(old_dentry->vnode->aspace);
2168                     }
2169 #endif
2170                     ret = mnt->fs_ops->rename(old_dentry, new_dentry);
2171                 }
2172             }
2173         }
2174 
2175         dfs_dentry_unref(old_dentry);
2176         dfs_dentry_unref(new_dentry);
2177     }
2178 
2179     if (old_fullpath)
2180     {
2181         rt_free(old_fullpath);
2182     }
2183 
2184     if (new_fullpath)
2185     {
2186         rt_free(new_fullpath);
2187     }
2188 
2189     return ret;
2190 }
2191 
2192 /**
2193  * @brief Truncate or extend a file to the specified length
2194  *
2195  * This function changes the size of the file referenced by the file descriptor.
2196  * If the new size is smaller than current size, the file is truncated.
2197  * If larger, the file is extended and the extended area is filled with zeros.
2198  *
2199  * @param[in] file Pointer to the file structure to truncate
2200  * @param[in] length New length of the file in bytes
2201  *
2202  * @return int Operation result:
2203  *         - 0 on success
2204  *         -EBADF if invalid file descriptor
2205  *         -ENOSYS if truncate operation not supported
2206  *         -EINVAL if invalid parameters or not mounted
2207  *
2208  * @note If RT_USING_PAGECACHE is enabled, the page cache will be cleaned
2209  *       before truncation to ensure data consistency
2210  */
dfs_file_ftruncate(struct dfs_file * file,off_t length)2211 int dfs_file_ftruncate(struct dfs_file *file, off_t length)
2212 {
2213     int ret = 0;
2214 
2215     if (file)
2216     {
2217         if (file->fops->truncate)
2218         {
2219             if (dfs_is_mounted(file->vnode->mnt) == 0)
2220             {
2221 #ifdef RT_USING_PAGECACHE
2222                 if (file->vnode->aspace)
2223                 {
2224                     dfs_aspace_clean(file->vnode->aspace);
2225                 }
2226 #endif
2227                 ret = file->fops->truncate(file, length);
2228             }
2229             else
2230             {
2231                 ret = -EINVAL;
2232             }
2233         }
2234         else
2235         {
2236             ret = -ENOSYS;
2237         }
2238     }
2239     else
2240     {
2241         ret = -EBADF;
2242     }
2243 
2244     return ret;
2245 }
2246 
2247 /**
2248  * @brief Flush file buffers to storage device
2249  *
2250  * This function forces any buffered data to be written to the underlying storage device.
2251  *
2252  * @param[in,out] file Pointer to the file structure to flush (both input and output)
2253  *
2254  * @return int Operation result:
2255  *         - 0 on success
2256  *         -EBADF if invalid file descriptor
2257  *         -ENOSYS if flush operation not supported
2258  *         -EINVAL if invalid parameters or not mounted
2259  *
2260  * @note This function provides stronger guarantees than regular writes
2261  *       about data persistence on storage media
2262  * @note If RT_USING_PAGECACHE is enabled, the page cache will be flushed first
2263  */
dfs_file_flush(struct dfs_file * file)2264 int dfs_file_flush(struct dfs_file *file)
2265 {
2266     int ret = 0;
2267 
2268     if (file)
2269     {
2270         if (file->fops->flush)
2271         {
2272             if (dfs_is_mounted(file->vnode->mnt) == 0)
2273             {
2274 #ifdef RT_USING_PAGECACHE
2275                 if (file->vnode->aspace)
2276                 {
2277                     dfs_aspace_flush(file->vnode->aspace);
2278                 }
2279 #endif
2280                 ret = file->fops->flush(file);
2281             }
2282             else
2283             {
2284                 ret = -EINVAL;
2285             }
2286         }
2287         else
2288         {
2289             ret = -ENOSYS;
2290         }
2291     }
2292     else
2293     {
2294         ret = -EBADF;
2295     }
2296 
2297     return ret;
2298 }
2299 
2300 /**
2301  * @brief Read directory entries
2302  *
2303  * This function reads directory entries from the directory file descriptor into
2304  * the buffer provided. Each entry is stored as a struct dirent.
2305  *
2306  * @param[in] file Pointer to the directory file structure
2307  * @param[out] dirp Buffer to store directory entries
2308  * @param[in] nbytes Size of the buffer in bytes
2309  *
2310  * @return int Number of bytes read on success, or negative error code:
2311  *         -EBADF if invalid file descriptor
2312  *         -ENOTDIR if not a directory
2313  *         -EINVAL if not mounted or invalid parameters
2314  *         -RT_ERROR for general errors
2315  */
dfs_file_getdents(struct dfs_file * file,struct dirent * dirp,size_t nbytes)2316 int dfs_file_getdents(struct dfs_file *file, struct dirent *dirp, size_t nbytes)
2317 {
2318     int ret = -RT_ERROR;
2319 
2320     if (file)
2321     {
2322         if (file->vnode && S_ISDIR(file->vnode->mode))
2323         {
2324             if (file->fops && file->fops->getdents)
2325             {
2326                 DLOG(msg, "dfs_file", file->dentry->mnt->fs_ops->name, DLOG_MSG, "fops->getdents()");
2327 
2328                 if (dfs_is_mounted(file->vnode->mnt) == 0)
2329                 {
2330                     ret = file->fops->getdents(file, dirp, nbytes);
2331                 }
2332                 else
2333                 {
2334                     ret = -EINVAL;
2335                 }
2336             }
2337         }
2338     }
2339     else
2340     {
2341         ret = -EBADF;
2342     }
2343 
2344     return ret;
2345 }
2346 
2347 /**
2348  * @brief Check if a path refers to a directory
2349  *
2350  * This function checks whether the specified path exists and is a directory.
2351  *
2352  * @param[in] path The filesystem path to check
2353  *
2354  * @return int Operation result:
2355  *         - 0 if path exists and is a directory
2356  *         -RT_ERROR if path doesn't exist or isn't a directory
2357  */
dfs_file_isdir(const char * path)2358 int dfs_file_isdir(const char *path)
2359 {
2360     int ret = -RT_ERROR;
2361     char *fullpath = RT_NULL;
2362     struct dfs_mnt *mnt = RT_NULL;
2363     struct dfs_dentry *dentry = RT_NULL;
2364 
2365     fullpath = dfs_normalize_path(NULL, path);
2366     if (fullpath)
2367     {
2368         DLOG(msg, "dfs_file", "mnt", DLOG_MSG, "dfs_mnt_lookup(%s)", fullpath);
2369         mnt = dfs_mnt_lookup(fullpath);
2370         if (mnt)
2371         {
2372             char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_NONE);
2373             if (tmp)
2374             {
2375                 rt_free(fullpath);
2376                 fullpath = tmp;
2377             }
2378 
2379             DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dentry = dfs_dentry_lookup(mnt, %s)", fullpath);
2380             dentry = dfs_dentry_lookup(mnt, fullpath, 0);
2381             if (dentry)
2382             {
2383                 DLOG(msg, "dentry", "dfs_file", DLOG_MSG_RET, "return dentry");
2384                 if (mnt->fs_ops->stat)
2385                 {
2386                     struct stat stat = {0};
2387                     DLOG(msg, "dfs_file", mnt->fs_ops->name, DLOG_MSG, "fs_ops->stat(dentry, buf)");
2388 
2389                     if (dfs_is_mounted(mnt) == 0)
2390                     {
2391                         ret = mnt->fs_ops->stat(dentry, &stat);
2392                     }
2393 
2394                     if (ret == RT_EOK && S_ISDIR(stat.st_mode))
2395                     {
2396                         ret = RT_EOK;
2397                     }
2398                     else
2399                     {
2400                         ret = -RT_ERROR;
2401                     }
2402                 }
2403 
2404                 /* unref dentry */
2405                 DLOG(msg, "dfs_file", "dentry", DLOG_MSG, "dfs_dentry_unref(dentry)");
2406                 dfs_dentry_unref(dentry);
2407                 dentry = RT_NULL;
2408             }
2409         }
2410 
2411         rt_free(fullpath);
2412         fullpath = RT_NULL;
2413     }
2414 
2415     return ret;
2416 }
2417 
2418 /**
2419  * @brief Check file accessibility with specified mode
2420  *
2421  * This function checks whether the file specified by path can be accessed
2422  * with the given mode.
2423  *
2424  * @param[in] path The file path to check accessibility
2425  * @param[in] mode The access mode to check (read/write/execute permissions)
2426  *
2427  * @return int Access status:
2428  *         - 0 if file is accessible with specified mode
2429  *         - -1 if file is not accessible
2430  */
dfs_file_access(const char * path,mode_t mode)2431 int dfs_file_access(const char *path, mode_t mode)
2432 {
2433     int ret;
2434     struct dfs_file file;
2435 
2436     dfs_file_init(&file);
2437 
2438     if (dfs_file_open(&file, path, O_RDONLY, mode) >= 0)
2439     {
2440         ret = 0;
2441         dfs_file_close(&file);
2442     }
2443     else
2444     {
2445         ret = -1;
2446     }
2447 
2448     dfs_file_deinit(&file);
2449 
2450     return ret;
2451 }
2452 
2453 #ifdef RT_USING_SMART
2454 /**
2455  * @brief Memory map a file or device into process address space
2456  *
2457  * This function maps a file or device into the calling process's address space.
2458  * It handles both regular files and device files differently:
2459  * - For regular files: uses standard mmap operation
2460  * - For device files: uses device-specific ioctl with RT_FIOMMAP2 command
2461  *
2462  * @param[in] file Pointer to the file structure to be mapped
2463  * @param[in,out] mmap2 Pointer to mmap arguments structure (both input and output)
2464  *
2465  * @return int Operation result:
2466  *         - RT_EOK on success
2467  *         - EINVAL if invalid parameters or operation not supported
2468  *         - Other error codes from underlying mmap/ioctl operations
2469  *
2470  * @note For device files, the actual mapping behavior depends on the device driver
2471  */
dfs_file_mmap2(struct dfs_file * file,struct dfs_mmap2_args * mmap2)2472 int dfs_file_mmap2(struct dfs_file *file, struct dfs_mmap2_args *mmap2)
2473 {
2474     int ret = RT_EOK;
2475 
2476     if (file && mmap2)
2477     {
2478         if (file->vnode->type == FT_REGULAR)
2479         {
2480             ret = dfs_file_mmap(file, mmap2);
2481             if (ret != 0)
2482             {
2483                 ret = ret > 0 ? ret : -ret;
2484                 rt_set_errno(ret);
2485             }
2486         }
2487         else if (file->vnode->type != FT_DEVICE || !file->vnode->fops->ioctl)
2488         {
2489             rt_set_errno(EINVAL);
2490         }
2491         else if (file->vnode->type == FT_DEVICE && file->vnode->fops->ioctl)
2492         {
2493             if (dfs_is_mounted(file->vnode->mnt) == 0)
2494             {
2495                 ret = file->vnode->fops->ioctl(file, RT_FIOMMAP2, mmap2);
2496             }
2497             else
2498             {
2499                 ret = EINVAL;
2500             }
2501 
2502             if (ret != 0)
2503             {
2504                 ret = ret > 0 ? ret : -ret;
2505                 rt_set_errno(ret);
2506             }
2507         }
2508     }
2509 
2510     return ret;
2511 }
2512 #endif
2513 
2514 #ifdef RT_USING_FINSH
2515 
2516 #define _COLOR_RED      "\033[31m"
2517 #define _COLOR_GREEN    "\033[32m"
2518 #define _COLOR_YELLOW   "\033[33m"
2519 #define _COLOR_BLUE     "\033[34m"
2520 #define _COLOR_CYAN     "\033[36m"
2521 #define _COLOR_WHITE    "\033[37m"
2522 #define _COLOR_NORMAL   "\033[0m"
2523 
2524 /**
2525  * @brief List directory contents with colored output
2526  *
2527  * This function lists all entries in the specified directory with colored output
2528  * that distinguishes different file types. It handles:
2529  * - Directories (blue)
2530  * - Symbolic links (cyan with target path)
2531  * - Executable files (green)
2532  * - Character devices (yellow)
2533  * - Regular files (default color)
2534  *
2535  * @param[in] pathname The directory path to list (NULL for current directory)
2536  */
ls(const char * pathname)2537 void ls(const char *pathname)
2538 {
2539     struct dirent dirent;
2540     struct stat stat;
2541     int length;
2542     char *fullpath, *path;
2543     struct dfs_file file;
2544 
2545     if (pathname == NULL)
2546     {
2547 #ifdef DFS_USING_WORKDIR
2548         /* open current working directory */
2549         path = rt_strdup(working_directory);
2550 #else
2551         path = rt_strdup("/");
2552 #endif
2553         if (path == NULL)
2554         {
2555             return; /* out of memory */
2556         }
2557     }
2558     else
2559     {
2560         path = dfs_normalize_path(NULL, (char *)pathname);
2561         if (path == NULL)
2562         {
2563             return; /* out of memory */
2564         }
2565     }
2566 
2567     dfs_file_init(&file);
2568 
2569     /* list directory */
2570     DLOG(msg, "dfs", "dfs_file", DLOG_MSG, "dfs_file_open(%s, O_DIRECTORY, 0)", path);
2571     if (dfs_file_open(&file, path, O_DIRECTORY, 0) >= 0)
2572     {
2573         char *link_fn = (char *)rt_malloc(DFS_PATH_MAX);
2574         if (link_fn)
2575         {
2576             rt_kprintf("Directory %s:\n", path);
2577             do
2578             {
2579                 memset(&dirent, 0, sizeof(struct dirent));
2580 
2581                 DLOG(group, "foreach_item");
2582                 DLOG(msg, "dfs", "dfs_file", DLOG_MSG, "dfs_file_getdents(&dirent)");
2583                 length = dfs_file_getdents(&file, &dirent, sizeof(struct dirent));
2584                 if (length > 0)
2585                 {
2586                     DLOG(msg, "dfs_file", "dfs", DLOG_MSG_RET, "dirent.d_name=%s", dirent.d_name);
2587                     memset(&stat, 0, sizeof(struct stat));
2588 
2589                     /* build full path for each file */
2590                     fullpath = dfs_normalize_path(path, dirent.d_name);
2591                     if (fullpath == NULL)
2592                         break;
2593 
2594                     DLOG(msg, "dfs", "dfs_file", DLOG_MSG, "dfs_file_lstat(%s, &stat)", fullpath);
2595                     if (dfs_file_lstat(fullpath, &stat) == 0)
2596                     {
2597                         if (S_ISDIR(stat.st_mode))
2598                         {
2599                             rt_kprintf(_COLOR_BLUE "%-20s" _COLOR_NORMAL, dirent.d_name);
2600                             rt_kprintf("%-25s\n", "<DIR>");
2601                         }
2602                         else if (S_ISLNK(stat.st_mode))
2603                         {
2604                             int ret = 0;
2605 
2606                             rt_kprintf(_COLOR_CYAN "%-20s" _COLOR_NORMAL, dirent.d_name);
2607 
2608                             ret = dfs_file_readlink(fullpath, link_fn, DFS_PATH_MAX);
2609                             if (ret > 0)
2610                             {
2611                                 char *link_path = link_fn;
2612                                 struct dfs_mnt *mnt = RT_NULL;
2613 
2614                                 mnt = dfs_mnt_lookup(fullpath);
2615                                 if (mnt)
2616                                 {
2617                                     char *tmp = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST);
2618                                     if (tmp)
2619                                     {
2620                                         char *index;
2621 
2622                                         index = strrchr(fullpath, '/');
2623                                         if (index)
2624                                         {
2625                                             int length = index - fullpath;
2626                                             char *parent = (char*) rt_malloc (length + 1);
2627                                             if (parent)
2628                                             {
2629                                                 rt_memcpy(parent, fullpath, length);
2630                                                 parent[length] = '\0';
2631 
2632                                                 ret = rt_strncmp(parent, link_fn, length);
2633                                                 if (ret == 0)
2634                                                 {
2635                                                     link_path = link_fn + length;
2636                                                     if (*link_path == '/')
2637                                                     {
2638                                                         link_path ++;
2639                                                     }
2640                                                 }
2641                                                 rt_free(parent);
2642                                             }
2643                                         }
2644                                         rt_free(tmp);
2645                                     }
2646                                 }
2647 
2648                                 rt_kprintf("-> %s\n", link_path);
2649                             }
2650                             else
2651                             {
2652                                 rt_kprintf(_COLOR_RED "-> link_error\n" _COLOR_NORMAL);
2653                             }
2654                         }
2655                         else if (stat.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
2656                         {
2657                             rt_kprintf(_COLOR_GREEN "%-20s" _COLOR_NORMAL, dirent.d_name);
2658                             rt_kprintf("%-25lu\n", (unsigned long)stat.st_size);
2659                         }
2660                         else if (S_ISCHR(stat.st_mode))
2661                         {
2662                             rt_kprintf(_COLOR_YELLOW "%-20s" _COLOR_NORMAL, dirent.d_name);
2663                             rt_kprintf("%-25s\n", "<CHR>");
2664                         }
2665                         else
2666                         {
2667                             rt_kprintf("%-20s", dirent.d_name);
2668                             rt_kprintf("%-25lu\n", (unsigned long)stat.st_size);
2669                         }
2670                     }
2671                     else
2672                     {
2673                         rt_kprintf(_COLOR_RED "%-20s\n" _COLOR_NORMAL, dirent.d_name);
2674                     }
2675 
2676                     rt_free(fullpath);
2677                 }
2678                 else
2679                 {
2680                     DLOG(msg, "dfs_file", "dfs", DLOG_MSG_RET, "return NULL");
2681                 }
2682 
2683                 DLOG(group_end);
2684             } while (length > 0);
2685 
2686             rt_free(link_fn);
2687         }
2688 
2689         DLOG(msg, "dfs", "dfs_file", DLOG_MSG, "dfs_file_close()");
2690         dfs_file_close(&file);
2691     }
2692     else
2693     {
2694         rt_kprintf("No such directory\n");
2695     }
2696 
2697     dfs_file_deinit(&file);
2698 
2699     DLOG(msg, "dfs_file", "dfs", DLOG_MSG_RET, "return");
2700     rt_free(path);
2701 }
2702 
2703 /**
2704  * @brief Display file contents to standard output
2705  *
2706  * This function reads and prints the contents of the specified file to the console.
2707  * It handles both text and binary files by reading in chunks and printing the output.
2708  *
2709  * @param[in] filename Path to the file to be displayed
2710  */
cat(const char * filename)2711 void cat(const char *filename)
2712 {
2713     int length = 0;
2714     char buffer[81];
2715     struct dfs_file file;
2716 
2717     if (filename && dfs_file_isdir(filename) == 0)
2718     {
2719         rt_kprintf("cat: %s Is a directory\n", filename);
2720         return;
2721     }
2722 
2723     dfs_file_init(&file);
2724 
2725     DLOG(msg, "dfs", "dfs_file", DLOG_MSG, "dfs_file_open(%s, O_RDONLY, 0)", filename);
2726     if (dfs_file_open(&file, filename, O_RDONLY, 0) < 0)
2727     {
2728         rt_kprintf("Open %s failed\n", filename);
2729         dfs_file_deinit(&file);
2730         return;
2731     }
2732 
2733     do
2734     {
2735         rt_memset(buffer, 0x0, sizeof(buffer));
2736         DLOG(msg, "dfs", "dfs_file", DLOG_MSG, "dfs_file_read(fd, buffer, %d)", sizeof(buffer) - 1);
2737         length = dfs_file_read(&file, (void *)buffer, sizeof(buffer) - 1);
2738         if (length > 0)
2739         {
2740             buffer[length] = '\0';
2741             rt_kprintf("%s", buffer);
2742         }
2743     } while (length > 0);
2744     rt_kprintf("\n");
2745 
2746     DLOG(msg, "dfs", "dfs_file", DLOG_MSG, "dfs_file_close()");
2747     dfs_file_close(&file);
2748     dfs_file_deinit(&file);
2749 }
2750 
2751 #define BUF_SZ  4096
2752 /**
2753  * @brief Copy file contents from source to destination
2754  *
2755  * This function copies the contents of a source file to a destination file.
2756  * It handles memory allocation, file operations, and error checking.
2757  *
2758  * @param[in] src Path to the source file to be copied
2759  * @param[in] dst Path to the destination file to be created/overwritten
2760  */
copyfile(const char * src,const char * dst)2761 static void copyfile(const char *src, const char *dst)
2762 {
2763     int ret;
2764     struct dfs_file src_file, dst_file;
2765     rt_uint8_t *block_ptr;
2766     rt_int32_t read_bytes;
2767 
2768     block_ptr = (rt_uint8_t *)rt_malloc(BUF_SZ);
2769     if (block_ptr == NULL)
2770     {
2771         rt_kprintf("out of memory\n");
2772         return;
2773     }
2774 
2775     dfs_file_init(&src_file);
2776 
2777     ret = dfs_file_open(&src_file, src, O_RDONLY, 0);
2778     if (ret < 0)
2779     {
2780         dfs_file_deinit(&src_file);
2781         rt_free(block_ptr);
2782         rt_kprintf("Read %s failed\n", src);
2783         return;
2784     }
2785 
2786     dfs_file_init(&dst_file);
2787 
2788     ret = dfs_file_open(&dst_file, dst, O_WRONLY | O_CREAT | O_TRUNC, 0);
2789     if (ret < 0)
2790     {
2791         dfs_file_deinit(&dst_file);
2792         dfs_file_close(&src_file);
2793         dfs_file_deinit(&src_file);
2794         rt_free(block_ptr);
2795         rt_kprintf("Write %s failed\n", dst);
2796         return;
2797     }
2798 
2799     do
2800     {
2801         read_bytes = dfs_file_read(&src_file, block_ptr, BUF_SZ);
2802         if (read_bytes > 0)
2803         {
2804             int length;
2805 
2806             length = dfs_file_write(&dst_file, block_ptr, read_bytes);
2807             if (length != read_bytes)
2808             {
2809                 /* write failed. */
2810                 rt_kprintf("Write file data failed, errno=%d\n", length);
2811                 break;
2812             }
2813         }
2814     } while (read_bytes > 0);
2815 
2816     dfs_file_close(&dst_file);
2817     dfs_file_deinit(&dst_file);
2818     dfs_file_close(&src_file);
2819     dfs_file_deinit(&src_file);
2820     rt_free(block_ptr);
2821 }
2822 
2823 extern int mkdir(const char *path, mode_t mode);
2824 
2825 /**
2826  * @brief Recursively copy directory contents from source to destination
2827  *
2828  * This function recursively copies all files and subdirectories from the source
2829  * directory to the destination directory. It handles both files and directories
2830  * appropriately.
2831  *
2832  * @param[in] src Path to the source directory to be copied
2833  * @param[in] dst Path to the destination directory to be created
2834  */
copydir(const char * src,const char * dst)2835 static void copydir(const char *src, const char *dst)
2836 {
2837     struct dirent dirent;
2838     struct stat stat;
2839     int length;
2840     struct dfs_file file;
2841 
2842     dfs_file_init(&file);
2843 
2844     if (dfs_file_open(&file, src, O_DIRECTORY, 0) < 0)
2845     {
2846         rt_kprintf("open %s failed\n", src);
2847         dfs_file_deinit(&file);
2848         return ;
2849     }
2850 
2851     do
2852     {
2853         rt_memset(&dirent, 0, sizeof(struct dirent));
2854 
2855         length = dfs_file_getdents(&file, &dirent, sizeof(struct dirent));
2856         if (length > 0)
2857         {
2858             char *src_entry_full = NULL;
2859             char *dst_entry_full = NULL;
2860 
2861             if (strcmp(dirent.d_name, "..") == 0 || strcmp(dirent.d_name, ".") == 0)
2862                 continue;
2863 
2864             /* build full path for each file */
2865             if ((src_entry_full = dfs_normalize_path(src, dirent.d_name)) == NULL)
2866             {
2867                 rt_kprintf("out of memory!\n");
2868                 break;
2869             }
2870             if ((dst_entry_full = dfs_normalize_path(dst, dirent.d_name)) == NULL)
2871             {
2872                 rt_kprintf("out of memory!\n");
2873                 rt_free(src_entry_full);
2874                 break;
2875             }
2876 
2877             rt_memset(&stat, 0, sizeof(struct stat));
2878             if (dfs_file_lstat(src_entry_full, &stat) != 0)
2879             {
2880                 rt_kprintf("open file: %s failed\n", dirent.d_name);
2881                 continue;
2882             }
2883 
2884             if (S_ISDIR(stat.st_mode))
2885             {
2886                 mkdir(dst_entry_full, 0);
2887                 copydir(src_entry_full, dst_entry_full);
2888             }
2889             else
2890             {
2891                 copyfile(src_entry_full, dst_entry_full);
2892             }
2893             rt_free(src_entry_full);
2894             rt_free(dst_entry_full);
2895         }
2896     }
2897     while (length > 0);
2898 
2899     dfs_file_close(&file);
2900     dfs_file_deinit(&file);
2901 }
2902 
2903 /**
2904  * @brief Extract the last component from a path string
2905  *
2906  * This function extracts the filename or last directory name from a given path.
2907  * It searches for the last '/' character and returns the substring after it.
2908  *
2909  * @param[in] path The input path string to process
2910  *
2911  * @return const char* Pointer to:
2912  *         - The last path component if '/' is found
2913  *         - The original path if no '/' is found
2914  *         - NULL if input path is NULL
2915  */
_get_path_lastname(const char * path)2916 static const char *_get_path_lastname(const char *path)
2917 {
2918     char *ptr;
2919     if ((ptr = (char *)strrchr(path, '/')) == NULL)
2920         return path;
2921 
2922     /* skip the '/' then return */
2923     return ++ptr;
2924 }
2925 
2926 /**
2927  * @brief Copy files or directories from source to destination
2928  *
2929  * This function handles copying operations between files and directories with
2930  * various combinations of source and destination types. It supports:
2931  * - File to file copy
2932  * - File to directory copy (copies into directory with original filename)
2933  * - Directory to directory copy (recursive)
2934  * - Directory to new directory creation and copy
2935  *
2936  * @param[in] src Path to the source file/directory to copy
2937  * @param[in] dst Path to the destination file/directory
2938  */
copy(const char * src,const char * dst)2939 void copy(const char *src, const char *dst)
2940 {
2941 #define FLAG_SRC_TYPE      0x03
2942 #define FLAG_SRC_IS_DIR    0x01
2943 #define FLAG_SRC_IS_FILE   0x02
2944 #define FLAG_SRC_NON_EXSIT 0x00
2945 
2946 #define FLAG_DST_TYPE      0x0C
2947 #define FLAG_DST_IS_DIR    0x04
2948 #define FLAG_DST_IS_FILE   0x08
2949 #define FLAG_DST_NON_EXSIT 0x00
2950 
2951     struct stat stat;
2952     uint32_t flag = 0;
2953 
2954     /* check the staus of src and dst */
2955     if (dfs_file_lstat(src, &stat) < 0)
2956     {
2957         rt_kprintf("copy failed, bad %s\n", src);
2958         return;
2959     }
2960     if (S_ISDIR(stat.st_mode))
2961         flag |= FLAG_SRC_IS_DIR;
2962     else
2963         flag |= FLAG_SRC_IS_FILE;
2964 
2965     if (dfs_file_stat(dst, &stat) < 0)
2966     {
2967         flag |= FLAG_DST_NON_EXSIT;
2968     }
2969     else
2970     {
2971         if (S_ISDIR(stat.st_mode))
2972             flag |= FLAG_DST_IS_DIR;
2973         else
2974             flag |= FLAG_DST_IS_FILE;
2975     }
2976 
2977     /* 2. check status */
2978     if ((flag & FLAG_SRC_IS_DIR) && (flag & FLAG_DST_IS_FILE))
2979     {
2980         rt_kprintf("cp faild, cp dir to file is not permitted!\n");
2981         return ;
2982     }
2983 
2984     /* 3. do copy */
2985     if (flag & FLAG_SRC_IS_FILE)
2986     {
2987         if (flag & FLAG_DST_IS_DIR)
2988         {
2989             char *fdst;
2990             fdst = dfs_normalize_path(dst, _get_path_lastname(src));
2991             if (fdst == NULL)
2992             {
2993                 rt_kprintf("out of memory\n");
2994                 return;
2995             }
2996             copyfile(src, fdst);
2997             rt_free(fdst);
2998         }
2999         else
3000         {
3001             copyfile(src, dst);
3002         }
3003     }
3004     else /* flag & FLAG_SRC_IS_DIR */
3005     {
3006         if (flag & FLAG_DST_IS_DIR)
3007         {
3008             char *fdst;
3009             fdst = dfs_normalize_path(dst, _get_path_lastname(src));
3010             if (fdst == NULL)
3011             {
3012                 rt_kprintf("out of memory\n");
3013                 return;
3014             }
3015             mkdir(fdst, 0);
3016             copydir(src, fdst);
3017             rt_free(fdst);
3018         }
3019         else if ((flag & FLAG_DST_TYPE) == FLAG_DST_NON_EXSIT)
3020         {
3021             mkdir(dst, 0);
3022             copydir(src, dst);
3023         }
3024         else
3025         {
3026             copydir(src, dst);
3027         }
3028     }
3029 }
3030 FINSH_FUNCTION_EXPORT(copy, copy file or dir)
3031 
3032 #endif