601 lines
14 KiB
C
601 lines
14 KiB
C
/*
|
|
* Copyright (c) 2006-2021, RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2012-11-27 prife the first version
|
|
* 2013-03-03 aozima add dfs_win32_stat st_mtime support.
|
|
* 2017-10-20 urey support rt-thread 3.0
|
|
*/
|
|
#include <rtthread.h>
|
|
#include <rtlibc.h>
|
|
|
|
#include <dfs_fs.h>
|
|
#include <dfs_file.h>
|
|
#include <rtdevice.h>
|
|
|
|
#include <io.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <WinError.h>
|
|
#include <windows.h>
|
|
|
|
#if defined(__MINGW32__) && defined(_NO_OLDNAMES)
|
|
#define O_RDONLY _O_RDONLY
|
|
#define O_WRONLY _O_WRONLY
|
|
#define O_RDWR _O_RDWR
|
|
#define O_ACCMODE _O_ACCMODE
|
|
#define O_APPEND _O_APPEND
|
|
#define O_CREAT _O_CREAT
|
|
#define O_TRUNC _O_TRUNC
|
|
#define O_EXCL _O_EXCL
|
|
#define O_TEXT _O_TEXT
|
|
#define O_BINARY _O_BINARY
|
|
#define O_TEMPORARY _O_TEMPORARY
|
|
#define O_NOINHERIT _O_NOINHERIT
|
|
#define O_SEQUENTIAL _O_SEQUENTIAL
|
|
#define O_RANDOM _O_RANDOM
|
|
#endif
|
|
/*
|
|
* RT-Thread DFS Interface for win-directory as an disk device
|
|
*/
|
|
#define FILE_PATH_MAX 256 /* the longest file path */
|
|
|
|
#define WIN32_DIRDISK_ROOT "./disk"
|
|
|
|
typedef struct {
|
|
HANDLE handle;
|
|
char *start;
|
|
char *end;
|
|
char *curr;
|
|
struct _finddata_t finddata;
|
|
} WINDIR;
|
|
|
|
/* There are so many error codes in windows, you'd better google for details.
|
|
* google "System Error Codes (Windows)"
|
|
* http://msdn.microsoft.com/ZH-CN/library/windows/desktop/ms681381(v=vs.85).aspx
|
|
*/
|
|
|
|
struct _errcode_map
|
|
{
|
|
rt_uint16_t dfserr;
|
|
rt_uint16_t win32err;
|
|
};
|
|
|
|
static const struct _errcode_map errcode_table[] =
|
|
{
|
|
{ENOENT, ERROR_FILE_NOT_FOUND },
|
|
{ENOENT, ERROR_PATH_NOT_FOUND },
|
|
{EEXIST, ERROR_FILE_EXISTS },
|
|
{EEXIST, ERROR_ALREADY_EXISTS },
|
|
{ENOTEMPTY, ERROR_DIR_NOT_EMPTY },
|
|
{EBUSY, ERROR_PATH_BUSY },
|
|
{EINVAL, ERROR_ACCESS_DENIED },
|
|
|
|
#if 0 /* TODO: MORE NEED BE ADDED */
|
|
{DFS_STATUS_EISDIR, ERROR_FILE_EXISTS },
|
|
{DFS_STATUS_ENOTDIR, ERROR_FILE_EXISTS },
|
|
{DFS_STATUS_EBADF, ERROR_FILE_EXISTS },
|
|
{DFS_STATUS_EBUSY, ERROR_FILE_EXISTS },
|
|
{DFS_STATUS_ENOMEM, ERROR_FILE_EXISTS },
|
|
{DFS_STATUS_ENOSPC, ERROR_FILE_EXISTS },
|
|
#endif
|
|
};
|
|
static int win32_result_to_dfs(DWORD res)
|
|
{
|
|
int i;
|
|
int err = 0;
|
|
for (i = 0; i < sizeof(errcode_table) / sizeof(struct _errcode_map); i++)
|
|
{
|
|
if (errcode_table[i].win32err == (res & 0xFFFF))
|
|
{
|
|
err = -errcode_table[i].dfserr;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
/* unknown error */
|
|
rt_kprintf("dfs win32 error not supported yet: %d\n", res);
|
|
return -1;
|
|
}
|
|
|
|
static int dfs_win32_mount(
|
|
struct dfs_filesystem *fs,
|
|
unsigned long rwflag,
|
|
const void *data)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int dfs_win32_unmount(struct dfs_filesystem *fs)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int dfs_win32_mkfs(rt_device_t devid)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int dfs_win32_statfs(struct dfs_filesystem *fs,
|
|
struct statfs *buf)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static char *winpath_dirdup(char *des, const char *src)
|
|
{
|
|
char *path;
|
|
int i = 0;
|
|
|
|
path = rt_malloc(FILE_PATH_MAX);
|
|
if (path == RT_NULL)
|
|
return RT_NULL;
|
|
|
|
strcpy(path, des);
|
|
strcat(path, src);
|
|
|
|
while (1)
|
|
{
|
|
if (path[i] == 0)
|
|
break;
|
|
|
|
if (path[i] == '/')
|
|
path[i] = '\\';
|
|
|
|
i++;
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
/* This function can convert the path in rt-thread/dfs to the path in windows */
|
|
char * dfs_win32_dirdup(char * path)
|
|
{
|
|
char * file_path;
|
|
file_path = winpath_dirdup(WIN32_DIRDISK_ROOT, path);
|
|
return file_path;
|
|
}
|
|
|
|
static int dfs_win32_open(struct dfs_fd *file)
|
|
{
|
|
int fd;
|
|
uint32_t oflag, mode;
|
|
char *file_path;
|
|
int res;
|
|
|
|
oflag = file->flags;
|
|
if (oflag & O_DIRECTORY) /* operations about dir */
|
|
{
|
|
WINDIR *wdirp;
|
|
HANDLE handle;
|
|
int len;
|
|
|
|
file_path = winpath_dirdup(WIN32_DIRDISK_ROOT, file->path);
|
|
|
|
if (oflag & O_CREAT) /* create a dir*/
|
|
{
|
|
res = CreateDirectory(file_path, NULL);
|
|
if (res == 0)
|
|
{
|
|
rt_free(file_path);
|
|
return win32_result_to_dfs(GetLastError());
|
|
}
|
|
}
|
|
|
|
len = strlen(file_path);
|
|
if (file_path[len - 1] != '\\')
|
|
{
|
|
file_path[len] = '\\';
|
|
file_path[len + 1] = 0;
|
|
}
|
|
|
|
strcat(file_path, "*.*");
|
|
/* _findfirst will get '.' */
|
|
/* save this pointer,will used by dfs_win32_getdents*/
|
|
wdirp = rt_malloc(sizeof(WINDIR));
|
|
RT_ASSERT(wdirp != NULL);
|
|
if ((handle = _findfirst(file_path, &wdirp->finddata)) == -1)
|
|
{
|
|
rt_free(wdirp);
|
|
rt_free(file_path);
|
|
goto __err;
|
|
}
|
|
len = strlen(wdirp->finddata.name) + 1;
|
|
wdirp->handle = handle;
|
|
//wdirp->nfiles = 1;
|
|
wdirp->start = malloc(len); //not rt_malloc!
|
|
wdirp->end = wdirp->curr = wdirp->start;
|
|
wdirp->end += len;
|
|
strncpy(wdirp->curr, wdirp->finddata.name, len);
|
|
|
|
file->data = (void *)wdirp;
|
|
rt_free(file_path);
|
|
return 0;
|
|
}
|
|
/* regular file operations */
|
|
mode = O_BINARY;
|
|
if (oflag & O_RDONLY) mode |= O_RDONLY;
|
|
if (oflag & O_WRONLY) mode |= O_WRONLY;
|
|
if (oflag & O_RDWR) mode |= O_RDWR;
|
|
/* Opens the file, if it is existing. If not, a new file is created. */
|
|
if (oflag & O_CREAT) mode |= O_CREAT;
|
|
/* Creates a new file. If the file is existing, it is truncated and overwritten. */
|
|
if (oflag & O_TRUNC) mode |= O_TRUNC;
|
|
/* Creates a new file. The function fails if the file is already existing. */
|
|
if (oflag & O_EXCL) mode |= O_EXCL;
|
|
|
|
file_path = winpath_dirdup(WIN32_DIRDISK_ROOT, file->path);
|
|
fd = _open(file_path, mode, 0x0100 | 0x0080); /* _S_IREAD | _S_IWRITE */
|
|
rt_free(file_path);
|
|
|
|
if (fd < 0)
|
|
goto __err;
|
|
|
|
/* save this pointer, it will be used when calling read(), write(),
|
|
* flush(), seek(), and will be free when calling close()*/
|
|
file->data = (void *)fd;
|
|
file->pos = 0;
|
|
file->size = _lseek(fd, 0, SEEK_END);
|
|
|
|
if (oflag & O_APPEND)
|
|
{
|
|
file->pos = file->size;
|
|
}
|
|
else
|
|
_lseek(fd, 0, SEEK_SET);
|
|
|
|
return 0;
|
|
|
|
__err:
|
|
res = GetLastError();
|
|
return win32_result_to_dfs(res);
|
|
}
|
|
|
|
static int dfs_win32_close(struct dfs_fd *file)
|
|
{
|
|
if (file->flags & O_DIRECTORY)
|
|
{
|
|
WINDIR *wdirp = (WINDIR*)(file->data);
|
|
RT_ASSERT(wdirp != RT_NULL);
|
|
if (_findclose((intptr_t)wdirp->handle) == 0) {
|
|
free(wdirp->start); //NOTE: here we don't use rt_free!
|
|
rt_free(wdirp);
|
|
return 0;
|
|
}
|
|
}
|
|
else /* regular file operations */
|
|
{
|
|
if (_close((int)(file->data)) == 0)
|
|
return 0;
|
|
}
|
|
|
|
return win32_result_to_dfs(GetLastError());
|
|
}
|
|
|
|
static int dfs_win32_ioctl(struct dfs_fd *file, int cmd, void *args)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int dfs_win32_read(struct dfs_fd *file, void *buf, size_t len)
|
|
{
|
|
int fd;
|
|
int char_read;
|
|
|
|
fd = (int)(file->data);
|
|
char_read = _read(fd, buf, len);
|
|
if (char_read < 0)
|
|
return win32_result_to_dfs(GetLastError());
|
|
|
|
/* update position */
|
|
file->pos = _lseek(fd, 0, SEEK_CUR);
|
|
return char_read;
|
|
}
|
|
|
|
static int dfs_win32_write(struct dfs_fd *file, const void *buf, size_t len)
|
|
{
|
|
int fd;
|
|
int char_write;
|
|
|
|
fd = (int)(file->data);
|
|
|
|
char_write = _write(fd, buf, len);
|
|
if (char_write < 0)
|
|
return win32_result_to_dfs(GetLastError());
|
|
|
|
/* update position */
|
|
file->pos = _lseek(fd, 0, SEEK_CUR);
|
|
return char_write;
|
|
}
|
|
|
|
static int dfs_win32_flush(struct dfs_fd *file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int dfs_win32_seek(struct dfs_fd *file,
|
|
rt_off_t offset)
|
|
{
|
|
int result;
|
|
|
|
/* set offset as current offset */
|
|
if (file->type == FT_DIRECTORY)
|
|
{
|
|
WINDIR* wdirp = (WINDIR*)(file->data);
|
|
RT_ASSERT(wdirp != RT_NULL);
|
|
wdirp->curr = wdirp->start + offset;
|
|
return offset;
|
|
}
|
|
else //file->type == FT_REGULAR)
|
|
{
|
|
result = _lseek((int)(file->data), offset, SEEK_SET);
|
|
if (result >= 0)
|
|
return offset;
|
|
else
|
|
return win32_result_to_dfs(GetLastError());
|
|
}
|
|
}
|
|
|
|
/* return the size of struct dirent*/
|
|
static int dfs_win32_getdents(struct dfs_fd *file, struct dirent *dirp, rt_uint32_t count)
|
|
{
|
|
WINDIR *wdirp;
|
|
struct dirent *d = dirp;
|
|
int result;
|
|
|
|
/* make integer count */
|
|
if (count / sizeof(struct dirent) != 1)
|
|
return -EINVAL;
|
|
|
|
wdirp = (WINDIR*)(file->data);
|
|
RT_ASSERT(wdirp != RT_NULL);
|
|
if (wdirp->curr == NULL) //no more entries in this directory
|
|
return 0;
|
|
|
|
/* get the current entry */
|
|
if (wdirp->finddata.attrib & _A_SUBDIR)
|
|
d->d_type = DT_DIR;
|
|
else
|
|
d->d_type = DT_REG;
|
|
d->d_namlen = strlen(wdirp->curr);
|
|
strncpy(d->d_name, wdirp->curr, DFS_PATH_MAX);
|
|
d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
|
|
wdirp->curr += (strlen(wdirp->curr) + 1);
|
|
file->pos = wdirp->curr - wdirp->start + sizeof(struct dirent);//NOTE!
|
|
|
|
/* now set up for the next call to readdir */
|
|
if (wdirp->curr >= wdirp->end)
|
|
{
|
|
if (_findnext(wdirp->handle, &wdirp->finddata) == 0)
|
|
{
|
|
char* old_start = wdirp->start;
|
|
long name_len = strlen(wdirp->finddata.name) + 1;
|
|
wdirp->start = realloc(wdirp->start, wdirp->end - wdirp->start + name_len);
|
|
wdirp->curr = wdirp->start + (wdirp->curr - old_start);
|
|
wdirp->end = wdirp->curr + name_len;
|
|
strcpy(wdirp->curr, wdirp->finddata.name);
|
|
}
|
|
else
|
|
{
|
|
if ((result = GetLastError()) == ERROR_NO_MORE_FILES)
|
|
wdirp->curr = NULL;
|
|
else
|
|
return win32_result_to_dfs(result);
|
|
}
|
|
}
|
|
|
|
return sizeof(struct dirent);
|
|
}
|
|
|
|
static int dfs_win32_unlink(struct dfs_filesystem *fs, const char *path)
|
|
{
|
|
int result;
|
|
char *fp;
|
|
fp = winpath_dirdup(WIN32_DIRDISK_ROOT, path);
|
|
if (fp == RT_NULL)
|
|
{
|
|
rt_kprintf("out of memory.\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
result = GetFileAttributes(fp);
|
|
if (result == INVALID_FILE_ATTRIBUTES)
|
|
goto __err;
|
|
|
|
if (result & FILE_ATTRIBUTE_DIRECTORY)//winnt.h
|
|
{
|
|
if (RemoveDirectory(fp) == RT_FALSE)
|
|
goto __err;
|
|
}
|
|
else //(result & FILE_ATTRIBUTE_NORMAL)
|
|
{
|
|
if (_unlink(fp) < 0)
|
|
goto __err;
|
|
}
|
|
|
|
rt_free(fp);
|
|
return 0;
|
|
__err:
|
|
rt_free(fp);
|
|
return win32_result_to_dfs(GetLastError());
|
|
}
|
|
|
|
static int dfs_win32_rename(
|
|
struct dfs_filesystem *fs,
|
|
const char *oldpath,
|
|
const char *newpath)
|
|
{
|
|
int result;
|
|
char *op, *np;
|
|
op = winpath_dirdup(WIN32_DIRDISK_ROOT, oldpath);
|
|
np = winpath_dirdup(WIN32_DIRDISK_ROOT, newpath);
|
|
if (op == RT_NULL || np == RT_NULL)
|
|
{
|
|
rt_kprintf("out of memory.\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* If the function fails, the return value is zero. */
|
|
result = MoveFile(op, np);
|
|
|
|
rt_free(op);
|
|
rt_free(np);
|
|
|
|
if (result == 0)
|
|
return win32_result_to_dfs(GetLastError());
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dfs_win32_stat(struct dfs_filesystem *fs, const char *path, struct stat *st)
|
|
{
|
|
WIN32_FIND_DATA fileInfo;
|
|
HANDLE hFind;
|
|
char *fp;
|
|
fp = winpath_dirdup(WIN32_DIRDISK_ROOT, path);
|
|
if (fp == RT_NULL)
|
|
{
|
|
rt_kprintf("out of memory.\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
hFind = FindFirstFile(fp, &fileInfo);
|
|
rt_free(fp);
|
|
|
|
if (hFind == INVALID_HANDLE_VALUE)
|
|
goto __err;
|
|
|
|
st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH |
|
|
S_IWUSR | S_IWGRP | S_IWOTH;
|
|
|
|
/* convert file info to dfs stat structure */
|
|
if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
st->st_mode &= ~S_IFREG;
|
|
st->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
|
|
}
|
|
|
|
st->st_dev = 0;
|
|
st->st_size = fileInfo.nFileSizeLow;
|
|
|
|
/* get st_mtime. */
|
|
{
|
|
LARGE_INTEGER time_tmp;
|
|
time_tmp.LowPart = fileInfo.ftLastWriteTime.dwLowDateTime;
|
|
time_tmp.HighPart = fileInfo.ftLastWriteTime.dwHighDateTime;
|
|
|
|
/* removes the diff between 1970 and 1601. */
|
|
time_tmp.QuadPart -= 11644473600000 * 10000;
|
|
/* converts back from 100-nanoseconds to seconds. */
|
|
time_tmp.QuadPart /= 10UL * 1000 * 1000;
|
|
|
|
st->st_mtime = time_tmp.QuadPart;
|
|
}
|
|
|
|
FindClose(hFind);
|
|
|
|
return 0;
|
|
|
|
__err:
|
|
return win32_result_to_dfs(GetLastError());
|
|
}
|
|
|
|
static const struct dfs_file_ops dfs_win32_file_ops =
|
|
{
|
|
dfs_win32_open,
|
|
dfs_win32_close,
|
|
dfs_win32_ioctl,
|
|
dfs_win32_read,
|
|
dfs_win32_write,
|
|
dfs_win32_flush,
|
|
dfs_win32_seek,
|
|
dfs_win32_getdents,
|
|
};
|
|
|
|
static const struct dfs_filesystem_ops dfs_win32_ops =
|
|
{
|
|
"wdir", /* file system type: dir */
|
|
DFS_FS_FLAG_DEFAULT,
|
|
&dfs_win32_file_ops,
|
|
dfs_win32_mount,
|
|
dfs_win32_unmount,
|
|
dfs_win32_mkfs,
|
|
dfs_win32_statfs,
|
|
dfs_win32_unlink,
|
|
dfs_win32_stat,
|
|
dfs_win32_rename,
|
|
};
|
|
|
|
int dfs_win32_init(void)
|
|
{
|
|
/* register uffs file system */
|
|
dfs_register(&dfs_win32_ops);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static rt_err_t nop_init(rt_device_t dev)
|
|
{
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t nop_open(rt_device_t dev, rt_uint16_t oflag)
|
|
{
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t nop_close(rt_device_t dev)
|
|
{
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_size_t nop_read(rt_device_t dev,
|
|
rt_off_t pos,
|
|
void *buffer,
|
|
rt_size_t size)
|
|
{
|
|
return size;
|
|
}
|
|
|
|
static rt_size_t nop_write(rt_device_t dev,
|
|
rt_off_t pos,
|
|
const void *buffer,
|
|
rt_size_t size)
|
|
{
|
|
return size;
|
|
}
|
|
|
|
static rt_err_t nop_control(rt_device_t dev, int cmd, void *args)
|
|
{
|
|
return RT_EOK;
|
|
}
|
|
|
|
static struct rt_device win_sharedir_dev;
|
|
rt_err_t rt_win_sharedir_init(const char *name)
|
|
{
|
|
rt_device_t dev;
|
|
|
|
dev = &win_sharedir_dev;
|
|
RT_ASSERT(dev != RT_NULL);
|
|
|
|
/* set device class and generic device interface */
|
|
dev->type = RT_Device_Class_Block;
|
|
dev->init = nop_init;
|
|
dev->open = nop_open;
|
|
dev->read = nop_read;
|
|
dev->write = nop_write;
|
|
dev->close = nop_close;
|
|
dev->control = nop_control;
|
|
|
|
dev->rx_indicate = RT_NULL;
|
|
dev->tx_complete = RT_NULL;
|
|
|
|
/* register to RT-Thread device system */
|
|
return rt_device_register(dev, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
|
|
}
|