1 /*
2  * Copyright (c) 2006-2021, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2012-11-27     prife        the first version
9  * 2013-03-03     aozima       add dfs_win32_stat st_mtime support.
10  * 2017-10-20     urey         support rt-thread 3.0
11  */
12 #include <rtthread.h>
13 
14 #include <dfs_fs.h>
15 #include <dfs_file.h>
16 #include <rtdevice.h>
17 
18 #include <io.h>
19 #include <fcntl.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <WinError.h>
24 #include <windows.h>
25 
26 /*
27  * RT-Thread DFS Interface for win-directory as an disk device
28  */
29 #define FILE_PATH_MAX           256  /* the longest file path */
30 
31 #define WIN32_DIRDISK_ROOT  "./disk"
32 
33 typedef struct {
34     HANDLE handle;
35     char *start;
36     char *end;
37     char *curr;
38     struct _finddata_t finddata;
39 } WINDIR;
40 
41 /* There are so many error codes in windows, you'd better google for details.
42  * google  "System Error Codes (Windows)"
43  * http://msdn.microsoft.com/ZH-CN/library/windows/desktop/ms681381(v=vs.85).aspx
44  */
45 
46 struct _errcode_map
47 {
48     rt_uint16_t dfserr;
49     rt_uint16_t win32err;
50 };
51 
52 static const struct _errcode_map errcode_table[] =
53 {
54     {ENOENT, ERROR_FILE_NOT_FOUND },
55     {ENOENT, ERROR_PATH_NOT_FOUND },
56     {EEXIST, ERROR_FILE_EXISTS },
57     {EEXIST, ERROR_ALREADY_EXISTS },
58     {ENOTEMPTY, ERROR_DIR_NOT_EMPTY },
59     {EBUSY, ERROR_PATH_BUSY },
60     {EINVAL, ERROR_ACCESS_DENIED },
61 
62 #if 0 /* TODO: MORE NEED BE ADDED */
63     {DFS_STATUS_EISDIR, ERROR_FILE_EXISTS },
64     {DFS_STATUS_ENOTDIR, ERROR_FILE_EXISTS },
65     {DFS_STATUS_EBADF, ERROR_FILE_EXISTS },
66     {DFS_STATUS_EBUSY, ERROR_FILE_EXISTS },
67     {DFS_STATUS_ENOMEM, ERROR_FILE_EXISTS },
68     {DFS_STATUS_ENOSPC, ERROR_FILE_EXISTS },
69 #endif
70 };
win32_result_to_dfs(DWORD res)71 static int win32_result_to_dfs(DWORD res)
72 {
73     int i;
74     int err = 0;
75     for (i = 0; i < sizeof(errcode_table) / sizeof(struct _errcode_map); i++)
76     {
77         if (errcode_table[i].win32err == (res & 0xFFFF))
78         {
79             err = -errcode_table[i].dfserr;
80             return err;
81         }
82     }
83 
84     /* unknown error */
85     rt_kprintf("dfs win32 error not supported yet: %d\n", res);
86     return -1;
87 }
88 
dfs_win32_mount(struct dfs_filesystem * fs,unsigned long rwflag,const void * data)89 static int dfs_win32_mount(
90     struct dfs_filesystem *fs,
91     unsigned long rwflag,
92     const void *data)
93 {
94     return 0;
95 }
96 
dfs_win32_unmount(struct dfs_filesystem * fs)97 static int dfs_win32_unmount(struct dfs_filesystem *fs)
98 {
99     return 0;
100 }
101 
dfs_win32_mkfs(rt_device_t devid)102 static int dfs_win32_mkfs(rt_device_t devid)
103 {
104     return -ENOSYS;
105 }
106 
dfs_win32_statfs(struct dfs_filesystem * fs,struct statfs * buf)107 static int dfs_win32_statfs(struct dfs_filesystem *fs,
108                             struct statfs *buf)
109 {
110     return -ENOSYS;
111 }
112 
winpath_dirdup(char * des,const char * src)113 static char *winpath_dirdup(char *des, const char *src)
114 {
115     char *path;
116     int i = 0;
117 
118     path = rt_malloc(FILE_PATH_MAX);
119     if (path == RT_NULL)
120         return RT_NULL;
121 
122     strcpy(path, des);
123     strcat(path, src);
124 
125     while (1)
126     {
127         if (path[i] == 0)
128             break;
129 
130         if (path[i] == '/')
131             path[i] = '\\';
132 
133         i++;
134     }
135 
136     return path;
137 }
138 
139 /* This function can convert the path in rt-thread/dfs to the path in windows */
dfs_win32_dirdup(char * path)140 char * dfs_win32_dirdup(char * path)
141 {
142     char * file_path;
143     file_path = winpath_dirdup(WIN32_DIRDISK_ROOT, path);
144     return file_path;
145 }
146 
dfs_win32_open(struct dfs_file * file)147 static int dfs_win32_open(struct dfs_file *file)
148 {
149     int fd;
150     uint32_t oflag, mode;
151     char *file_path;
152     int res;
153 
154     oflag = file->flags;
155     if (oflag & O_DIRECTORY)   /* operations about dir */
156     {
157         WINDIR *wdirp;
158         HANDLE handle;
159         int len;
160 
161         file_path = winpath_dirdup(WIN32_DIRDISK_ROOT, file->vnode->path);
162 
163         if (oflag & O_CREAT)   /* create a dir*/
164         {
165             res = CreateDirectory(file_path, NULL);
166             if (res == 0)
167             {
168                 rt_free(file_path);
169                 return win32_result_to_dfs(GetLastError());
170             }
171         }
172 
173         len = strlen(file_path);
174         if (file_path[len - 1] != '\\')
175         {
176             file_path[len] = '\\';
177             file_path[len + 1] = 0;
178         }
179 
180         strcat(file_path, "*.*");
181         /* _findfirst will get '.' */
182         /* save this pointer,will used by  dfs_win32_getdents*/
183         wdirp = rt_malloc(sizeof(WINDIR));
184         RT_ASSERT(wdirp != NULL);
185         if ((handle = _findfirst(file_path, &wdirp->finddata)) == -1)
186         {
187             rt_free(wdirp);
188             rt_free(file_path);
189             goto __err;
190         }
191         len = strlen(wdirp->finddata.name) + 1;
192         wdirp->handle = handle;
193         //wdirp->nfiles = 1;
194         wdirp->start = (char *)malloc(len); //not rt_malloc!
195         wdirp->end = wdirp->curr = wdirp->start;
196         wdirp->end += len;
197         rt_strncpy(wdirp->curr, wdirp->finddata.name, len);
198 
199         file->vnode->data = (void *)wdirp;
200         rt_free(file_path);
201         return 0;
202     }
203     /* regular file operations */
204     mode = O_BINARY;
205     if (oflag & O_RDONLY) mode |= O_RDONLY;
206     if (oflag & O_WRONLY) mode |= O_WRONLY;
207     if (oflag & O_RDWR)   mode |= O_RDWR;
208     /* Opens the file, if it is existing. If not, a new file is created. */
209     if (oflag & O_CREAT) mode |= O_CREAT;
210     /* Creates a new file. If the file is existing, it is truncated and overwritten. */
211     if (oflag & O_TRUNC) mode |= O_TRUNC;
212     /* Creates a new file. The function fails if the file is already existing. */
213     if (oflag & O_EXCL) mode |= O_EXCL;
214 
215     file_path = winpath_dirdup(WIN32_DIRDISK_ROOT, file->vnode->path);
216     fd = _open(file_path, mode, 0x0100 | 0x0080); /* _S_IREAD | _S_IWRITE */
217     rt_free(file_path);
218 
219     if (fd < 0)
220         goto __err;
221 
222     /* save this pointer, it will be used when calling read(), write(),
223      * flush(), seek(), and will be free when calling close()*/
224     file->data = (void *)fd;
225     file->pos  = 0;
226     file->vnode->size = _lseek(fd, 0, SEEK_END);
227 
228     if (oflag & O_APPEND)
229     {
230         file->pos = file->vnode->size;
231     }
232     else
233         _lseek(fd, 0, SEEK_SET);
234 
235     return 0;
236 
237 __err:
238     res = GetLastError();
239     return win32_result_to_dfs(res);
240 }
241 
dfs_win32_close(struct dfs_file * file)242 static int dfs_win32_close(struct dfs_file *file)
243 {
244     if (file->flags & O_DIRECTORY)
245     {
246         WINDIR *wdirp = (WINDIR*)(file->vnode->data);
247         RT_ASSERT(wdirp != RT_NULL);
248         if (_findclose((intptr_t)wdirp->handle) == 0) {
249             free(wdirp->start); /* NOTE: here we don't use rt_free! */
250             rt_free(wdirp);
251             return 0;
252         }
253     }
254     else /* regular file operations */
255     {
256         if (_close((int)(file->data)) == 0)
257             return 0;
258     }
259 
260     return win32_result_to_dfs(GetLastError());
261 }
262 
dfs_win32_ioctl(struct dfs_file * file,int cmd,void * args)263 static int dfs_win32_ioctl(struct dfs_file *file, int cmd, void *args)
264 {
265     return -ENOSYS;
266 }
267 
dfs_win32_read(struct dfs_file * file,void * buf,size_t len)268 static int dfs_win32_read(struct dfs_file *file, void *buf, size_t len)
269 {
270     int fd;
271     int char_read;
272 
273     fd = (int)(file->data);
274     char_read = _read(fd, buf, len);
275     if (char_read < 0)
276         return win32_result_to_dfs(GetLastError());
277 
278     /* update position */
279     file->pos = _lseek(fd, 0, SEEK_CUR);
280     return char_read;
281 }
282 
dfs_win32_write(struct dfs_file * file,const void * buf,size_t len)283 static int dfs_win32_write(struct dfs_file *file, const void *buf, size_t len)
284 {
285     int fd;
286     int char_write;
287 
288     fd = (int)(file->data);
289 
290     char_write = _write(fd, buf, len);
291     if (char_write < 0)
292         return win32_result_to_dfs(GetLastError());
293 
294     /* update position */
295     file->pos = _lseek(fd, 0, SEEK_CUR);
296     return char_write;
297 }
298 
dfs_win32_flush(struct dfs_file * file)299 static int dfs_win32_flush(struct dfs_file *file)
300 {
301     return 0;
302 }
303 
dfs_win32_seek(struct dfs_file * file,rt_off_t offset)304 static int dfs_win32_seek(struct dfs_file *file,
305                           rt_off_t offset)
306 {
307     int result;
308 
309     /* set offset as current offset */
310     if (file->vnode->type == FT_DIRECTORY)
311     {
312         WINDIR* wdirp = (WINDIR*)(file->vnode->data);
313         RT_ASSERT(wdirp != RT_NULL);
314         wdirp->curr = wdirp->start + offset;
315         return offset;
316     }
317     else //file->type == FT_REGULAR)
318     {
319         result = _lseek((int)(file->data), offset, SEEK_SET);
320         if (result >= 0)
321             return offset;
322         else
323             return win32_result_to_dfs(GetLastError());
324     }
325 }
326 
327 /* return the size of struct dirent*/
dfs_win32_getdents(struct dfs_file * file,struct dirent * dirp,rt_uint32_t count)328 static int dfs_win32_getdents(struct dfs_file *file, struct dirent *dirp, rt_uint32_t count)
329 {
330     WINDIR *wdirp;
331     struct dirent *d = dirp;
332     int result;
333 
334     /* make integer count */
335     if (count / sizeof(struct dirent) != 1)
336         return -EINVAL;
337 
338     wdirp = (WINDIR*)(file->vnode->data);
339     RT_ASSERT(wdirp != RT_NULL);
340     if (wdirp->curr == NULL) //no more entries in this directory
341         return 0;
342 
343     /* get the current entry */
344     if (wdirp->finddata.attrib & _A_SUBDIR)
345         d->d_type = DT_DIR;
346     else
347         d->d_type = DT_REG;
348     d->d_namlen = (rt_uint8_t)strlen(wdirp->curr);
349     strncpy(d->d_name, wdirp->curr, DIRENT_NAME_MAX);
350     d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
351     wdirp->curr += (strlen(wdirp->curr) + 1);
352     file->pos = wdirp->curr - wdirp->start + sizeof(struct dirent);//NOTE!
353 
354     /* now set up for the next call to readdir */
355     if (wdirp->curr >= wdirp->end)
356     {
357         if (_findnext(wdirp->handle, &wdirp->finddata) == 0)
358         {
359             char* old_start = wdirp->start;
360             long name_len = strlen(wdirp->finddata.name) + 1;
361             wdirp->start = realloc(wdirp->start, wdirp->end - wdirp->start + name_len);
362             wdirp->curr = wdirp->start + (wdirp->curr - old_start);
363             wdirp->end = wdirp->curr + name_len;
364             rt_strcpy(wdirp->curr, wdirp->finddata.name);
365         }
366         else
367         {
368             if ((result = GetLastError()) == ERROR_NO_MORE_FILES)
369                 wdirp->curr = NULL;
370             else
371                 return win32_result_to_dfs(result);
372         }
373     }
374 
375     return sizeof(struct dirent);
376 }
377 
dfs_win32_unlink(struct dfs_filesystem * fs,const char * path)378 static int dfs_win32_unlink(struct dfs_filesystem *fs, const char *path)
379 {
380     int result;
381     char *fp;
382     fp = winpath_dirdup(WIN32_DIRDISK_ROOT, path);
383     if (fp == RT_NULL)
384     {
385         rt_kprintf("out of memory.\n");
386         return -ENOMEM;
387     }
388 
389     result = GetFileAttributes(fp);
390     if (result == INVALID_FILE_ATTRIBUTES)
391         goto __err;
392 
393     if (result & FILE_ATTRIBUTE_DIRECTORY)//winnt.h
394     {
395         if (RemoveDirectory(fp) == RT_FALSE)
396             goto __err;
397     }
398     else //(result & FILE_ATTRIBUTE_NORMAL)
399     {
400         if (_unlink(fp) < 0)
401             goto __err;
402     }
403 
404     rt_free(fp);
405     return 0;
406 __err:
407     rt_free(fp);
408     return win32_result_to_dfs(GetLastError());
409 }
410 
dfs_win32_rename(struct dfs_filesystem * fs,const char * oldpath,const char * newpath)411 static int dfs_win32_rename(
412     struct dfs_filesystem *fs,
413     const char *oldpath,
414     const char *newpath)
415 {
416     int result;
417     char *op, *np;
418     op = winpath_dirdup(WIN32_DIRDISK_ROOT, oldpath);
419     np = winpath_dirdup(WIN32_DIRDISK_ROOT, newpath);
420     if (op == RT_NULL || np == RT_NULL)
421     {
422         rt_kprintf("out of memory.\n");
423         return -ENOMEM;
424     }
425 
426     /* If the function fails, the return value is zero. */
427     result = MoveFile(op, np);
428 
429     rt_free(op);
430     rt_free(np);
431 
432     if (result == 0)
433         return win32_result_to_dfs(GetLastError());
434 
435     return 0;
436 }
437 
dfs_win32_stat(struct dfs_filesystem * fs,const char * path,struct stat * st)438 static int dfs_win32_stat(struct dfs_filesystem *fs, const char *path, struct stat *st)
439 {
440     WIN32_FIND_DATA fileInfo;
441     HANDLE hFind;
442     char *fp;
443     fp = winpath_dirdup(WIN32_DIRDISK_ROOT, path);
444     if (fp == RT_NULL)
445     {
446         rt_kprintf("out of memory.\n");
447         return -ENOMEM;
448     }
449 
450     hFind = FindFirstFile(fp, &fileInfo);
451     rt_free(fp);
452 
453     if (hFind == INVALID_HANDLE_VALUE)
454         goto __err;
455 
456     st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH |
457                   S_IWUSR | S_IWGRP | S_IWOTH;
458 
459     /* convert file info to dfs stat structure */
460     if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
461     {
462         st->st_mode &= ~S_IFREG;
463         st->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
464     }
465 
466     st->st_dev  = 0;
467     st->st_size = fileInfo.nFileSizeLow;
468 
469     /* get st_mtime. */
470     {
471         LARGE_INTEGER time_tmp;
472         time_tmp.LowPart = fileInfo.ftLastWriteTime.dwLowDateTime;
473         time_tmp.HighPart = fileInfo.ftLastWriteTime.dwHighDateTime;
474 
475         /* removes the diff between 1970 and 1601. */
476         time_tmp.QuadPart -= 11644473600000 * 10000;
477         /* converts back from 100-nanoseconds to seconds. */
478         time_tmp.QuadPart /= 10UL * 1000 * 1000;
479 
480         st->st_mtime = time_tmp.QuadPart;
481     }
482 
483     FindClose(hFind);
484 
485     return 0;
486 
487 __err:
488     return win32_result_to_dfs(GetLastError());
489 }
490 
491 static const struct dfs_file_ops dfs_win32_file_ops =
492 {
493     dfs_win32_open,
494     dfs_win32_close,
495     dfs_win32_ioctl,
496     dfs_win32_read,
497     dfs_win32_write,
498     dfs_win32_flush,
499     dfs_win32_seek,
500     dfs_win32_getdents,
501 };
502 
503 static const struct dfs_filesystem_ops dfs_win32_ops =
504 {
505     "wdir", /* file system type: dir */
506     DFS_FS_FLAG_DEFAULT,
507     &dfs_win32_file_ops,
508     dfs_win32_mount,
509     dfs_win32_unmount,
510     dfs_win32_mkfs,
511     dfs_win32_statfs,
512     dfs_win32_unlink,
513     dfs_win32_stat,
514     dfs_win32_rename,
515 };
516 
dfs_win32_init(void)517 int dfs_win32_init(void)
518 {
519     /* register uffs file system */
520     dfs_register(&dfs_win32_ops);
521 
522     return 0;
523 }
524 
nop_init(rt_device_t dev)525 static rt_err_t nop_init(rt_device_t dev)
526 {
527     return RT_EOK;
528 }
529 
nop_open(rt_device_t dev,rt_uint16_t oflag)530 static rt_err_t nop_open(rt_device_t dev, rt_uint16_t oflag)
531 {
532     return RT_EOK;
533 }
534 
nop_close(rt_device_t dev)535 static rt_err_t nop_close(rt_device_t dev)
536 {
537     return RT_EOK;
538 }
539 
nop_read(rt_device_t dev,rt_off_t pos,void * buffer,rt_size_t size)540 static rt_ssize_t nop_read(rt_device_t dev,
541                           rt_off_t    pos,
542                           void       *buffer,
543                           rt_size_t   size)
544 {
545     return size;
546 }
547 
nop_write(rt_device_t dev,rt_off_t pos,const void * buffer,rt_size_t size)548 static rt_ssize_t nop_write(rt_device_t dev,
549                            rt_off_t    pos,
550                            const void *buffer,
551                            rt_size_t   size)
552 {
553     return size;
554 }
555 
nop_control(rt_device_t dev,int cmd,void * args)556 static rt_err_t nop_control(rt_device_t dev, int cmd, void *args)
557 {
558     return RT_EOK;
559 }
560 
561 static struct rt_device win_sharedir_dev;
rt_win_sharedir_init(const char * name)562 rt_err_t rt_win_sharedir_init(const char *name)
563 {
564     rt_device_t dev;
565 
566     dev = &win_sharedir_dev;
567     RT_ASSERT(dev != RT_NULL);
568 
569     /* set device class and generic device interface */
570     dev->type        = RT_Device_Class_Block;
571     dev->init        = nop_init;
572     dev->open        = nop_open;
573     dev->read        = nop_read;
574     dev->write       = nop_write;
575     dev->close       = nop_close;
576     dev->control     = nop_control;
577 
578     dev->rx_indicate = RT_NULL;
579     dev->tx_complete = RT_NULL;
580 
581     /* register to RT-Thread device system */
582     return rt_device_register(dev, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
583 }
584