1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2015-09-25     Bernard      the first verion for FinSH
9  * 2021-06-09     Meco Man     implement tail command
10  */
11 
12 #include <rtthread.h>
13 
14 #if defined(RT_USING_FINSH) && defined(DFS_USING_POSIX)
15 
16 #include <finsh.h>
17 #include "msh.h"
18 #include <dfs_file.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 #ifdef RT_USING_DFS_V2
22 #include <dfs_mnt.h>
23 #endif
24 
25 #ifdef RT_USING_SMART
26 #include "lwp.h"
27 #endif /* RT_USING_SMART */
28 
msh_readline(int fd,char * line_buf,int size)29 static int msh_readline(int fd, char *line_buf, int size)
30 {
31     char ch;
32     int index = 0;
33 
34     do
35     {
36         if (read(fd, &ch, 1) != 1)
37         {
38             /* nothing in this file */
39             return 0;
40         }
41     }
42     while (ch == '\n' || ch == '\r');
43 
44     /* set the first character */
45     line_buf[index ++] = ch;
46 
47     while (index < size)
48     {
49         if (read(fd, &ch, 1) == 1)
50         {
51             if (ch == '\n' || ch == '\r')
52             {
53                 line_buf[index] = '\0';
54                 break;
55             }
56 
57             line_buf[index++] = ch;
58         }
59         else
60         {
61             line_buf[index] = '\0';
62             break;
63         }
64     }
65 
66     return index;
67 }
68 
msh_exec_script(const char * cmd_line,int size)69 int msh_exec_script(const char *cmd_line, int size)
70 {
71     int ret;
72     int fd = -1;
73     char *pg_name;
74     int length, cmd_length = 0;
75 
76     if (size == 0) return -RT_ERROR;
77 
78     /* get the length of command0 */
79     while ((cmd_line[cmd_length] != ' ' && cmd_line[cmd_length] != '\t') && cmd_length < size)
80         cmd_length ++;
81 
82     /* get name length */
83     length = cmd_length + 32;
84 
85     /* allocate program name memory */
86     pg_name = (char *) rt_malloc(length);
87     if (pg_name == RT_NULL) return -RT_ENOMEM;
88 
89     /* copy command0 */
90     rt_memcpy(pg_name, cmd_line, cmd_length);
91     pg_name[cmd_length] = '\0';
92 
93     if (strstr(pg_name, ".sh") != RT_NULL || strstr(pg_name, ".SH") != RT_NULL)
94     {
95         /* try to open program */
96         fd = open(pg_name, O_RDONLY, 0);
97 
98         /* search in /bin path */
99         if (fd < 0)
100         {
101             rt_snprintf(pg_name, length - 1, "/bin/%.*s", cmd_length, cmd_line);
102             fd = open(pg_name, O_RDONLY, 0);
103         }
104     }
105 
106     rt_free(pg_name);
107     if (fd >= 0)
108     {
109         /* found script */
110         char *line_buf;
111         int length;
112 
113         line_buf = (char *) rt_malloc(RT_CONSOLEBUF_SIZE);
114         if (line_buf == RT_NULL)
115         {
116             close(fd);
117             return -RT_ENOMEM;
118         }
119 
120         /* read line by line and then exec it */
121         do
122         {
123             length = msh_readline(fd, line_buf, RT_CONSOLEBUF_SIZE);
124             if (length > 0)
125             {
126                 char ch = '\0';
127                 int index;
128 
129                 for (index = 0; index < length; index ++)
130                 {
131                     ch = line_buf[index];
132                     if (ch == ' ' || ch == '\t') continue;
133                     else break;
134                 }
135 
136                 if (ch != '#') /* not a comment */
137                     msh_exec(line_buf, length);
138             }
139         }
140         while (length > 0);
141 
142         close(fd);
143         rt_free(line_buf);
144 
145         ret = 0;
146     }
147     else
148     {
149         ret = -1;
150     }
151 
152     return ret;
153 }
154 
155 #ifdef DFS_USING_WORKDIR
156     extern char working_directory[];
157 #endif
158 
cmd_ls(int argc,char ** argv)159 static int cmd_ls(int argc, char **argv)
160 {
161     extern void ls(const char *pathname);
162 
163     if (argc == 1)
164     {
165 #ifdef DFS_USING_WORKDIR
166 #ifdef RT_USING_SMART
167         ls(lwp_getcwd());
168 #else
169         ls(working_directory);
170 #endif
171 #else
172         ls("/");
173 #endif
174     }
175     else
176     {
177         ls(argv[1]);
178     }
179 
180     return 0;
181 }
182 MSH_CMD_EXPORT_ALIAS(cmd_ls, ls, List information about the FILEs.);
183 
184 #ifdef RT_USING_DFS_V2
cmd_ln(int argc,char ** argv)185 static int cmd_ln(int argc, char **argv)
186 {
187     if (argc < 3)
188     {
189         rt_kprintf("Usage: ln target link_name\n");
190         rt_kprintf("Make symbolic link between files.\n");
191     }
192     else
193     {
194         for(int i = 0; i + 3 <= argc; i ++)
195         {
196             dfs_file_symlink(argv[1], argv[2 + i]);
197         }
198     }
199 
200     return 0;
201 }
202 MSH_CMD_EXPORT_ALIAS(cmd_ln, ln, Make symbolic link between files);
203 
cmd_link(int argc,char ** argv)204 static int cmd_link(int argc, char **argv)
205 {
206     if (argc < 3)
207     {
208         rt_kprintf("Usage: link target link_name\n");
209         rt_kprintf("Make link between files.\n");
210     }
211     else
212     {
213         for(int i = 0; i + 3 <= argc; i ++)
214         {
215             dfs_file_link(argv[1], argv[2 + i]);
216         }
217     }
218 
219     return 0;
220 }
221 MSH_CMD_EXPORT_ALIAS(cmd_link, link, Make link between files);
222 #endif
223 
cmd_cp(int argc,char ** argv)224 static int cmd_cp(int argc, char **argv)
225 {
226     void copy(const char *src, const char *dst);
227 
228     if (argc != 3)
229     {
230         rt_kprintf("Usage: cp SOURCE DEST\n");
231         rt_kprintf("Copy SOURCE to DEST.\n");
232     }
233     else
234     {
235         copy(argv[1], argv[2]);
236     }
237 
238     return 0;
239 }
240 MSH_CMD_EXPORT_ALIAS(cmd_cp, cp, Copy SOURCE to DEST.);
241 
cmd_mv(int argc,char ** argv)242 static int cmd_mv(int argc, char **argv)
243 {
244     if (argc != 3)
245     {
246         rt_kprintf("Usage: mv SOURCE DEST\n");
247         rt_kprintf("Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n");
248     }
249     else
250     {
251         int fd;
252         char *dest = RT_NULL;
253 
254         rt_kprintf("%s => %s\n", argv[1], argv[2]);
255 
256         fd = open(argv[2], O_DIRECTORY, 0);
257         if (fd >= 0)
258         {
259             char *src;
260 
261             close(fd);
262 
263             /* it's a directory */
264             dest = (char *)rt_malloc(DFS_PATH_MAX);
265             if (dest == RT_NULL)
266             {
267                 rt_kprintf("out of memory\n");
268                 return -RT_ENOMEM;
269             }
270 
271             src = argv[1] + rt_strlen(argv[1]);
272             while (src != argv[1])
273             {
274                 if (*src == '/') break;
275                 src --;
276             }
277 
278             rt_snprintf(dest, DFS_PATH_MAX - 1, "%s/%s", argv[2], src);
279         }
280         else
281         {
282             fd = open(argv[2], O_RDONLY, 0);
283             if (fd >= 0)
284             {
285                 close(fd);
286 
287                 unlink(argv[2]);
288             }
289 
290             dest = argv[2];
291         }
292 
293         rename(argv[1], dest);
294         if (dest != RT_NULL && dest != argv[2]) rt_free(dest);
295     }
296 
297     return 0;
298 }
299 MSH_CMD_EXPORT_ALIAS(cmd_mv, mv, Rename SOURCE to DEST.);
300 
cmd_cat(int argc,char ** argv)301 static int cmd_cat(int argc, char **argv)
302 {
303     int index;
304     extern void cat(const char *filename);
305 
306     if (argc == 1)
307     {
308         rt_kprintf("Usage: cat [FILE]...\n");
309         rt_kprintf("Concatenate FILE(s)\n");
310         return 0;
311     }
312 
313     for (index = 1; index < argc; index ++)
314     {
315         cat(argv[index]);
316     }
317 
318     return 0;
319 }
320 MSH_CMD_EXPORT_ALIAS(cmd_cat, cat, Concatenate FILE(s));
321 
directory_delete_for_msh(const char * pathname,char f,char v)322 static void directory_delete_for_msh(const char *pathname, char f, char v)
323 {
324     DIR *dir = NULL;
325     struct dirent *dirent = NULL;
326     char *full_path;
327 
328     if (pathname == RT_NULL)
329         return;
330 
331     full_path = (char *)rt_malloc(DFS_PATH_MAX);
332     if (full_path == RT_NULL)
333         return;
334 
335     dir = opendir(pathname);
336     if (dir == RT_NULL)
337     {
338         if (f == 0)
339         {
340             rt_kprintf("cannot remove '%s'\n", pathname);
341         }
342         rt_free(full_path);
343         return;
344     }
345 
346     while (1)
347     {
348         dirent = readdir(dir);
349         if (dirent == RT_NULL)
350             break;
351         if (rt_strcmp(".", dirent->d_name) != 0 &&
352                 rt_strcmp("..", dirent->d_name) != 0)
353         {
354             if (strlen(pathname) + 1 + strlen(dirent->d_name) > DFS_PATH_MAX)
355             {
356                 rt_kprintf("cannot remove '%s/%s', path too long.\n", pathname, dirent->d_name);
357                 continue;
358             }
359             rt_sprintf(full_path, "%s/%s", pathname, dirent->d_name);
360             if (dirent->d_type != DT_DIR)
361             {
362                 if (unlink(full_path) != 0)
363                 {
364                     if (f == 0)
365                         rt_kprintf("cannot remove '%s'\n", full_path);
366                 }
367                 else if (v)
368                 {
369                     rt_kprintf("removed '%s'\n", full_path);
370                 }
371             }
372             else
373             {
374                 directory_delete_for_msh(full_path, f, v);
375             }
376         }
377     }
378     closedir(dir);
379     rt_free(full_path);
380     if (rmdir(pathname) != 0)
381     {
382         if (f == 0)
383             rt_kprintf("cannot remove '%s'\n", pathname);
384     }
385     else if (v)
386     {
387         rt_kprintf("removed directory '%s'\n", pathname);
388     }
389 }
390 
cmd_rm(int argc,char ** argv)391 static int cmd_rm(int argc, char **argv)
392 {
393     int index, n;
394     char f = 0, r = 0, v = 0;
395 
396     if (argc == 1)
397     {
398         rt_kprintf("Usage: rm option(s) FILE...\n");
399         rt_kprintf("Remove (unlink) the FILE(s).\n");
400         return 0;
401     }
402 
403     if (argv[1][0] == '-')
404     {
405         for (n = 0; argv[1][n]; n++)
406         {
407             switch (argv[1][n])
408             {
409             case 'f':
410                 f = 1;
411                 break;
412             case 'r':
413                 r = 1;
414                 break;
415             case 'v':
416                 v = 1;
417                 break;
418             case '-':
419                 break;
420             default:
421                 rt_kprintf("Error: Bad option: %c\n", argv[1][n]);
422                 return 0;
423             }
424         }
425         argc -= 1;
426         argv = argv + 1;
427     }
428 
429     for (index = 1; index < argc; index ++)
430     {
431         struct stat s;
432 #ifdef RT_USING_DFS_V2
433         if (dfs_file_lstat(argv[index], &s) == 0)
434 #else
435         if (stat(argv[index], &s) == 0)
436 #endif
437         {
438             if (S_ISDIR(s.st_mode))
439             {
440                 if (r == 0)
441                     rt_kprintf("cannot remove '%s': Is a directory\n", argv[index]);
442                 else
443                     directory_delete_for_msh(argv[index], f, v);
444             }
445             else
446             {
447                 if (unlink(argv[index]) != 0)
448                 {
449                     if (f == 0)
450                         rt_kprintf("cannot remove '%s'\n", argv[index]);
451                 }
452                 else if (v)
453                 {
454                     rt_kprintf("removed '%s'\n", argv[index]);
455                 }
456             }
457         }
458         else if (f == 0)
459         {
460             rt_kprintf("cannot remove '%s': No such file or directory\n", argv[index]);
461         }
462     }
463     return 0;
464 }
465 MSH_CMD_EXPORT_ALIAS(cmd_rm, rm, Remove(unlink) the FILE(s).);
466 
467 #ifdef DFS_USING_WORKDIR
cmd_cd(int argc,char ** argv)468 static int cmd_cd(int argc, char **argv)
469 {
470     if (argc == 1)
471     {
472         rt_kprintf("%s\n", working_directory);
473     }
474     else if (argc == 2)
475     {
476         if (chdir(argv[1]) != 0)
477         {
478             rt_kprintf("No such directory: %s\n", argv[1]);
479         }
480     }
481 
482     return 0;
483 }
484 MSH_CMD_EXPORT_ALIAS(cmd_cd, cd, Change the shell working directory.);
485 
cmd_pwd(int argc,char ** argv)486 static int cmd_pwd(int argc, char **argv)
487 {
488     rt_kprintf("%s\n", working_directory);
489     return 0;
490 }
491 MSH_CMD_EXPORT_ALIAS(cmd_pwd, pwd, Print the name of the current working directory.);
492 #endif
493 
cmd_mkdir(int argc,char ** argv)494 static int cmd_mkdir(int argc, char **argv)
495 {
496     if (argc == 1)
497     {
498         rt_kprintf("Usage: mkdir [OPTION] DIRECTORY\n");
499         rt_kprintf("Create the DIRECTORY, if they do not already exist.\n");
500     }
501     else
502     {
503         mkdir(argv[1], 0);
504     }
505 
506     return 0;
507 }
508 MSH_CMD_EXPORT_ALIAS(cmd_mkdir, mkdir, Create the DIRECTORY.);
509 
cmd_mkfs(int argc,char ** argv)510 static int cmd_mkfs(int argc, char **argv)
511 {
512     int result = 0;
513     char *type = "elm"; /* use the default file system type as 'fatfs' */
514 
515     if (argc == 2)
516     {
517         result = dfs_mkfs(type, argv[1]);
518     }
519     else if (argc == 4)
520     {
521         if (strcmp(argv[1], "-t") == 0)
522         {
523             type = argv[2];
524             result = dfs_mkfs(type, argv[3]);
525         }
526     }
527     else
528     {
529         rt_kprintf("Usage: mkfs [-t type] device\n");
530         return 0;
531     }
532 
533     if (result != RT_EOK)
534     {
535         rt_kprintf("mkfs failed, result=%d\n", result);
536     }
537 
538     return 0;
539 }
540 MSH_CMD_EXPORT_ALIAS(cmd_mkfs, mkfs, format disk with file system);
541 
542 /*
543  * If no argument is specified, display the mount history;
544  * If there are 3 arguments, mount the filesystem.
545  * The order of the arguments is:
546  * argv[1]: device name
547  * argv[2]: mountpoint path
548  * argv[3]: filesystem type
549  */
cmd_mount(int argc,char ** argv)550 static int cmd_mount(int argc, char **argv)
551 {
552     if (argc == 1)
553     {
554 #ifdef RT_USING_DFS_V2
555         /* display the mount history */
556         rt_kprintf("filesystem  device  mountpoint  refcount\n");
557         rt_kprintf("----------  ------  ----------  --------\n");
558 
559         dfs_mnt_list(RT_NULL);
560 #else
561         extern struct dfs_filesystem filesystem_table[];
562         struct dfs_filesystem *iter;
563 
564         /* display the mount history */
565         rt_kprintf("filesystem  device  mountpoint\n");
566         rt_kprintf("----------  ------  ----------\n");
567         for (iter = &filesystem_table[0];
568                 iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++)
569         {
570             if ((iter != NULL) && (iter->path != NULL))
571             {
572                 rt_kprintf("%-10s  %-6s  %-s\n",
573                            iter->ops->name, iter->dev_id->parent.name, iter->path);
574             }
575         }
576 #endif
577         return 0;
578     }
579     else if (argc == 4)
580     {
581         char *device = argv[1];
582         char *path = argv[2];
583         char *fstype = argv[3];
584         char *data = 0;
585 
586         /* mount a filesystem to the specified directory */
587         rt_kprintf("mount device %s(%s) onto %s ... ", device, fstype, path);
588         if (strcmp(fstype, "nfs") == 0)
589         {
590             data = argv[1];
591             device = 0;
592         }
593 
594         if (dfs_mount(device, path, fstype, 0, data) == 0)
595         {
596             rt_kprintf("succeed!\n");
597             return 0;
598         }
599         else
600         {
601             rt_kprintf("failed!\n");
602             return -1;
603         }
604     }
605     else if (argc == 3)
606     {
607         char *path = argv[1];
608         char *fstype = argv[2];
609 
610         /* mount a filesystem to the specified directory */
611         rt_kprintf("mount (%s) onto %s ... ", fstype, path);
612         if (dfs_mount(NULL, path, fstype, 0, 0) == 0)
613         {
614             rt_kprintf("succeed!\n");
615             return 0;
616         }
617         else
618         {
619             rt_kprintf("failed!\n");
620             return -1;
621         }
622     }
623     else
624     {
625         rt_kprintf("Usage: mount <device> <mountpoint> <fstype>.\n");
626         return -1;
627     }
628 }
629 MSH_CMD_EXPORT_ALIAS(cmd_mount, mount, mount <device> <mountpoint> <fstype>);
630 
631 /* unmount the filesystem from the specified mountpoint */
cmd_umount(int argc,char ** argv)632 static int cmd_umount(int argc, char **argv)
633 {
634 #ifndef RT_USING_DFS_V2
635     char *path = argv[1];
636 
637     if (argc != 2)
638     {
639         rt_kprintf("Usage: unmount <mountpoint>.\n");
640         return -1;
641     }
642 
643     rt_kprintf("unmount %s ... ", path);
644     if (dfs_unmount(path) < 0)
645     {
646         rt_kprintf("failed!\n");
647         return -1;
648     }
649     else
650     {
651         rt_kprintf("succeed!\n");
652         return 0;
653     }
654 #else
655     int flags = 0;
656     char *path = argv[1];
657 
658     if (argc < 2)
659     {
660         rt_kprintf("Usage: unmount [-f] <mountpoint>.\n");
661         return -1;
662     }
663 
664     if (argc > 2)
665     {
666         flags = strcmp(argv[1], "-f") == 0 ? MNT_FORCE : 0;
667         path  = argv[2];
668     }
669 
670     rt_kprintf("unmount %s ... ", path);
671     if (dfs_umount(path, flags) < 0)
672     {
673         rt_kprintf("failed!\n");
674         return -1;
675     }
676     else
677     {
678         rt_kprintf("succeed!\n");
679         return 0;
680     }
681 #endif
682 }
683 MSH_CMD_EXPORT_ALIAS(cmd_umount, umount, Unmount the mountpoint);
684 
cmd_df(int argc,char ** argv)685 static int cmd_df(int argc, char **argv)
686 {
687 #ifndef RT_USING_DFS_V2
688     extern int df(const char *path);
689 
690     if (argc != 2)
691     {
692         df("/");
693     }
694     else
695     {
696         if ((strcmp(argv[1], "--help") == 0) || (strcmp(argv[1], "-h") == 0))
697         {
698             rt_kprintf("df [path]\n");
699         }
700         else
701         {
702             df(argv[1]);
703         }
704     }
705 #endif
706 
707     return 0;
708 }
709 MSH_CMD_EXPORT_ALIAS(cmd_df, df, disk free);
710 
cmd_echo(int argc,char ** argv)711 static int cmd_echo(int argc, char **argv)
712 {
713     if (argc == 2)
714     {
715         rt_kprintf("%s\n", argv[1]);
716     }
717     else if (argc == 3)
718     {
719         int fd;
720 
721         fd = open(argv[2], O_RDWR | O_APPEND | O_CREAT, 0);
722         if (fd >= 0)
723         {
724             write(fd, argv[1], strlen(argv[1]));
725             close(fd);
726         }
727         else
728         {
729             rt_kprintf("open file:%s failed!\n", argv[2]);
730         }
731     }
732     else
733     {
734         rt_kprintf("Usage: echo \"string\" [filename]\n");
735     }
736 
737     return 0;
738 }
739 MSH_CMD_EXPORT_ALIAS(cmd_echo, echo, echo string to file);
740 
cmd_tail(int argc,char ** argv)741 static int cmd_tail(int argc, char **argv)
742 {
743     int fd;
744     char c = RT_NULL;
745     char *file_name = RT_NULL;
746     rt_uint32_t total_lines = 0;
747     rt_uint32_t target_line = 0;
748     rt_uint32_t current_line = 0;
749     rt_uint32_t required_lines = 0;
750     rt_uint32_t start_line = 0;
751 
752     if (argc < 2)
753     {
754         rt_kprintf("Usage: tail [-n numbers] <filename>\n");
755         return -1;
756     }
757     else if (argc == 2)
758     {
759         required_lines = 10; /* default: 10 lines from tail */
760         file_name = argv[1];
761     }
762     else if (rt_strcmp(argv[1], "-n") == 0)
763     {
764         if (argv[2][0] != '+')
765         {
766             required_lines = atoi(argv[2]);
767         }
768         else
769         {
770             start_line = atoi(&argv[2][1]); /* eg: +100, to get the 100 */
771         }
772         file_name = argv[3];
773     }
774     else
775     {
776         rt_kprintf("Usage: tail [-n numbers] <filename>\n");
777         return -1;
778     }
779 
780     fd = open(file_name, O_RDONLY);
781     if (fd < 0)
782     {
783         rt_kprintf("File doesn't exist\n");
784         return -1;
785     }
786 
787     while ((read(fd, &c, sizeof(char))) > 0)
788     {
789         if(total_lines == 0)
790         {
791             total_lines++;
792         }
793         if (c == '\n')
794         {
795             total_lines++;
796         }
797     }
798 
799     rt_kprintf("\nTotal Number of lines:%d\n", total_lines);
800 
801     if (start_line != 0)
802     {
803         if (total_lines >= start_line)
804         {
805             required_lines = total_lines - start_line + 1;
806         }
807         else
808         {
809             rt_kprintf("\nError:Required lines are more than total number of lines\n");
810             close(fd);
811             return -1;
812         }
813     }
814 
815     if (required_lines > total_lines)
816     {
817         rt_kprintf("\nError:Required lines are more than total number of lines\n");
818         close(fd);
819         return -1;
820     }
821     rt_kprintf("Required Number of lines:%d\n", required_lines);
822 
823     target_line = total_lines - required_lines;
824     lseek(fd, 0, SEEK_SET); /* back to head */
825 
826     while ((read(fd, &c, sizeof(char))) > 0)
827     {
828         if (current_line >= target_line)
829         {
830             rt_kprintf("%c", c);
831         }
832         if (c == '\n')
833         {
834             current_line++;
835         }
836     }
837     rt_kprintf("\n");
838 
839     close(fd);
840     return 0;
841 }
842 MSH_CMD_EXPORT_ALIAS(cmd_tail, tail, print the last N - lines data of the given file);
843 
844 #ifdef RT_USING_DFS_V2
845 
directory_setattr(const char * pathname,struct dfs_attr * attr,char f,char v)846 static void directory_setattr(const char *pathname, struct dfs_attr *attr, char f, char v)
847 {
848     DIR *dir = NULL;
849     struct dirent *dirent = NULL;
850     char *full_path;
851 
852     if (pathname == RT_NULL)
853         return;
854 
855     full_path = (char *)rt_malloc(DFS_PATH_MAX);
856     if (full_path == RT_NULL)
857         return;
858 
859     dir = opendir(pathname);
860     if (dir == RT_NULL)
861     {
862         if (f == 0)
863         {
864             rt_kprintf("cannot open '%s'\n", pathname);
865         }
866         rt_free(full_path);
867         return;
868     }
869 
870     while (1)
871     {
872         dirent = readdir(dir);
873         if (dirent == RT_NULL)
874             break;
875         if (rt_strcmp(".", dirent->d_name) != 0 &&
876             rt_strcmp("..", dirent->d_name) != 0)
877         {
878             if (strlen(pathname) + 1 + strlen(dirent->d_name) > DFS_PATH_MAX)
879             {
880                 rt_kprintf("'%s/%s' setattr failed, path too long.\n", pathname, dirent->d_name);
881                 continue;
882             }
883             rt_sprintf(full_path, "%s/%s", pathname, dirent->d_name);
884             if (dirent->d_type == DT_REG)
885             {
886                 if (dfs_file_setattr(full_path, attr) != 0)
887                 {
888                     if (f == 0)
889                     {
890                         rt_kprintf("'%s' setattr failed, no such file or directory\n", full_path);
891                     }
892                 }
893                 else if (v)
894                 {
895                     rt_kprintf("'%s' setattr 0x%X\n", full_path, attr->st_mode);
896                 }
897             }
898             else if (dirent->d_type == DT_DIR)
899             {
900                 directory_setattr(full_path, attr, f, v);
901             }
902         }
903     }
904     closedir(dir);
905     rt_free(full_path);
906     if (dfs_file_setattr(pathname, attr) != 0)
907     {
908         if (f == 0)
909         {
910             rt_kprintf("'%s' setattr failed, no such file or directory\n", pathname);
911         }
912     }
913     else if (v)
914     {
915         rt_kprintf("'%s' setattr 0x%X\n", pathname, attr->st_mode);
916     }
917 }
918 
cmd_chmod(int argc,char ** argv)919 static int cmd_chmod(int argc, char **argv)
920 {
921     if (argc < 3)
922     {
923         rt_kprintf("Usage: chmod [OPTION]... MODE[,MODE]... FILE...\n");
924         rt_kprintf("  chmod [-f|v|r] [u|g|o|a][+/-/=][r|w|x] file...\n");
925         rt_kprintf("  -f  suppress most error messages\n");
926         rt_kprintf("  -v  output a diagnostic for every file processed\n");
927         rt_kprintf("  -r  change files and directories recursively\n");
928         rt_kprintf("Change the mode of each FILE to MODE.\n");
929     }
930     else
931     {
932         int argv_c = 1;
933         char f = 0, r = 0, v = 0;
934 
935         if (argv[argv_c][0] == '-')
936         {
937             for (int i = 1; argv[argv_c][i]; i++)
938             {
939                 switch (argv[argv_c][i])
940                 {
941                 case 'f':
942                     f = 1;
943                     break;
944                 case 'r':
945                     r = 1;
946                     break;
947                 case 'v':
948                     v = 1;
949                     break;
950                 default:
951                     rt_kprintf("Error: Bad option: %c\n", argv[argv_c][i]);
952                     return 0;
953                 }
954             }
955             argv_c++;
956         }
957 
958         if (argc - argv_c > 1)
959         {
960             int U = 1, G = 2, O = 4, ALL = 7;
961             int off[5] = {0, 6, 3, 0, 0};
962             int ADD = 1, SUB = 2, SET = 4;
963             int R = 4, W = 2, X = 1;
964             int user[3] = {0}, change[3] = {0}, mode[3] = {0};
965             struct dfs_attr attr;
966             char *cmd = argv[argv_c];
967             int index = 0, num = 0;
968 
969             while (cmd[index] != '\0')
970             {
971                 switch (cmd[index])
972                 {
973                 case 'u':
974                     user[num] |= U;
975                     break;
976                 case 'g':
977                     user[num] |= G;
978                     break;
979                 case 'o':
980                     user[num] |= O;
981                     break;
982                 case 'a':
983                     user[num] |= ALL;
984                     break;
985                 case ',':
986                     if (num < 2)
987                         num++;
988                     break;
989                 }
990                 index++;
991             }
992 
993             index = 0;
994             num = 0;
995 
996             while (cmd[index] != '\0')
997             {
998                 switch (cmd[index])
999                 {
1000                 case '+':
1001                     change[num] = ADD;
1002                     break;
1003                 case '-':
1004                     change[num] = SUB;
1005                     break;
1006                 case '=':
1007                     change[num] = SET;
1008                     break;
1009                 case ',':
1010                     if (num < 2)
1011                         num++;
1012                     break;
1013                 }
1014                 index++;
1015             }
1016 
1017             index = 0;
1018             num = 0;
1019 
1020             while (cmd[index] != '\0')
1021             {
1022                 switch (cmd[index])
1023                 {
1024                 case 'r':
1025                     mode[num] |= R;
1026                     break;
1027                 case 'w':
1028                     mode[num] |= W;
1029                     break;
1030                 case 'x':
1031                     mode[num] |= X;
1032                     break;
1033                 case ',':
1034                     if (num < 2)
1035                         num++;
1036                     break;
1037                 }
1038                 index++;
1039             }
1040 
1041             attr.st_mode = 0;
1042 
1043             for (int i = 0; i <= num; i++)
1044             {
1045                 if (change[i] == ADD)
1046                 {
1047                     if (user[i] & U)
1048                     {
1049                         attr.st_mode |= mode[i] << off[user[i] & U];
1050                     }
1051 
1052                     if (user[i] & G)
1053                     {
1054                         attr.st_mode |= mode[i] << off[user[i] & G];
1055                     }
1056 
1057                     if (user[i] & O)
1058                     {
1059                         attr.st_mode |= mode[i] << off[user[i] & O];
1060                     }
1061                 }
1062                 else if (change[i] == SUB)
1063                 {
1064                     if (user[i] & U)
1065                     {
1066                         attr.st_mode &= ~(mode[i] << off[user[i] & U]);
1067                     }
1068 
1069                     if (user[i] & G)
1070                     {
1071                         attr.st_mode &= ~(mode[i] << off[user[i] & G]);
1072                     }
1073 
1074                     if (user[i] & O)
1075                     {
1076                         attr.st_mode &= ~(mode[i] << off[user[i] & O]);
1077                     }
1078                 }
1079                 else if (change[i] == SET)
1080                 {
1081                     if (user[i] & U)
1082                     {
1083                         attr.st_mode &= ~(7 << off[user[i] & U]);
1084                         attr.st_mode |= mode[i] << off[user[i] & U];
1085                     }
1086 
1087                     if (user[i] & G)
1088                     {
1089                         attr.st_mode &= ~(7 << off[user[i] & G]);
1090                         attr.st_mode |= mode[i] << off[user[i] & G];
1091                     }
1092 
1093                     if (user[i] & O)
1094                     {
1095                         attr.st_mode &= ~(7 << off[user[i] & O]);
1096                         attr.st_mode |= mode[i] << off[user[i] & O];
1097                     }
1098                 }
1099             }
1100 
1101             argv_c++;
1102 
1103             for (int i = argv_c; i < argc; i++)
1104             {
1105                 if (r)
1106                 {
1107                     struct stat s;
1108                     if (stat(argv[i], &s) == 0)
1109                     {
1110                         if (S_ISDIR(s.st_mode))
1111                         {
1112                             directory_setattr(argv[i], &attr, f, v);
1113                         }
1114                         else if (f == 0)
1115                         {
1116                             rt_kprintf("'%s' is not a directory\n", argv[i]);
1117                         }
1118                     }
1119                 }
1120                 else
1121                 {
1122                     if (dfs_file_setattr(argv[i], &attr) != 0)
1123                     {
1124                         if (f == 0)
1125                         {
1126                             rt_kprintf("'%s' setattr failed, no such file or directory\n", argv[i]);
1127                         }
1128                     }
1129                     else if (v)
1130                     {
1131                         rt_kprintf("'%s' setattr 0x%X\n", argv[i], attr.st_mode);
1132                     }
1133                 }
1134             }
1135         }
1136     }
1137 
1138     return 0;
1139 }
1140 MSH_CMD_EXPORT_ALIAS(cmd_chmod, chmod, Change the file attr.);
1141 
1142 #endif
1143 
1144 #endif /* defined(RT_USING_FINSH) && defined(DFS_USING_POSIX) */
1145