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