Commit 4bf43ab9 authored by longpanda's avatar longpanda
Browse files

VentoyPlugson ---- A GUI ventoy.json configurator

parent 9eeb94e8
/******************************************************************************
* ventoy_util.c ---- ventoy util
* Copyright (c) 2021, longpanda <admin@ventoy.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <ventoy_define.h>
#include <ventoy_util.h>
static int g_tar_filenum = 0;
static char *g_tar_buffer = NULL;
static ventoy_file *g_tar_filelist = NULL;
SYSINFO g_sysinfo;
unsigned char *g_unxz_buffer = NULL;
int g_unxz_len = 0;
void unxz_error(char *x)
{
vlog("%s\n", x);
}
int unxz_flush(void *src, unsigned int size)
{
memcpy(g_unxz_buffer + g_unxz_len, src, size);
g_unxz_len += (int)size;
return (int)size;
}
uint64_t ventoy_get_human_readable_gb(uint64_t SizeBytes)
{
int i;
int Pow2 = 1;
double Delta;
double GB = SizeBytes * 1.0 / 1000 / 1000 / 1000;
if ((SizeBytes % SIZE_1GB) == 0)
{
return (uint64_t)(SizeBytes / SIZE_1GB);
}
for (i = 0; i < 12; i++)
{
if (Pow2 > GB)
{
Delta = (Pow2 - GB) / Pow2;
}
else
{
Delta = (GB - Pow2) / Pow2;
}
if (Delta < 0.05)
{
return Pow2;
}
Pow2 <<= 1;
}
return (uint64_t)GB;
}
int ventoy_read_file_to_buf(const char *FileName, int ExtLen, void **Bufer, int *BufLen)
{
int FileSize;
FILE *fp = NULL;
void *Data = NULL;
#if defined(_MSC_VER) || defined(WIN32)
fopen_s(&fp, FileName, "rb");
#else
fp = fopen(FileName, "rb");
#endif
if (fp == NULL)
{
vlog("Failed to open file %s", FileName);
return 1;
}
fseek(fp, 0, SEEK_END);
FileSize = (int)ftell(fp);
Data = malloc(FileSize + ExtLen);
if (!Data)
{
fclose(fp);
return 1;
}
fseek(fp, 0, SEEK_SET);
fread(Data, 1, FileSize, fp);
fclose(fp);
*Bufer = Data;
*BufLen = FileSize;
return 0;
}
ventoy_file * ventoy_tar_find_file(const char *path)
{
int i;
int len;
ventoy_file *node = g_tar_filelist;
len = (int)strlen(path);
for (i = 0; i < g_tar_filenum; i++, node++)
{
if (node->pathlen == len && memcmp(node->path, path, len) == 0)
{
return node;
}
if (node->pathlen > len)
{
break;
}
}
return NULL;
}
int ventoy_www_init(void)
{
int i = 0;
int j = 0;
int size = 0;
int tarsize = 0;
int offset = 0;
ventoy_file *node = NULL;
ventoy_file *node2 = NULL;
VENTOY_TAR_HEAD *pHead = NULL;
ventoy_file tmpnode;
if (!g_tar_filelist)
{
g_tar_filelist = malloc(VENTOY_FILE_MAX * sizeof(ventoy_file));
g_tar_buffer = malloc(TAR_BUF_MAX);
g_tar_filenum = 0;
}
if ((!g_tar_filelist) || (!g_tar_buffer))
{
return 1;
}
if (ventoy_decompress_tar(g_tar_buffer, TAR_BUF_MAX, &tarsize))
{
return 1;
}
pHead = (VENTOY_TAR_HEAD *)g_tar_buffer;
node = g_tar_filelist;
while (g_tar_filenum < VENTOY_FILE_MAX && size < tarsize && memcmp(pHead->magic, TMAGIC, 5) == 0)
{
if (pHead->typeflag == REGTYPE)
{
node->size = (int)strtol(pHead->size, NULL, 8);
node->pathlen = (int)scnprintf(node->path, MAX_PATH, "%s", pHead->name);
node->addr = pHead + 1;
if (node->pathlen == 13 && strcmp(pHead->name, "www/buildtime") == 0)
{
scnprintf(g_sysinfo.buildtime, sizeof(g_sysinfo.buildtime), "%s", (char *)node->addr);
vlog("Plugson buildtime %s\n", g_sysinfo.buildtime);
}
offset = 512 + VENTOY_UP_ALIGN(node->size, 512);
node++;
g_tar_filenum++;
}
else
{
offset = 512;
}
pHead = (VENTOY_TAR_HEAD *)((char *)pHead + offset);
size += offset;
}
//sort
for (i = 0; i < g_tar_filenum; i++)
for (j = i + 1; j < g_tar_filenum; j++)
{
node = g_tar_filelist + i;
node2 = g_tar_filelist + j;
if (node->pathlen > node2->pathlen)
{
memcpy(&tmpnode, node, sizeof(ventoy_file));
memcpy(node, node2, sizeof(ventoy_file));
memcpy(node2, &tmpnode, sizeof(ventoy_file));
}
}
vlog("Total extract %d files from tar file.\n", g_tar_filenum);
return 0;
}
void ventoy_www_exit(void)
{
check_free(g_tar_filelist);
check_free(g_tar_buffer);
g_tar_filelist = NULL;
g_tar_buffer = NULL;
g_tar_filenum = 0;
}
void ventoy_get_json_path(char *path, char *backup)
{
#if defined(_MSC_VER) || defined(WIN32)
scnprintf(path, 64, "%C:\\ventoy\\ventoy.json", g_cur_dir[0]);
if (backup)
{
scnprintf(backup, 64, "%C:\\ventoy\\ventoy_backup.json", g_cur_dir[0]);
}
#else
scnprintf(path, 64, "%s/ventoy/ventoy.json", g_cur_dir);
if (backup)
{
scnprintf(backup, 64, "%s/ventoy/ventoy_backup.json", g_cur_dir);
}
#endif
}
/******************************************************************************
* ventoy_util.h
*
* Copyright (c) 2021, longpanda <admin@ventoy.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef __VENTOY_UTIL_H__
#define __VENTOY_UTIL_H__
#define check_free(p) if (p) free(p)
#define vtoy_safe_close_fd(fd) \
{\
if ((fd) >= 0) \
{ \
close(fd); \
(fd) = -1; \
}\
}
extern char g_cur_dir[MAX_PATH];
extern char g_ventoy_dir[MAX_PATH];
#if defined(_MSC_VER) || defined(WIN32)
typedef HANDLE pthread_mutex_t;
static __inline int pthread_mutex_init(pthread_mutex_t *mutex, void *unused)
{
(void)unused;
*mutex = CreateMutex(NULL, FALSE, NULL);
return *mutex == NULL ? -1 : 0;
}
static __inline int pthread_mutex_destroy(pthread_mutex_t *mutex)
{
return CloseHandle(*mutex) == 0 ? -1 : 0;
}
static __inline int pthread_mutex_unlock(pthread_mutex_t *mutex)
{
return ReleaseMutex(*mutex) == 0 ? -1 : 0;
}
static __inline int pthread_mutex_lock(pthread_mutex_t *mutex)
{
return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0 ? 0 : -1;
}
int ventoy_path_case(char *path, int slash);
#else
int ventoy_get_sys_file_line(char *buffer, int buflen, const char *fmt, ...);
#define UINT8 uint8_t
#define UINT16 uint16_t
#define UINT32 uint32_t
#define UINT64 uint64_t
static inline int ventoy_path_case(char *path, int slash)
{
(void)path;
(void)slash;
return 0;
}
#endif
#define LANGUAGE_EN 0
#define LANGUAGE_CN 1
typedef struct SYSINFO
{
char buildtime[128];
int syntax_error;
int language;
int pathcase;
char cur_fsname[64];
char cur_capacity[64];
char cur_model[256];
char cur_ventoy_ver[64];
int cur_secureboot;
int cur_part_style;
char ip[32];
char port[16];
}SYSINFO;
extern SYSINFO g_sysinfo;
#define TMAGIC "ustar"
#define REGTYPE '0'
#define AREGTYPE '\0'
#define LNKTYPE '1'
#define CHRTYPE '3'
#define BLKTYPE '4'
#define DIRTYPE '5'
#define FIFOTYPE '6'
#define CONTTYPE '7'
#pragma pack(1)
typedef struct tag_tar_head
{
char name[100];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char chksum[8];
char typeflag;
char linkname[100];
char magic[6];
char version[2];
char uname[32];
char gname[32];
char devmajor[8];
char devminor[8];
char prefix[155];
char padding[12];
}VENTOY_TAR_HEAD;
typedef struct VENTOY_MAGIC
{
uint32_t magic1; // 0x51 0x52 0x53 0x54
uint32_t xzlen; //
uint32_t magic2; // 0xa1 0xa2 0xa3 0xa4
}VENTOY_MAGIC;
#pragma pack()
#define VENTOY_UP_ALIGN(N, align) (((N) + ((align) - 1)) / (align) * (align))
#define VENTOY_FILE_MAX 2048
#if defined(_MSC_VER) || defined(WIN32)
#define million_sleep(a) Sleep(a)
#else
#define million_sleep(a) usleep((a) * 1000)
#endif
typedef struct ventoy_file
{
int size;
char path[MAX_PATH];
int pathlen;
void *addr;
}ventoy_file;
int ventoy_is_file_exist(const char *fmt, ...);
int ventoy_is_directory_exist(const char *fmt, ...);
void ventoy_gen_preudo_uuid(void *uuid);
uint64_t ventoy_get_human_readable_gb(uint64_t SizeBytes);
void ventoy_md5(const void *data, uint32_t len, uint8_t *md5);
int ventoy_is_disk_mounted(const char *devpath);
int unxz(unsigned char *in, int in_size,
int (*fill)(void *dest, unsigned int size),
int (*flush)(void *src, unsigned int size),
unsigned char *out, int *in_used,
void (*error)(char *x));
int ventoy_read_file_to_buf(const char *FileName, int ExtLen, void **Bufer, int *BufLen);
int ventoy_write_buf_to_file(const char *FileName, void *Bufer, int BufLen);
const char * ventoy_get_os_language(void);
int ventoy_get_file_size(const char *file);
int ventoy_www_init(void);
void ventoy_www_exit(void);
int ventoy_decompress_tar(char *tarbuf, int buflen, int *tarsize);
ventoy_file * ventoy_tar_find_file(const char *path);
void ventoy_get_json_path(char *path, char *backup);
int ventoy_copy_file(const char *a, const char *b);
typedef int (*ventoy_http_writeback_pf)(void);
int ventoy_start_writeback_thread(ventoy_http_writeback_pf callback);
void ventoy_stop_writeback_thread(void);
void ventoy_set_writeback_event(void);
extern unsigned char *g_unxz_buffer;
extern int g_unxz_len;
void unxz_error(char *x);
int unxz_flush(void *src, unsigned int size);
#endif /* __VENTOY_UTIL_H__ */
/******************************************************************************
* ventoy_util_linux.c ---- ventoy util
* Copyright (c) 2021, longpanda <admin@ventoy.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <linux/fs.h>
#include <dirent.h>
#include <time.h>
#include <ventoy_define.h>
#include <ventoy_util.h>
void ventoy_gen_preudo_uuid(void *uuid)
{
int i;
int fd;
fd = open("/dev/urandom", O_RDONLY | O_BINARY);
if (fd < 0)
{
srand(time(NULL));
for (i = 0; i < 8; i++)
{
*((uint16_t *)uuid + i) = (uint16_t)(rand() & 0xFFFF);
}
}
else
{
read(fd, uuid, 16);
close(fd);
}
}
int ventoy_get_sys_file_line(char *buffer, int buflen, const char *fmt, ...)
{
int len;
char c;
char path[256];
va_list arg;
va_start(arg, fmt);
vsnprintf(path, 256, fmt, arg);
va_end(arg);
if (access(path, F_OK) >= 0)
{
FILE *fp = fopen(path, "r");
memset(buffer, 0, buflen);
len = (int)fread(buffer, 1, buflen - 1, fp);
fclose(fp);
while (len > 0)
{
c = buffer[len - 1];
if (c == '\r' || c == '\n' || c == ' ' || c == '\t')
{
buffer[len - 1] = 0;
len--;
}
else
{
break;
}
}
return 0;
}
else
{
vdebug("%s not exist \n", path);
return 1;
}
}
int ventoy_is_disk_mounted(const char *devpath)
{
int len;
int mount = 0;
char line[512];
FILE *fp = NULL;
fp = fopen("/proc/mounts", "r");
if (!fp)
{
return 0;
}
len = (int)strlen(devpath);
while (fgets(line, sizeof(line), fp))
{
if (strncmp(line, devpath, len) == 0)
{
mount = 1;
vdebug("%s mounted <%s>\n", devpath, line);
goto end;
}
}
end:
fclose(fp);
return mount;
}
const char * ventoy_get_os_language(void)
{
const char *env = getenv("LANG");
if (env && strncasecmp(env, "zh_CN", 5) == 0)
{
return "cn";
}
else
{
return "en";
}
}
int ventoy_is_file_exist(const char *fmt, ...)
{
va_list ap;
struct stat sb;
char fullpath[MAX_PATH];
va_start (ap, fmt);
vsnprintf(fullpath, MAX_PATH, fmt, ap);
va_end (ap);
if (stat(fullpath, &sb))
{
return 0;
}
if (S_ISREG(sb.st_mode))
{
return 1;
}
return 0;
}
int ventoy_is_directory_exist(const char *fmt, ...)
{
va_list ap;
struct stat sb;
char fullpath[MAX_PATH];
va_start (ap, fmt);
vsnprintf(fullpath, MAX_PATH, fmt, ap);
va_end (ap);
if (stat(fullpath, &sb))
{
return 0;
}
if (S_ISDIR(sb.st_mode))
{
return 1;
}
return 0;
}
int ventoy_get_file_size(const char *file)
{
int Size = -1;
struct stat stStat;
if (stat(file, &stStat) >= 0)
{
Size = (int)(stStat.st_size);
}
return Size;
}
int ventoy_write_buf_to_file(const char *FileName, void *Bufer, int BufLen)
{
int fd;
int rc;
ssize_t size;
fd = open(FileName, O_CREAT | O_RDWR | O_TRUNC, 0755);
if (fd < 0)
{
vlog("Failed to open file %s %d\n", FileName, errno);
return 1;
}
rc = fchmod(fd, 0755);
if (rc)
{
vlog("Failed to chmod <%s> %d\n", FileName, errno);
}
size = write(fd, Bufer, BufLen);
if ((int)size != BufLen)
{
close(fd);
vlog("write file %s failed %d err:%d\n", FileName, (int)size, errno);
return 1;
}
fsync(fd);
close(fd);
return 0;
}
int ventoy_decompress_tar(char *tarbuf, int buflen, int *tarsize)
{
int rc = 1;
int inused = 0;
int BufLen = 0;
unsigned char *buffer = NULL;
char tarxz[MAX_PATH];
scnprintf(tarxz, sizeof(tarxz), "%s/tool/plugson.tar.xz", g_ventoy_dir);
if (ventoy_read_file_to_buf(tarxz, 0, (void **)&buffer, &BufLen))
{
vlog("Failed to read file <%s>\n", tarxz);
return 1;
}
g_unxz_buffer = (unsigned char *)tarbuf;
g_unxz_len = 0;
unxz(buffer, BufLen, NULL, unxz_flush, NULL, &inused, unxz_error);
vlog("xzlen:%u rawdata size:%d\n", BufLen, g_unxz_len);
if (inused != BufLen)
{
vlog("Failed to unxz data %d %d\n", inused, BufLen);
rc = 1;
}
else
{
*tarsize = g_unxz_len;
rc = 0;
}
free(buffer);
return rc;
}
static volatile int g_thread_stop = 0;
static pthread_t g_writeback_thread;
static pthread_mutex_t g_writeback_mutex;
static pthread_cond_t g_writeback_cond;
static void * ventoy_local_thread_run(void* data)
{
ventoy_http_writeback_pf callback = (ventoy_http_writeback_pf)data;
while (1)
{
pthread_mutex_lock(&g_writeback_mutex);
pthread_cond_wait(&g_writeback_cond, &g_writeback_mutex);
if (g_thread_stop)
{
pthread_mutex_unlock(&g_writeback_mutex);
break;
}
else
{
callback();
pthread_mutex_unlock(&g_writeback_mutex);
}
}
return NULL;
}
void ventoy_set_writeback_event(void)
{
pthread_cond_signal(&g_writeback_cond);
}
int ventoy_start_writeback_thread(ventoy_http_writeback_pf callback)
{
g_thread_stop = 0;
pthread_mutex_init(&g_writeback_mutex, NULL);
pthread_cond_init(&g_writeback_cond, NULL);
pthread_create(&g_writeback_thread, NULL, ventoy_local_thread_run, callback);
return 0;
}
void ventoy_stop_writeback_thread(void)
{
g_thread_stop = 1;
pthread_cond_signal(&g_writeback_cond);
pthread_join(g_writeback_thread, NULL);
pthread_cond_destroy(&g_writeback_cond);
pthread_mutex_destroy(&g_writeback_mutex);
}
int ventoy_copy_file(const char *a, const char *b)
{
int len = 0;
char *buf = NULL;
if (0 == ventoy_read_file_to_buf(a, 0, (void **)&buf, &len))
{
ventoy_write_buf_to_file(b, buf, len);
free(buf);
}
return 0;
}
/******************************************************************************
* ventoy_util.c ---- ventoy util
* Copyright (c) 2021, longpanda <admin@ventoy.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <ventoy_define.h>
#include <ventoy_util.h>
#include <ventoy_disk.h>
#include "fat_filelib.h"
static void TrimString(CHAR *String)
{
CHAR *Pos1 = String;
CHAR *Pos2 = String;
size_t Len = strlen(String);
while (Len > 0)
{
if (String[Len - 1] != ' ' && String[Len - 1] != '\t')
{
break;
}
String[Len - 1] = 0;
Len--;
}
while (*Pos1 == ' ' || *Pos1 == '\t')
{
Pos1++;
}
while (*Pos1)
{
*Pos2++ = *Pos1++;
}
*Pos2++ = 0;
return;
}
void ventoy_gen_preudo_uuid(void *uuid)
{
CoCreateGuid((GUID *)uuid);
}
static int IsUTF8Encode(const char *src)
{
int i;
const UCHAR *Byte = (const UCHAR *)src;
for (i = 0; i < MAX_PATH && Byte[i]; i++)
{
if (Byte[i] > 127)
{
return 1;
}
}
return 0;
}
static int Utf8ToUtf16(const char* src, WCHAR * dst)
{
return MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, MAX_PATH * sizeof(WCHAR));
}
static int Utf16ToUtf8(const WCHAR* src, CHAR * dst)
{
int size = WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, MAX_PATH, NULL, 0);
dst[size] = 0;
return size;
}
int ventoy_path_case(char *path, int slash)
{
int i;
int j = 0;
int count = 0;
int isUTF8 = 0;
BOOL bRet;
HANDLE handle = INVALID_HANDLE_VALUE;
WCHAR Buffer[MAX_PATH + 16];
WCHAR FilePathW[MAX_PATH];
CHAR FilePathA[MAX_PATH];
FILE_NAME_INFO *pInfo = NULL;
if (g_sysinfo.pathcase == 0)
{
return 0;
}
if (path == NULL || path[0] == '/' || path[0] == '\\')
{
return 0;
}
isUTF8 = IsUTF8Encode(path);
if (isUTF8)
{
Utf8ToUtf16(path, FilePathW);
handle = CreateFileW(FilePathW, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
}
else
{
handle = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
}
if (handle != INVALID_HANDLE_VALUE)
{
bRet = GetFileInformationByHandleEx(handle, FileNameInfo, Buffer, sizeof(Buffer));
if (bRet)
{
pInfo = (FILE_NAME_INFO *)Buffer;
if (slash)
{
for (i = 0; i < (int)(pInfo->FileNameLength / sizeof(WCHAR)); i++)
{
if (pInfo->FileName[i] == L'\\')
{
pInfo->FileName[i] = L'/';
}
}
}
pInfo->FileName[(pInfo->FileNameLength / sizeof(WCHAR))] = 0;
memset(FilePathA, 0, sizeof(FilePathA));
Utf16ToUtf8(pInfo->FileName, FilePathA);
if (FilePathA[1] == ':')
{
j = 3;
}
else
{
j = 1;
}
for (i = 0; i < MAX_PATH && j < MAX_PATH; i++, j++)
{
if (FilePathA[j] == 0)
{
break;
}
if (path[i] != FilePathA[j])
{
path[i] = FilePathA[j];
count++;
}
}
}
CHECK_CLOSE_HANDLE(handle);
}
return count;
}
int ventoy_is_directory_exist(const char *Fmt, ...)
{
va_list Arg;
DWORD Attr;
int UTF8 = 0;
CHAR FilePathA[MAX_PATH];
WCHAR FilePathW[MAX_PATH];
va_start(Arg, Fmt);
vsnprintf_s(FilePathA, sizeof(FilePathA), sizeof(FilePathA), Fmt, Arg);
va_end(Arg);
UTF8 = IsUTF8Encode(FilePathA);
if (UTF8)
{
Utf8ToUtf16(FilePathA, FilePathW);
Attr = GetFileAttributesW(FilePathW);
}
else
{
Attr = GetFileAttributesA(FilePathA);
}
if (Attr != INVALID_FILE_ATTRIBUTES && (Attr & FILE_ATTRIBUTE_DIRECTORY))
{
return TRUE;
}
return FALSE;
}
int ventoy_is_file_exist(const char *Fmt, ...)
{
va_list Arg;
HANDLE hFile;
DWORD Attr;
int UTF8 = 0;
CHAR FilePathA[MAX_PATH];
WCHAR FilePathW[MAX_PATH];
va_start(Arg, Fmt);
vsnprintf_s(FilePathA, sizeof(FilePathA), sizeof(FilePathA), Fmt, Arg);
va_end(Arg);
UTF8 = IsUTF8Encode(FilePathA);
if (UTF8)
{
Utf8ToUtf16(FilePathA, FilePathW);
hFile = CreateFileW(FilePathW, FILE_READ_EA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
Attr = GetFileAttributesW(FilePathW);
}
else
{
hFile = CreateFileA(FilePathA, FILE_READ_EA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
Attr = GetFileAttributesA(FilePathA);
}
if (INVALID_HANDLE_VALUE == hFile)
{
return 0;
}
CloseHandle(hFile);
if (Attr & FILE_ATTRIBUTE_DIRECTORY)
{
return 0;
}
return 1;
}
const char * ventoy_get_os_language(void)
{
if (GetUserDefaultUILanguage() == 0x0804)
{
return "cn";
}
else
{
return "en";
}
}
int GetPhyDriveByLogicalDrive(int DriveLetter, UINT64 *Offset)
{
BOOL Ret;
DWORD dwSize;
HANDLE Handle;
VOLUME_DISK_EXTENTS DiskExtents;
CHAR PhyPath[128];
scnprintf(PhyPath, sizeof(PhyPath), "\\\\.\\%C:", (CHAR)DriveLetter);
Handle = CreateFileA(PhyPath, 0, 0, 0, OPEN_EXISTING, 0, 0);
if (Handle == INVALID_HANDLE_VALUE)
{
vlog("CreateFileA %s failed %u\n", PhyPath, LASTERR);
return -1;
}
Ret = DeviceIoControl(Handle,
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
NULL,
0,
&DiskExtents,
(DWORD)(sizeof(DiskExtents)),
(LPDWORD)&dwSize,
NULL);
if (!Ret || DiskExtents.NumberOfDiskExtents == 0)
{
vlog("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTSfailed %u\n", LASTERR);
CHECK_CLOSE_HANDLE(Handle);
return -1;
}
CHECK_CLOSE_HANDLE(Handle);
if (Offset)
{
*Offset = (UINT64)(DiskExtents.Extents[0].StartingOffset.QuadPart);
}
return (int)DiskExtents.Extents[0].DiskNumber;
}
int GetPhyDriveInfo(int PhyDrive, UINT64 *Size, CHAR *Vendor, CHAR *Product)
{
int ret = 1;
BOOL bRet;
DWORD dwBytes;
CHAR Drive[64];
HANDLE Handle = INVALID_HANDLE_VALUE;
GET_LENGTH_INFORMATION LengthInfo;
STORAGE_PROPERTY_QUERY Query;
STORAGE_DESCRIPTOR_HEADER DevDescHeader;
STORAGE_DEVICE_DESCRIPTOR *pDevDesc = NULL;
sprintf_s(Drive, sizeof(Drive), "\\\\.\\PhysicalDrive%d", PhyDrive);
Handle = CreateFileA(Drive, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (Handle == INVALID_HANDLE_VALUE)
{
vlog("CreateFileA %s failed %u\n", Drive, LASTERR);
goto out;
}
bRet = DeviceIoControl(Handle,
IOCTL_DISK_GET_LENGTH_INFO, NULL,
0,
&LengthInfo,
sizeof(LengthInfo),
&dwBytes,
NULL);
if (!bRet)
{
vlog("IOCTL_DISK_GET_LENGTH_INFO failed %u\n", LASTERR);
return 1;
}
if (Size)
{
*Size = (UINT64)LengthInfo.Length.QuadPart;
}
Query.PropertyId = StorageDeviceProperty;
Query.QueryType = PropertyStandardQuery;
bRet = DeviceIoControl(Handle,
IOCTL_STORAGE_QUERY_PROPERTY,
&Query,
sizeof(Query),
&DevDescHeader,
sizeof(STORAGE_DESCRIPTOR_HEADER),
&dwBytes,
NULL);
if (!bRet)
{
vlog("IOCTL_STORAGE_QUERY_PROPERTY failed %u\n", LASTERR);
goto out;
}
if (DevDescHeader.Size < sizeof(STORAGE_DEVICE_DESCRIPTOR))
{
vlog("DevDescHeader.size invalid %u\n", DevDescHeader.Size);
goto out;
}
pDevDesc = (STORAGE_DEVICE_DESCRIPTOR *)malloc(DevDescHeader.Size);
if (!pDevDesc)
{
vlog("malloc failed\n");
goto out;
}
bRet = DeviceIoControl(Handle,
IOCTL_STORAGE_QUERY_PROPERTY,
&Query,
sizeof(Query),
pDevDesc,
DevDescHeader.Size,
&dwBytes,
NULL);
if (!bRet)
{
vlog("IOCTL_STORAGE_QUERY_PROPERTY failed %u\n", LASTERR);
goto out;
}
if (pDevDesc->VendorIdOffset && Vendor)
{
strcpy_s(Vendor, 128, (char *)pDevDesc + pDevDesc->VendorIdOffset);
TrimString(Vendor);
}
if (pDevDesc->ProductIdOffset && Product)
{
strcpy_s(Product, 128, (char *)pDevDesc + pDevDesc->ProductIdOffset);
TrimString(Product);
}
ret = 0;
out:
CHECK_FREE(pDevDesc);
CHECK_CLOSE_HANDLE(Handle);
return ret;
}
int ventoy_get_file_size(const char *file)
{
int Size = -1;
HANDLE hFile;
hFile = CreateFileA(file, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
Size = (int)GetFileSize(hFile, NULL);
CHECK_CLOSE_HANDLE(hFile);
}
return Size;
}
static HANDLE g_FatPhyDrive;
static UINT64 g_Part2StartSec;
const CHAR* ParseVentoyVersionFromString(CHAR *Buf)
{
CHAR *Pos = NULL;
CHAR *End = NULL;
static CHAR LocalVersion[64] = { 0 };
Pos = strstr(Buf, "VENTOY_VERSION=");
if (Pos)
{
Pos += strlen("VENTOY_VERSION=");
if (*Pos == '"')
{
Pos++;
}
End = Pos;
while (*End != 0 && *End != '"' && *End != '\r' && *End != '\n')
{
End++;
}
*End = 0;
sprintf_s(LocalVersion, sizeof(LocalVersion), "%s", Pos);
return LocalVersion;
}
return "";
}
static int GetVentoyVersionFromFatFile(CHAR *VerBuf, size_t BufLen)
{
int rc = 1;
int size = 0;
char *buf = NULL;
void *flfile = NULL;
flfile = fl_fopen("/grub/grub.cfg", "rb");
if (flfile)
{
fl_fseek(flfile, 0, SEEK_END);
size = (int)fl_ftell(flfile);
fl_fseek(flfile, 0, SEEK_SET);
buf = (char *)malloc(size + 1);
if (buf)
{
fl_fread(buf, 1, size, flfile);
buf[size] = 0;
rc = 0;
sprintf_s(VerBuf, BufLen, "%s", ParseVentoyVersionFromString(buf));
free(buf);
}
fl_fclose(flfile);
}
return rc;
}
static int VentoyFatDiskRead(uint32 Sector, uint8 *Buffer, uint32 SectorCount)
{
DWORD dwSize;
BOOL bRet;
DWORD ReadSize;
LARGE_INTEGER liCurrentPosition;
liCurrentPosition.QuadPart = Sector + g_Part2StartSec;
liCurrentPosition.QuadPart *= 512;
SetFilePointerEx(g_FatPhyDrive, liCurrentPosition, &liCurrentPosition, FILE_BEGIN);
ReadSize = (DWORD)(SectorCount * 512);
bRet = ReadFile(g_FatPhyDrive, Buffer, ReadSize, &dwSize, NULL);
if (bRet == FALSE || dwSize != ReadSize)
{
}
return 1;
}
static int GetVentoyVersion(int PhyDrive, ventoy_disk *disk)
{
int ret = 1;
BOOL bRet;
DWORD dwBytes;
UINT64 Part2Offset;
HANDLE Handle = INVALID_HANDLE_VALUE;
VTOY_GPT_INFO *pGPT = NULL;
CHAR Drive[64];
void *flfile = NULL;
UCHAR MbrData[] =
{
0xEB, 0x63, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x56, 0x54, 0x00, 0x47, 0x65, 0x00, 0x48, 0x44, 0x00, 0x52, 0x64, 0x00, 0x20, 0x45, 0x72, 0x0D,
};
sprintf_s(Drive, sizeof(Drive), "\\\\.\\PhysicalDrive%d", PhyDrive);
Handle = CreateFileA(Drive, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (Handle == INVALID_HANDLE_VALUE)
{
vlog("CreateFileA %s failed %u\n", Drive, LASTERR);
goto out;
}
pGPT = zalloc(sizeof(VTOY_GPT_INFO));
if (!pGPT)
{
goto out;
}
bRet = ReadFile(Handle, pGPT, sizeof(VTOY_GPT_INFO), &dwBytes, NULL);
if (!bRet || dwBytes != sizeof(VTOY_GPT_INFO))
{
vlog("ReadFile failed %u\n", LASTERR);
goto out;
}
if (memcmp(pGPT->MBR.BootCode, MbrData, 0x30) || memcmp(pGPT->MBR.BootCode + 0x190, MbrData + 0x30, 0x10))
{
vlog("Invalid MBR Code %u\n", LASTERR);
goto out;
}
if (pGPT->MBR.PartTbl[0].FsFlag == 0xEE)
{
if (memcmp(pGPT->Head.Signature, "EFI PART", 8))
{
vlog("Invalid GPT Signature\n");
goto out;
}
Part2Offset = pGPT->PartTbl[1].StartLBA;
disk->cur_part_style = 1;
}
else
{
Part2Offset = pGPT->MBR.PartTbl[1].StartSectorId;
disk->cur_part_style = 0;
}
g_FatPhyDrive = Handle;
g_Part2StartSec = Part2Offset;
fl_init();
if (0 == fl_attach_media(VentoyFatDiskRead, NULL))
{
ret = GetVentoyVersionFromFatFile(disk->cur_ventoy_ver, sizeof(disk->cur_ventoy_ver));
if (ret == 0)
{
flfile = fl_fopen("/EFI/BOOT/grubx64_real.efi", "rb");
if (flfile)
{
disk->cur_secureboot = 1;
fl_fclose(flfile);
}
}
}
fl_shutdown();
out:
CHECK_FREE(pGPT);
CHECK_CLOSE_HANDLE(Handle);
return ret;
}
int CheckRuntimeEnvironment(char Letter, ventoy_disk *disk)
{
int PhyDrive;
UINT64 Offset = 0;
char Drive[32];
DWORD FsFlag;
CHAR Vendor[128] = { 0 };
CHAR Product[128] = { 0 };
CHAR FsName[MAX_PATH];
PhyDrive = GetPhyDriveByLogicalDrive(Letter, &Offset);
if (PhyDrive < 0)
{
vlog("GetPhyDriveByLogicalDrive failed %d %llu\n", PhyDrive, (ULONGLONG)Offset);
return 1;
}
if (Offset != 1048576)
{
vlog("Partition offset is NOT 1MB. This is NOT ventoy image partition (%llu)\n", (ULONGLONG)Offset);
return 1;
}
if (GetPhyDriveInfo(PhyDrive, &Offset, Vendor, Product) != 0)
{
vlog("GetPhyDriveInfo failed\n");
return 1;
}
sprintf_s(disk->cur_capacity, sizeof(disk->cur_capacity), "%dGB", (int)ventoy_get_human_readable_gb(Offset));
sprintf_s(disk->cur_model, sizeof(disk->cur_model), "%s %s", Vendor, Product);
scnprintf(Drive, sizeof(Drive), "%C:\\", Letter);
if (0 == GetVolumeInformationA(Drive, NULL, 0, NULL, NULL, &FsFlag, FsName, MAX_PATH))
{
vlog("GetVolumeInformationA failed %u\n", LASTERR);
return 1;
}
if (_stricmp(FsName, "NTFS") == 0)
{
disk->pathcase = 1;
}
strlcpy(disk->cur_fsname, FsName);
if (GetVentoyVersion(PhyDrive, disk) != 0)
{
vlog("GetVentoyVersion failed %u\n", LASTERR);
return 1;
}
return 0;
}
int ventoy_write_buf_to_file(const char *FileName, void *Bufer, int BufLen)
{
BOOL bRet;
DWORD dwBytes;
HANDLE hFile;
hFile = CreateFileA(FileName, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
vlog("CreateFile %s failed %u\n", FileName, LASTERR);
return 1;
}
bRet = WriteFile(hFile, Bufer, (DWORD)BufLen, &dwBytes, NULL);
if ((!bRet) || ((DWORD)BufLen != dwBytes))
{
vlog("Failed to write file <%s> %u err:%u", FileName, dwBytes, LASTERR);
CloseHandle(hFile);
return 1;
}
FlushFileBuffers(hFile);
CloseHandle(hFile);
return 0;
}
int ventoy_decompress_tar(char *tarbuf, int buflen, int *tarsize)
{
int rc = 1;
int inused;
HANDLE hFile;
DWORD dwSize;
DWORD dwRead;
WCHAR FullPath[MAX_PATH];
BYTE *buffer;
VENTOY_MAGIC Magic;
GetModuleFileNameW(NULL, FullPath, MAX_PATH);
hFile = CreateFileW(FullPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
vlog("Failed to open self %u\n", LASTERR);
return 1;
}
dwSize = GetFileSize(hFile, NULL);
if (dwSize == INVALID_FILE_SIZE)
{
vlog("Invalid self exe size %u\n", LASTERR);
CHECK_CLOSE_HANDLE(hFile);
return 1;
}
buffer = malloc(dwSize);
if (!buffer)
{
vlog("Failed to malloc %u\n", dwSize);
CHECK_CLOSE_HANDLE(hFile);
return 1;
}
ReadFile(hFile, buffer, dwSize, &dwRead, NULL);
memcpy(&Magic, buffer + dwSize - sizeof(Magic), sizeof(Magic));
if (Magic.magic1 == 0x54535251 && Magic.magic2 == 0xa4a3a2a1)
{
g_unxz_buffer = (UCHAR *)tarbuf;
g_unxz_len = 0;
unxz(buffer + dwSize - Magic.xzlen - sizeof(Magic), Magic.xzlen, NULL, unxz_flush, NULL, &inused, unxz_error);
vlog("bigexe:%u xzlen:%u rawdata size:%d\n", dwSize, Magic.xzlen, g_unxz_len);
if (inused != Magic.xzlen)
{
vlog("Failed to unxz www %d\n", inused);
rc = 1;
}
else
{
*tarsize = g_unxz_len;
rc = 0;
}
}
else
{
vlog("Invalid magic 0x%x 0x%x\n", Magic.magic1, Magic.magic2);
rc = 1;
}
free(buffer);
CHECK_CLOSE_HANDLE(hFile);
return rc;
}
static volatile int g_thread_stop = 0;
static HANDLE g_writeback_thread;
static HANDLE g_writeback_event;
DWORD WINAPI ventoy_local_thread_run(LPVOID lpParameter)
{
ventoy_http_writeback_pf callback = (ventoy_http_writeback_pf)lpParameter;
while (1)
{
WaitForSingleObject(g_writeback_event, INFINITE);
if (g_thread_stop)
{
break;
}
else
{
callback();
}
}
return 0;
}
void ventoy_set_writeback_event(void)
{
SetEvent(g_writeback_event);
}
int ventoy_start_writeback_thread(ventoy_http_writeback_pf callback)
{
g_thread_stop = 0;
g_writeback_event = CreateEventA(NULL, FALSE, FALSE, "VTOYWRBK");
g_writeback_thread = CreateThread(NULL, 0, ventoy_local_thread_run, callback, 0, NULL);
return 0;
}
void ventoy_stop_writeback_thread(void)
{
g_thread_stop = 1;
ventoy_set_writeback_event();
WaitForSingleObject(g_writeback_thread, INFINITE);
CHECK_CLOSE_HANDLE(g_writeback_thread);
CHECK_CLOSE_HANDLE(g_writeback_event);
}
int ventoy_copy_file(const char *a, const char *b)
{
CopyFileA(a, b, FALSE);
return 0;
}
File IO Lib API
-=-=-=-=-=-=-=-=-
void fl_init(void)
Called to initialize FAT IO library.
This should be called prior to any other functions.
void fl_attach_locks(void (*lock)(void), void (*unlock)(void))
[Optional] File system thread safety locking functions.
For thread safe operation, you should provide lock() and unlock() functions.
Note that locking primitive used must support recursive locking, i.e lock() called within an already ‘locked’ region.
int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)
This function is used to attach system specific disk/media access functions.
This should be done subsequent to calling fl_init() and fl_attach_locks() (if locking required).
void fl_shutdown(void)
Shutdown the FAT IO library. This purges any un-saved data back to disk.
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
File IO Lib Options
-=-=-=-=-=-=-=-=-=-
See defines in fat_opts.h:
FATFS_IS_LITTLE_ENDIAN [1/0]
Which endian is your system? Set to 1 for little endian, 0 for big endian.
FATFS_MAX_LONG_FILENAME [260]
By default, 260 characters (max LFN length). Increase this to support greater path depths.
FATFS_MAX_OPEN_FILES
The more files you wish to have concurrently open, the greater this number should be.
This increases the number of FL_FILE file structures in the library, each of these is around 1K in size (assuming 512 byte sectors).
FAT_BUFFER_SECTORS
Minimum is 1, more increases performance.
This defines how many FAT sectors can be buffered per FAT_BUFFER entry.
FAT_BUFFERS
Minimum is 1, more increases performance.
This defines how many FAT buffer entries are available.
Memory usage is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE
FATFS_INC_WRITE_SUPPORT
Support file write functionality.
FAT_SECTOR_SIZE
Sector size used by buffers. Most likely to be 512 bytes (standard for ATA/IDE).
FAT_PRINTF
A define that allows the File IO library to print to console/stdout.
Provide your own printf function if printf not available.
FAT_CLUSTER_CACHE_ENTRIES
Size of cluster chain cache (can be undefined if not required).
Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2
Improves access speed considerably.
FATFS_INC_LFN_SUPPORT [1/0]
Enable/Disable support for long filenames.
FATFS_DIR_LIST_SUPPORT [1/0]
Include support for directory listing.
FATFS_INC_TIME_DATE_SUPPORT [1/0]
Use time/date functions provided by time.h to update creation & modification timestamps.
FATFS_INC_FORMAT_SUPPORT
Include support for formatting disks (FAT16 only).
FAT_PRINTF_NOINC_STDIO
Disable use of printf & inclusion of stdio.h
Revision History
-=-=-=-=-=-=-=-=-
v2.6.11 - Fix compilation with GCC on 64-bit machines
v2.6.10 - Added support for FAT32 format.
V2.6.9 - Added support for time & date handling.
V2.6.8 - Fixed error with FSINFO sector write.
V2.6.7 - Added fgets().
Fixed C warnings, removed dependancy on some string.h functions.
V2.6.6 – Massive read + write performance improvements.
V2.6.5 – Bug fixes for big endian systems.
V2.6.4 – Further bug fixes and performance improvements for write operations.
V2.6.3 – Peformance improvements, FAT16 formatting support. Various bug fixes.
V2.6 - Basic support for FAT16 added (18-04-10).
V2.5 - Code cleaned up. Many bugs fixed. Thread safety functions added.
V2.x - Write support added as well as better stdio like API.
V1.0 - Rewrite of all code to enable multiple files to be opened and provides a
better file API.
Also better string matching, and generally better C code than origonal
version.
V0.1c - Fetch_ID_Max_LBA() function added to retrieve Drive infomation and stoping
the drive reads from addressing a sector that is out of range.
V0.1b - fopen(), fgetc(), fopenDIR() using new software stack for IDE and FAT32
access.
V0.1a - First release (27/12/03); fopen(), fgetc() unbuffered reads.
FAT File IO Library License
-=-=-=-=-=-=-=-=-=-=-=-=-=-
This versions license: GPL
If you include GPL software in your project, you must release the source code of that project too.
If you would like a version with a more permissive license for use in closed source commercial applications please contact me for details.
Email: admin@ultra-embedded.com
Media Access API
-=-=-=-=-=-=-=-=-
int media_read(uint32 sector, uint8 *buffer, uint32 sector_count)
Params:
Sector: 32-bit sector number
Buffer: Target buffer to read n sectors of data into.
Sector_count: Number of sectors to read.
Return:
int, 1 = success, 0 = failure.
Description:
Application/target specific disk/media read function.
Sector number (sectors are usually 512 byte pages) to read.
Media Write API
int media_write(uint32 sector, uint8 *buffer, uint32 sector_count)
Params:
Sector: 32-bit sector number
Buffer: Target buffer to write n sectors of data from.
Sector_count: Number of sectors to write.
Return:
int, 1 = success, 0 = failure.
Description:
Application/target specific disk/media write function.
Sector number (sectors are usually 512 byte pages) to write to.
File IO Library Linkage
Use the following API to attach the media IO functions to the File IO library.
int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// FAT16/32 File IO Library
// V2.6
// Ultra-Embedded.com
// Copyright 2003 - 2012
//
// Email: admin@ultra-embedded.com
//
// License: GPL
// If you would like a version with a more permissive license for use in
// closed source commercial applications please contact me for details.
//-----------------------------------------------------------------------------
//
// This file is part of FAT File IO Library.
//
// FAT File IO Library is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// FAT File IO Library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with FAT File IO Library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#include <string.h>
#include "fat_defs.h"
#include "fat_access.h"
#include "fat_table.h"
#include "fat_write.h"
#include "fat_string.h"
#include "fat_misc.h"
//-----------------------------------------------------------------------------
// fatfs_init: Load FAT Parameters
//-----------------------------------------------------------------------------
int fatfs_init(struct fatfs *fs)
{
uint8 num_of_fats;
uint16 reserved_sectors;
uint32 FATSz;
uint32 root_dir_sectors;
uint32 total_sectors;
uint32 data_sectors;
uint32 count_of_clusters;
uint8 valid_partition = 0;
fs->currentsector.address = FAT32_INVALID_CLUSTER;
fs->currentsector.dirty = 0;
fs->next_free_cluster = 0; // Invalid
fatfs_fat_init(fs);
// Make sure we have a read function (write function is optional)
if (!fs->disk_io.read_media)
return FAT_INIT_MEDIA_ACCESS_ERROR;
// MBR: Sector 0 on the disk
// NOTE: Some removeable media does not have this.
// Load MBR (LBA 0) into the 512 byte buffer
if (!fs->disk_io.read_media(0, fs->currentsector.sector, 1))
return FAT_INIT_MEDIA_ACCESS_ERROR;
// Make Sure 0x55 and 0xAA are at end of sector
// (this should be the case regardless of the MBR or boot sector)
if (fs->currentsector.sector[SIGNATURE_POSITION] != 0x55 || fs->currentsector.sector[SIGNATURE_POSITION+1] != 0xAA)
return FAT_INIT_INVALID_SIGNATURE;
// Now check again using the access function to prove endian conversion function
if (GET_16BIT_WORD(fs->currentsector.sector, SIGNATURE_POSITION) != SIGNATURE_VALUE)
return FAT_INIT_ENDIAN_ERROR;
// Verify packed structures
if (sizeof(struct fat_dir_entry) != FAT_DIR_ENTRY_SIZE)
return FAT_INIT_STRUCT_PACKING;
// Check the partition type code
switch(fs->currentsector.sector[PARTITION1_TYPECODE_LOCATION])
{
case 0x0B:
case 0x06:
case 0x0C:
case 0x0E:
case 0x0F:
case 0x05:
valid_partition = 1;
break;
case 0x00:
valid_partition = 0;
break;
default:
if (fs->currentsector.sector[PARTITION1_TYPECODE_LOCATION] <= 0x06)
valid_partition = 1;
break;
}
// Read LBA Begin for the file system
if (valid_partition)
fs->lba_begin = GET_32BIT_WORD(fs->currentsector.sector, PARTITION1_LBA_BEGIN_LOCATION);
// Else possibly MBR less disk
else
fs->lba_begin = 0;
// Load Volume 1 table into sector buffer
// (We may already have this in the buffer if MBR less drive!)
if (!fs->disk_io.read_media(fs->lba_begin, fs->currentsector.sector, 1))
return FAT_INIT_MEDIA_ACCESS_ERROR;
// Make sure there are 512 bytes per cluster
if (GET_16BIT_WORD(fs->currentsector.sector, 0x0B) != FAT_SECTOR_SIZE)
return FAT_INIT_INVALID_SECTOR_SIZE;
// Load Parameters of FAT partition
fs->sectors_per_cluster = fs->currentsector.sector[BPB_SECPERCLUS];
reserved_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_RSVDSECCNT);
num_of_fats = fs->currentsector.sector[BPB_NUMFATS];
fs->root_entry_count = GET_16BIT_WORD(fs->currentsector.sector, BPB_ROOTENTCNT);
if(GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16) != 0)
fs->fat_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16);
else
fs->fat_sectors = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_FATSZ32);
// For FAT32 (which this may be)
fs->rootdir_first_cluster = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_ROOTCLUS);
fs->fs_info_sector = GET_16BIT_WORD(fs->currentsector.sector, BPB_FAT32_FSINFO);
// For FAT16 (which this may be), rootdir_first_cluster is actuall rootdir_first_sector
fs->rootdir_first_sector = reserved_sectors + (num_of_fats * fs->fat_sectors);
fs->rootdir_sectors = ((fs->root_entry_count * 32) + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE;
// First FAT LBA address
fs->fat_begin_lba = fs->lba_begin + reserved_sectors;
// The address of the first data cluster on this volume
fs->cluster_begin_lba = fs->fat_begin_lba + (num_of_fats * fs->fat_sectors);
if (GET_16BIT_WORD(fs->currentsector.sector, 0x1FE) != 0xAA55) // This signature should be AA55
return FAT_INIT_INVALID_SIGNATURE;
// Calculate the root dir sectors
root_dir_sectors = ((GET_16BIT_WORD(fs->currentsector.sector, BPB_ROOTENTCNT) * 32) + (GET_16BIT_WORD(fs->currentsector.sector, BPB_BYTSPERSEC) - 1)) / GET_16BIT_WORD(fs->currentsector.sector, BPB_BYTSPERSEC);
if(GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16) != 0)
FATSz = GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16);
else
FATSz = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_FATSZ32);
if(GET_16BIT_WORD(fs->currentsector.sector, BPB_TOTSEC16) != 0)
total_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_TOTSEC16);
else
total_sectors = GET_32BIT_WORD(fs->currentsector.sector, BPB_TOTSEC32);
data_sectors = total_sectors - (GET_16BIT_WORD(fs->currentsector.sector, BPB_RSVDSECCNT) + (fs->currentsector.sector[BPB_NUMFATS] * FATSz) + root_dir_sectors);
// Find out which version of FAT this is...
if (fs->sectors_per_cluster != 0)
{
count_of_clusters = data_sectors / fs->sectors_per_cluster;
if(count_of_clusters < 4085)
// Volume is FAT12
return FAT_INIT_WRONG_FILESYS_TYPE;
else if(count_of_clusters < 65525)
{
// Clear this FAT32 specific param
fs->rootdir_first_cluster = 0;
// Volume is FAT16
fs->fat_type = FAT_TYPE_16;
return FAT_INIT_OK;
}
else
{
// Volume is FAT32
fs->fat_type = FAT_TYPE_32;
return FAT_INIT_OK;
}
}
else
return FAT_INIT_WRONG_FILESYS_TYPE;
}
//-----------------------------------------------------------------------------
// fatfs_lba_of_cluster: This function converts a cluster number into a sector /
// LBA number.
//-----------------------------------------------------------------------------
uint32 fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number)
{
if (fs->fat_type == FAT_TYPE_16)
return (fs->cluster_begin_lba + (fs->root_entry_count * 32 / FAT_SECTOR_SIZE) + ((Cluster_Number-2) * fs->sectors_per_cluster));
else
return ((fs->cluster_begin_lba + ((Cluster_Number-2)*fs->sectors_per_cluster)));
}
//-----------------------------------------------------------------------------
// fatfs_sector_read:
//-----------------------------------------------------------------------------
int fatfs_sector_read(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count)
{
return fs->disk_io.read_media(lba, target, count);
}
//-----------------------------------------------------------------------------
// fatfs_sector_write:
//-----------------------------------------------------------------------------
int fatfs_sector_write(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count)
{
return fs->disk_io.write_media(lba, target, count);
}
//-----------------------------------------------------------------------------
// fatfs_sector_reader: From the provided startcluster and sector offset
// Returns True if success, returns False if not (including if read out of range)
//-----------------------------------------------------------------------------
int fatfs_sector_reader(struct fatfs *fs, uint32 start_cluster, uint32 offset, uint8 *target)
{
uint32 sector_to_read = 0;
uint32 cluster_to_read = 0;
uint32 cluster_chain = 0;
uint32 i;
uint32 lba;
// FAT16 Root directory
if (fs->fat_type == FAT_TYPE_16 && start_cluster == 0)
{
if (offset < fs->rootdir_sectors)
lba = fs->lba_begin + fs->rootdir_first_sector + offset;
else
return 0;
}
// FAT16/32 Other
else
{
// Set start of cluster chain to initial value
cluster_chain = start_cluster;
// Find parameters
cluster_to_read = offset / fs->sectors_per_cluster;
sector_to_read = offset - (cluster_to_read*fs->sectors_per_cluster);
// Follow chain to find cluster to read
for (i=0; i<cluster_to_read; i++)
cluster_chain = fatfs_find_next_cluster(fs, cluster_chain);
// If end of cluster chain then return false
if (cluster_chain == FAT32_LAST_CLUSTER)
return 0;
// Calculate sector address
lba = fatfs_lba_of_cluster(fs, cluster_chain)+sector_to_read;
}
// User provided target array
if (target)
return fs->disk_io.read_media(lba, target, 1);
// Else read sector if not already loaded
else if (lba != fs->currentsector.address)
{
fs->currentsector.address = lba;
return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
}
else
return 1;
}
//-----------------------------------------------------------------------------
// fatfs_read_sector: Read from the provided cluster and sector offset
// Returns True if success, returns False if not
//-----------------------------------------------------------------------------
int fatfs_read_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target)
{
// FAT16 Root directory
if (fs->fat_type == FAT_TYPE_16 && cluster == 0)
{
uint32 lba;
// In FAT16, there are a limited amount of sectors in root dir!
if (sector < fs->rootdir_sectors)
lba = fs->lba_begin + fs->rootdir_first_sector + sector;
else
return 0;
// User target buffer passed in
if (target)
{
// Read from disk
return fs->disk_io.read_media(lba, target, 1);
}
else
{
// Calculate read address
fs->currentsector.address = lba;
// Read from disk
return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
}
}
// FAT16/32 Other
else
{
// User target buffer passed in
if (target)
{
// Calculate read address
uint32 lba = fatfs_lba_of_cluster(fs, cluster) + sector;
// Read from disk
return fs->disk_io.read_media(lba, target, 1);
}
else
{
// Calculate write address
fs->currentsector.address = fatfs_lba_of_cluster(fs, cluster)+sector;
// Read from disk
return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
}
}
}
//-----------------------------------------------------------------------------
// fatfs_write_sector: Write to the provided cluster and sector offset
// Returns True if success, returns False if not
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fatfs_write_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target)
{
// No write access?
if (!fs->disk_io.write_media)
return 0;
// FAT16 Root directory
if (fs->fat_type == FAT_TYPE_16 && cluster == 0)
{
uint32 lba;
// In FAT16 we cannot extend the root dir!
if (sector < fs->rootdir_sectors)
lba = fs->lba_begin + fs->rootdir_first_sector + sector;
else
return 0;
// User target buffer passed in
if (target)
{
// Write to disk
return fs->disk_io.write_media(lba, target, 1);
}
else
{
// Calculate write address
fs->currentsector.address = lba;
// Write to disk
return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
}
}
// FAT16/32 Other
else
{
// User target buffer passed in
if (target)
{
// Calculate write address
uint32 lba = fatfs_lba_of_cluster(fs, cluster) + sector;
// Write to disk
return fs->disk_io.write_media(lba, target, 1);
}
else
{
// Calculate write address
fs->currentsector.address = fatfs_lba_of_cluster(fs, cluster)+sector;
// Write to disk
return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
}
}
}
#endif
//-----------------------------------------------------------------------------
// fatfs_show_details: Show the details about the filesystem
//-----------------------------------------------------------------------------
void fatfs_show_details(struct fatfs *fs)
{
FAT_PRINTF(("FAT details:\r\n"));
FAT_PRINTF((" Type =%s", (fs->fat_type == FAT_TYPE_32) ? "FAT32": "FAT16"));
FAT_PRINTF((" Root Dir First Cluster = %x\r\n", fs->rootdir_first_cluster));
FAT_PRINTF((" FAT Begin LBA = 0x%x\r\n",fs->fat_begin_lba));
FAT_PRINTF((" Cluster Begin LBA = 0x%x\r\n",fs->cluster_begin_lba));
FAT_PRINTF((" Sectors Per Cluster = %d\r\n", fs->sectors_per_cluster));
}
//-----------------------------------------------------------------------------
// fatfs_get_root_cluster: Get the root dir cluster
//-----------------------------------------------------------------------------
uint32 fatfs_get_root_cluster(struct fatfs *fs)
{
// NOTE: On FAT16 this will be 0 which has a special meaning...
return fs->rootdir_first_cluster;
}
//-------------------------------------------------------------
// fatfs_get_file_entry: Find the file entry for a filename
//-------------------------------------------------------------
uint32 fatfs_get_file_entry(struct fatfs *fs, uint32 Cluster, char *name_to_find, struct fat_dir_entry *sfEntry)
{
uint8 item=0;
uint16 recordoffset = 0;
uint8 i=0;
int x=0;
char *long_filename = NULL;
char short_filename[13];
struct lfn_cache lfn;
int dotRequired = 0;
struct fat_dir_entry *directoryEntry;
fatfs_lfn_cache_init(&lfn, 1);
// Main cluster following loop
while (1)
{
// Read sector
if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
{
// Analyse Sector
for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
{
// Create the multiplier for sector access
recordoffset = FAT_DIR_ENTRY_SIZE * item;
// Overlay directory entry over buffer
directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
#if FATFS_INC_LFN_SUPPORT
// Long File Name Text Found
if (fatfs_entry_lfn_text(directoryEntry) )
fatfs_lfn_cache_entry(&lfn, fs->currentsector.sector+recordoffset);
// If Invalid record found delete any long file name information collated
else if (fatfs_entry_lfn_invalid(directoryEntry) )
fatfs_lfn_cache_init(&lfn, 0);
// Normal SFN Entry and Long text exists
else if (fatfs_entry_lfn_exists(&lfn, directoryEntry) )
{
long_filename = fatfs_lfn_cache_get(&lfn);
// Compare names to see if they match
if (fatfs_compare_names(long_filename, name_to_find))
{
memcpy(sfEntry,directoryEntry,sizeof(struct fat_dir_entry));
return 1;
}
fatfs_lfn_cache_init(&lfn, 0);
}
else
#endif
// Normal Entry, only 8.3 Text
if (fatfs_entry_sfn_only(directoryEntry) )
{
memset(short_filename, 0, sizeof(short_filename));
// Copy name to string
for (i=0; i<8; i++)
short_filename[i] = directoryEntry->Name[i];
// Extension
dotRequired = 0;
for (i=8; i<11; i++)
{
short_filename[i+1] = directoryEntry->Name[i];
if (directoryEntry->Name[i] != ' ')
dotRequired = 1;
}
// Dot only required if extension present
if (dotRequired)
{
// If not . or .. entry
if (short_filename[0]!='.')
short_filename[8] = '.';
else
short_filename[8] = ' ';
}
else
short_filename[8] = ' ';
// Compare names to see if they match
if (fatfs_compare_names(short_filename, name_to_find))
{
memcpy(sfEntry,directoryEntry,sizeof(struct fat_dir_entry));
return 1;
}
fatfs_lfn_cache_init(&lfn, 0);
}
} // End of if
}
else
break;
} // End of while loop
return 0;
}
//-------------------------------------------------------------
// fatfs_sfn_exists: Check if a short filename exists.
// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
//-------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fatfs_sfn_exists(struct fatfs *fs, uint32 Cluster, char *shortname)
{
uint8 item=0;
uint16 recordoffset = 0;
int x=0;
struct fat_dir_entry *directoryEntry;
// Main cluster following loop
while (1)
{
// Read sector
if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
{
// Analyse Sector
for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
{
// Create the multiplier for sector access
recordoffset = FAT_DIR_ENTRY_SIZE * item;
// Overlay directory entry over buffer
directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
#if FATFS_INC_LFN_SUPPORT
// Long File Name Text Found
if (fatfs_entry_lfn_text(directoryEntry) )
;
// If Invalid record found delete any long file name information collated
else if (fatfs_entry_lfn_invalid(directoryEntry) )
;
else
#endif
// Normal Entry, only 8.3 Text
if (fatfs_entry_sfn_only(directoryEntry) )
{
if (strncmp((const char*)directoryEntry->Name, shortname, 11)==0)
return 1;
}
} // End of if
}
else
break;
} // End of while loop
return 0;
}
#endif
//-------------------------------------------------------------
// fatfs_update_timestamps: Update date/time details
//-------------------------------------------------------------
#if FATFS_INC_TIME_DATE_SUPPORT
int fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access)
{
time_t time_now;
struct tm * time_info;
uint16 fat_time;
uint16 fat_date;
// Get system time
time(&time_now);
// Convert to local time
time_info = localtime(&time_now);
// Convert time to FAT format
fat_time = fatfs_convert_to_fat_time(time_info->tm_hour, time_info->tm_min, time_info->tm_sec);
// Convert date to FAT format
fat_date = fatfs_convert_to_fat_date(time_info->tm_mday, time_info->tm_mon + 1, time_info->tm_year + 1900);
// Update requested fields
if (create)
{
directoryEntry->CrtTime[1] = fat_time >> 8;
directoryEntry->CrtTime[0] = fat_time >> 0;
directoryEntry->CrtDate[1] = fat_date >> 8;
directoryEntry->CrtDate[0] = fat_date >> 0;
}
if (modify)
{
directoryEntry->WrtTime[1] = fat_time >> 8;
directoryEntry->WrtTime[0] = fat_time >> 0;
directoryEntry->WrtDate[1] = fat_date >> 8;
directoryEntry->WrtDate[0] = fat_date >> 0;
}
if (access)
{
directoryEntry->LstAccDate[1] = fat_time >> 8;
directoryEntry->LstAccDate[0] = fat_time >> 0;
directoryEntry->LstAccDate[1] = fat_date >> 8;
directoryEntry->LstAccDate[0] = fat_date >> 0;
}
return 1;
}
#endif
//-------------------------------------------------------------
// fatfs_update_file_length: Find a SFN entry and update it
// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
//-------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fatfs_update_file_length(struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength)
{
uint8 item=0;
uint16 recordoffset = 0;
int x=0;
struct fat_dir_entry *directoryEntry;
// No write access?
if (!fs->disk_io.write_media)
return 0;
// Main cluster following loop
while (1)
{
// Read sector
if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
{
// Analyse Sector
for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
{
// Create the multiplier for sector access
recordoffset = FAT_DIR_ENTRY_SIZE * item;
// Overlay directory entry over buffer
directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
#if FATFS_INC_LFN_SUPPORT
// Long File Name Text Found
if (fatfs_entry_lfn_text(directoryEntry) )
;
// If Invalid record found delete any long file name information collated
else if (fatfs_entry_lfn_invalid(directoryEntry) )
;
// Normal Entry, only 8.3 Text
else
#endif
if (fatfs_entry_sfn_only(directoryEntry) )
{
if (strncmp((const char*)directoryEntry->Name, shortname, 11)==0)
{
directoryEntry->FileSize = FAT_HTONL(fileLength);
#if FATFS_INC_TIME_DATE_SUPPORT
// Update access / modify time & date
fatfs_update_timestamps(directoryEntry, 0, 1, 1);
#endif
// Update sfn entry
memcpy((uint8*)(fs->currentsector.sector+recordoffset), (uint8*)directoryEntry, sizeof(struct fat_dir_entry));
// Write sector back
return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
}
}
} // End of if
}
else
break;
} // End of while loop
return 0;
}
#endif
//-------------------------------------------------------------
// fatfs_mark_file_deleted: Find a SFN entry and mark if as deleted
// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
//-------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fatfs_mark_file_deleted(struct fatfs *fs, uint32 Cluster, char *shortname)
{
uint8 item=0;
uint16 recordoffset = 0;
int x=0;
struct fat_dir_entry *directoryEntry;
// No write access?
if (!fs->disk_io.write_media)
return 0;
// Main cluster following loop
while (1)
{
// Read sector
if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
{
// Analyse Sector
for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
{
// Create the multiplier for sector access
recordoffset = FAT_DIR_ENTRY_SIZE * item;
// Overlay directory entry over buffer
directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
#if FATFS_INC_LFN_SUPPORT
// Long File Name Text Found
if (fatfs_entry_lfn_text(directoryEntry) )
;
// If Invalid record found delete any long file name information collated
else if (fatfs_entry_lfn_invalid(directoryEntry) )
;
// Normal Entry, only 8.3 Text
else
#endif
if (fatfs_entry_sfn_only(directoryEntry) )
{
if (strncmp((const char *)directoryEntry->Name, shortname, 11)==0)
{
// Mark as deleted
directoryEntry->Name[0] = FILE_HEADER_DELETED;
#if FATFS_INC_TIME_DATE_SUPPORT
// Update access / modify time & date
fatfs_update_timestamps(directoryEntry, 0, 1, 1);
#endif
// Update sfn entry
memcpy((uint8*)(fs->currentsector.sector+recordoffset), (uint8*)directoryEntry, sizeof(struct fat_dir_entry));
// Write sector back
return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
}
}
} // End of if
}
else
break;
} // End of while loop
return 0;
}
#endif
//-----------------------------------------------------------------------------
// fatfs_list_directory_start: Initialise a directory listing procedure
//-----------------------------------------------------------------------------
#if FATFS_DIR_LIST_SUPPORT
void fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster)
{
dirls->cluster = StartCluster;
dirls->sector = 0;
dirls->offset = 0;
}
#endif
//-----------------------------------------------------------------------------
// fatfs_list_directory_next: Get the next entry in the directory.
// Returns: 1 = found, 0 = end of listing
//-----------------------------------------------------------------------------
#if FATFS_DIR_LIST_SUPPORT
int fatfs_list_directory_next(struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry)
{
uint8 i,item;
uint16 recordoffset;
struct fat_dir_entry *directoryEntry;
char *long_filename = NULL;
char short_filename[13];
struct lfn_cache lfn;
int dotRequired = 0;
int result = 0;
// Initialise LFN cache first
fatfs_lfn_cache_init(&lfn, 0);
while (1)
{
// If data read OK
if (fatfs_sector_reader(fs, dirls->cluster, dirls->sector, 0))
{
// Maximum of 16 directory entries
for (item = dirls->offset; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
{
// Increase directory offset
recordoffset = FAT_DIR_ENTRY_SIZE * item;
// Overlay directory entry over buffer
directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
#if FATFS_INC_LFN_SUPPORT
// Long File Name Text Found
if ( fatfs_entry_lfn_text(directoryEntry) )
fatfs_lfn_cache_entry(&lfn, fs->currentsector.sector+recordoffset);
// If Invalid record found delete any long file name information collated
else if ( fatfs_entry_lfn_invalid(directoryEntry) )
fatfs_lfn_cache_init(&lfn, 0);
// Normal SFN Entry and Long text exists
else if (fatfs_entry_lfn_exists(&lfn, directoryEntry) )
{
// Get text
long_filename = fatfs_lfn_cache_get(&lfn);
#if defined(_MSC_VER) || defined(WIN32)
strcpy_s(entry->filename, FATFS_MAX_LONG_FILENAME - 1, long_filename);
#else
strncpy(entry->filename, long_filename, FATFS_MAX_LONG_FILENAME - 1);
#endif
if (fatfs_entry_is_dir(directoryEntry))
entry->is_dir = 1;
else
entry->is_dir = 0;
#if FATFS_INC_TIME_DATE_SUPPORT
// Get time / dates
entry->create_time = ((uint16)directoryEntry->CrtTime[1] << 8) | directoryEntry->CrtTime[0];
entry->create_date = ((uint16)directoryEntry->CrtDate[1] << 8) | directoryEntry->CrtDate[0];
entry->access_date = ((uint16)directoryEntry->LstAccDate[1] << 8) | directoryEntry->LstAccDate[0];
entry->write_time = ((uint16)directoryEntry->WrtTime[1] << 8) | directoryEntry->WrtTime[0];
entry->write_date = ((uint16)directoryEntry->WrtDate[1] << 8) | directoryEntry->WrtDate[0];
#endif
entry->size = FAT_HTONL(directoryEntry->FileSize);
entry->cluster = (FAT_HTONS(directoryEntry->FstClusHI)<<16) | FAT_HTONS(directoryEntry->FstClusLO);
// Next starting position
dirls->offset = item + 1;
result = 1;
return 1;
}
// Normal Entry, only 8.3 Text
else
#endif
if ( fatfs_entry_sfn_only(directoryEntry) )
{
fatfs_lfn_cache_init(&lfn, 0);
memset(short_filename, 0, sizeof(short_filename));
// Copy name to string
for (i=0; i<8; i++)
short_filename[i] = directoryEntry->Name[i];
// Extension
dotRequired = 0;
for (i=8; i<11; i++)
{
short_filename[i+1] = directoryEntry->Name[i];
if (directoryEntry->Name[i] != ' ')
dotRequired = 1;
}
// Dot only required if extension present
if (dotRequired)
{
// If not . or .. entry
if (short_filename[0]!='.')
short_filename[8] = '.';
else
short_filename[8] = ' ';
}
else
short_filename[8] = ' ';
fatfs_get_sfn_display_name(entry->filename, short_filename);
if (fatfs_entry_is_dir(directoryEntry))
entry->is_dir = 1;
else
entry->is_dir = 0;
#if FATFS_INC_TIME_DATE_SUPPORT
// Get time / dates
entry->create_time = ((uint16)directoryEntry->CrtTime[1] << 8) | directoryEntry->CrtTime[0];
entry->create_date = ((uint16)directoryEntry->CrtDate[1] << 8) | directoryEntry->CrtDate[0];
entry->access_date = ((uint16)directoryEntry->LstAccDate[1] << 8) | directoryEntry->LstAccDate[0];
entry->write_time = ((uint16)directoryEntry->WrtTime[1] << 8) | directoryEntry->WrtTime[0];
entry->write_date = ((uint16)directoryEntry->WrtDate[1] << 8) | directoryEntry->WrtDate[0];
#endif
entry->size = FAT_HTONL(directoryEntry->FileSize);
entry->cluster = (FAT_HTONS(directoryEntry->FstClusHI)<<16) | FAT_HTONS(directoryEntry->FstClusLO);
// Next starting position
dirls->offset = item + 1;
result = 1;
return 1;
}
}// end of for
// If reached end of the dir move onto next sector
dirls->sector++;
dirls->offset = 0;
}
else
break;
}
return result;
}
#endif
#ifndef __FAT_ACCESS_H__
#define __FAT_ACCESS_H__
#include "fat_defs.h"
#include "fat_opts.h"
//-----------------------------------------------------------------------------
// Defines
//-----------------------------------------------------------------------------
#define FAT_INIT_OK 0
#define FAT_INIT_MEDIA_ACCESS_ERROR (-1)
#define FAT_INIT_INVALID_SECTOR_SIZE (-2)
#define FAT_INIT_INVALID_SIGNATURE (-3)
#define FAT_INIT_ENDIAN_ERROR (-4)
#define FAT_INIT_WRONG_FILESYS_TYPE (-5)
#define FAT_INIT_WRONG_PARTITION_TYPE (-6)
#define FAT_INIT_STRUCT_PACKING (-7)
#define FAT_DIR_ENTRIES_PER_SECTOR (FAT_SECTOR_SIZE / FAT_DIR_ENTRY_SIZE)
//-----------------------------------------------------------------------------
// Function Pointers
//-----------------------------------------------------------------------------
typedef int (*fn_diskio_read) (uint32 sector, uint8 *buffer, uint32 sector_count);
typedef int (*fn_diskio_write)(uint32 sector, uint8 *buffer, uint32 sector_count);
//-----------------------------------------------------------------------------
// Structures
//-----------------------------------------------------------------------------
struct disk_if
{
// User supplied function pointers for disk IO
fn_diskio_read read_media;
fn_diskio_write write_media;
};
// Forward declaration
struct fat_buffer;
struct fat_buffer
{
uint8 sector[FAT_SECTOR_SIZE * FAT_BUFFER_SECTORS];
uint32 address;
int dirty;
uint8 * ptr;
// Next in chain of sector buffers
struct fat_buffer *next;
};
typedef enum eFatType
{
FAT_TYPE_16,
FAT_TYPE_32
} tFatType;
struct fatfs
{
// Filesystem globals
uint8 sectors_per_cluster;
uint32 cluster_begin_lba;
uint32 rootdir_first_cluster;
uint32 rootdir_first_sector;
uint32 rootdir_sectors;
uint32 fat_begin_lba;
uint16 fs_info_sector;
uint32 lba_begin;
uint32 fat_sectors;
uint32 next_free_cluster;
uint16 root_entry_count;
uint16 reserved_sectors;
uint8 num_of_fats;
tFatType fat_type;
// Disk/Media API
struct disk_if disk_io;
// [Optional] Thread Safety
void (*fl_lock)(void);
void (*fl_unlock)(void);
// Working buffer
struct fat_buffer currentsector;
// FAT Buffer
struct fat_buffer *fat_buffer_head;
struct fat_buffer fat_buffers[FAT_BUFFERS];
};
struct fs_dir_list_status
{
uint32 sector;
uint32 cluster;
uint8 offset;
};
struct fs_dir_ent
{
char filename[FATFS_MAX_LONG_FILENAME];
uint8 is_dir;
uint32 cluster;
uint32 size;
#if FATFS_INC_TIME_DATE_SUPPORT
uint16 access_date;
uint16 write_time;
uint16 write_date;
uint16 create_date;
uint16 create_time;
#endif
};
//-----------------------------------------------------------------------------
// Prototypes
//-----------------------------------------------------------------------------
int fatfs_init(struct fatfs *fs);
uint32 fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number);
int fatfs_sector_reader(struct fatfs *fs, uint32 Startcluster, uint32 offset, uint8 *target);
int fatfs_sector_read(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
int fatfs_sector_write(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
int fatfs_read_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
int fatfs_write_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
void fatfs_show_details(struct fatfs *fs);
uint32 fatfs_get_root_cluster(struct fatfs *fs);
uint32 fatfs_get_file_entry(struct fatfs *fs, uint32 Cluster, char *nametofind, struct fat_dir_entry *sfEntry);
int fatfs_sfn_exists(struct fatfs *fs, uint32 Cluster, char *shortname);
int fatfs_update_file_length(struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength);
int fatfs_mark_file_deleted(struct fatfs *fs, uint32 Cluster, char *shortname);
void fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster);
int fatfs_list_directory_next(struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry);
int fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access);
#endif
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// FAT16/32 File IO Library
// V2.6
// Ultra-Embedded.com
// Copyright 2003 - 2012
//
// Email: admin@ultra-embedded.com
//
// License: GPL
// If you would like a version with a more permissive license for use in
// closed source commercial applications please contact me for details.
//-----------------------------------------------------------------------------
//
// This file is part of FAT File IO Library.
//
// FAT File IO Library is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// FAT File IO Library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with FAT File IO Library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#include <string.h>
#include "fat_cache.h"
// Per file cluster chain caching used to improve performance.
// This does not have to be enabled for architectures with low
// memory space.
//-----------------------------------------------------------------------------
// fatfs_cache_init:
//-----------------------------------------------------------------------------
int fatfs_cache_init(struct fatfs *fs, FL_FILE *file)
{
#ifdef FAT_CLUSTER_CACHE_ENTRIES
int i;
for (i=0;i<FAT_CLUSTER_CACHE_ENTRIES;i++)
{
file->cluster_cache_idx[i] = 0xFFFFFFFF; // Not used
file->cluster_cache_data[i] = 0;
}
#endif
return 1;
}
//-----------------------------------------------------------------------------
// fatfs_cache_get_next_cluster:
//-----------------------------------------------------------------------------
int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster)
{
#ifdef FAT_CLUSTER_CACHE_ENTRIES
uint32 slot = clusterIdx % FAT_CLUSTER_CACHE_ENTRIES;
if (file->cluster_cache_idx[slot] == clusterIdx)
{
*pNextCluster = file->cluster_cache_data[slot];
return 1;
}
#endif
return 0;
}
//-----------------------------------------------------------------------------
// fatfs_cache_set_next_cluster:
//-----------------------------------------------------------------------------
int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster)
{
#ifdef FAT_CLUSTER_CACHE_ENTRIES
uint32 slot = clusterIdx % FAT_CLUSTER_CACHE_ENTRIES;
if (file->cluster_cache_idx[slot] == clusterIdx)
file->cluster_cache_data[slot] = nextCluster;
else
{
file->cluster_cache_idx[slot] = clusterIdx;
file->cluster_cache_data[slot] = nextCluster;
}
#endif
return 1;
}
#ifndef __FAT_CACHE_H__
#define __FAT_CACHE_H__
#include "fat_filelib.h"
//-----------------------------------------------------------------------------
// Prototypes
//-----------------------------------------------------------------------------
int fatfs_cache_init(struct fatfs *fs, FL_FILE *file);
int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster);
int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster);
#endif
#ifndef __FAT_DEFS_H__
#define __FAT_DEFS_H__
#include "fat_opts.h"
#include "fat_types.h"
//-----------------------------------------------------------------------------
// FAT32 Offsets
// Name Offset
//-----------------------------------------------------------------------------
// Boot Sector
#define BS_JMPBOOT 0 // Length = 3
#define BS_OEMNAME 3 // Length = 8
#define BPB_BYTSPERSEC 11 // Length = 2
#define BPB_SECPERCLUS 13 // Length = 1
#define BPB_RSVDSECCNT 14 // Length = 2
#define BPB_NUMFATS 16 // Length = 1
#define BPB_ROOTENTCNT 17 // Length = 2
#define BPB_TOTSEC16 19 // Length = 2
#define BPB_MEDIA 21 // Length = 1
#define BPB_FATSZ16 22 // Length = 2
#define BPB_SECPERTRK 24 // Length = 2
#define BPB_NUMHEADS 26 // Length = 2
#define BPB_HIDDSEC 28 // Length = 4
#define BPB_TOTSEC32 32 // Length = 4
// FAT 12/16
#define BS_FAT_DRVNUM 36 // Length = 1
#define BS_FAT_BOOTSIG 38 // Length = 1
#define BS_FAT_VOLID 39 // Length = 4
#define BS_FAT_VOLLAB 43 // Length = 11
#define BS_FAT_FILSYSTYPE 54 // Length = 8
// FAT 32
#define BPB_FAT32_FATSZ32 36 // Length = 4
#define BPB_FAT32_EXTFLAGS 40 // Length = 2
#define BPB_FAT32_FSVER 42 // Length = 2
#define BPB_FAT32_ROOTCLUS 44 // Length = 4
#define BPB_FAT32_FSINFO 48 // Length = 2
#define BPB_FAT32_BKBOOTSEC 50 // Length = 2
#define BS_FAT32_DRVNUM 64 // Length = 1
#define BS_FAT32_BOOTSIG 66 // Length = 1
#define BS_FAT32_VOLID 67 // Length = 4
#define BS_FAT32_VOLLAB 71 // Length = 11
#define BS_FAT32_FILSYSTYPE 82 // Length = 8
//-----------------------------------------------------------------------------
// FAT Types
//-----------------------------------------------------------------------------
#define FAT_TYPE_FAT12 1
#define FAT_TYPE_FAT16 2
#define FAT_TYPE_FAT32 3
//-----------------------------------------------------------------------------
// FAT32 Specific Statics
//-----------------------------------------------------------------------------
#define SIGNATURE_POSITION 510
#define SIGNATURE_VALUE 0xAA55
#define PARTITION1_TYPECODE_LOCATION 450
#define FAT32_TYPECODE1 0x0B
#define FAT32_TYPECODE2 0x0C
#define PARTITION1_LBA_BEGIN_LOCATION 454
#define PARTITION1_SIZE_LOCATION 458
#define FAT_DIR_ENTRY_SIZE 32
#define FAT_SFN_SIZE_FULL 11
#define FAT_SFN_SIZE_PARTIAL 8
//-----------------------------------------------------------------------------
// FAT32 File Attributes and Types
//-----------------------------------------------------------------------------
#define FILE_ATTR_READ_ONLY 0x01
#define FILE_ATTR_HIDDEN 0x02
#define FILE_ATTR_SYSTEM 0x04
#define FILE_ATTR_SYSHID 0x06
#define FILE_ATTR_VOLUME_ID 0x08
#define FILE_ATTR_DIRECTORY 0x10
#define FILE_ATTR_ARCHIVE 0x20
#define FILE_ATTR_LFN_TEXT 0x0F
#define FILE_HEADER_BLANK 0x00
#define FILE_HEADER_DELETED 0xE5
#define FILE_TYPE_DIR 0x10
#define FILE_TYPE_FILE 0x20
//-----------------------------------------------------------------------------
// Time / Date details
//-----------------------------------------------------------------------------
#define FAT_TIME_HOURS_SHIFT 11
#define FAT_TIME_HOURS_MASK 0x1F
#define FAT_TIME_MINUTES_SHIFT 5
#define FAT_TIME_MINUTES_MASK 0x3F
#define FAT_TIME_SECONDS_SHIFT 0
#define FAT_TIME_SECONDS_MASK 0x1F
#define FAT_TIME_SECONDS_SCALE 2
#define FAT_DATE_YEAR_SHIFT 9
#define FAT_DATE_YEAR_MASK 0x7F
#define FAT_DATE_MONTH_SHIFT 5
#define FAT_DATE_MONTH_MASK 0xF
#define FAT_DATE_DAY_SHIFT 0
#define FAT_DATE_DAY_MASK 0x1F
#define FAT_DATE_YEAR_OFFSET 1980
//-----------------------------------------------------------------------------
// Other Defines
//-----------------------------------------------------------------------------
#define FAT32_LAST_CLUSTER 0xFFFFFFFF
#define FAT32_INVALID_CLUSTER 0xFFFFFFFF
STRUCT_PACK_BEGIN
struct fat_dir_entry STRUCT_PACK
{
uint8 Name[11];
uint8 Attr;
uint8 NTRes;
uint8 CrtTimeTenth;
uint8 CrtTime[2];
uint8 CrtDate[2];
uint8 LstAccDate[2];
uint16 FstClusHI;
uint8 WrtTime[2];
uint8 WrtDate[2];
uint16 FstClusLO;
uint32 FileSize;
} STRUCT_PACKED;
STRUCT_PACK_END
#endif
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// FAT16/32 File IO Library
// V2.6
// Ultra-Embedded.com
// Copyright 2003 - 2012
//
// Email: admin@ultra-embedded.com
//
// License: GPL
// If you would like a version with a more permissive license for use in
// closed source commercial applications please contact me for details.
//-----------------------------------------------------------------------------
//
// This file is part of FAT File IO Library.
//
// FAT File IO Library is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// FAT File IO Library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with FAT File IO Library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#include <stdlib.h>
#include <string.h>
#include "fat_defs.h"
#include "fat_access.h"
#include "fat_table.h"
#include "fat_write.h"
#include "fat_misc.h"
#include "fat_string.h"
#include "fat_filelib.h"
#include "fat_cache.h"
//-----------------------------------------------------------------------------
// Locals
//-----------------------------------------------------------------------------
static FL_FILE _files[FATFS_MAX_OPEN_FILES];
static int _filelib_init = 0;
static int _filelib_valid = 0;
static struct fatfs _fs;
static struct fat_list _open_file_list;
static struct fat_list _free_file_list;
//-----------------------------------------------------------------------------
// Macros
//-----------------------------------------------------------------------------
// Macro for checking if file lib is initialised
#define CHECK_FL_INIT() { if (_filelib_init==0) fl_init(); }
#define FL_LOCK(a) do { if ((a)->fl_lock) (a)->fl_lock(); } while (0)
#define FL_UNLOCK(a) do { if ((a)->fl_unlock) (a)->fl_unlock(); } while (0)
//-----------------------------------------------------------------------------
// Local Functions
//-----------------------------------------------------------------------------
static void _fl_init();
//-----------------------------------------------------------------------------
// _allocate_file: Find a slot in the open files buffer for a new file
//-----------------------------------------------------------------------------
static FL_FILE* _allocate_file(void)
{
// Allocate free file
struct fat_node *node = fat_list_pop_head(&_free_file_list);
// Add to open list
if (node)
fat_list_insert_last(&_open_file_list, node);
return fat_list_entry(node, FL_FILE, list_node);
}
//-----------------------------------------------------------------------------
// _check_file_open: Returns true if the file is already open
//-----------------------------------------------------------------------------
static int _check_file_open(FL_FILE* file)
{
struct fat_node *node;
// Compare open files
fat_list_for_each(&_open_file_list, node)
{
FL_FILE* openFile = fat_list_entry(node, FL_FILE, list_node);
// If not the current file
if (openFile != file)
{
// Compare path and name
if ( (fatfs_compare_names(openFile->path,file->path)) && (fatfs_compare_names(openFile->filename,file->filename)) )
return 1;
}
}
return 0;
}
//-----------------------------------------------------------------------------
// _free_file: Free open file handle
//-----------------------------------------------------------------------------
static void _free_file(FL_FILE* file)
{
// Remove from open list
fat_list_remove(&_open_file_list, &file->list_node);
// Add to free list
fat_list_insert_last(&_free_file_list, &file->list_node);
}
//-----------------------------------------------------------------------------
// Low Level
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// _open_directory: Cycle through path string to find the start cluster
// address of the highest subdir.
//-----------------------------------------------------------------------------
static int _open_directory(char *path, uint32 *pathCluster)
{
int levels;
int sublevel;
char currentfolder[FATFS_MAX_LONG_FILENAME];
struct fat_dir_entry sfEntry;
uint32 startcluster;
// Set starting cluster to root cluster
startcluster = fatfs_get_root_cluster(&_fs);
// Find number of levels
levels = fatfs_total_path_levels(path);
// Cycle through each level and get the start sector
for (sublevel=0;sublevel<(levels+1);sublevel++)
{
if (fatfs_get_substring(path, sublevel, currentfolder, sizeof(currentfolder)) == -1)
return 0;
// Find clusteraddress for folder (currentfolder)
if (fatfs_get_file_entry(&_fs, startcluster, currentfolder,&sfEntry))
{
// Check entry is folder
if (fatfs_entry_is_dir(&sfEntry))
startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
else
return 0;
}
else
return 0;
}
*pathCluster = startcluster;
return 1;
}
//-----------------------------------------------------------------------------
// _create_directory: Cycle through path string and create the end directory
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
static int _create_directory(char *path)
{
FL_FILE* file;
struct fat_dir_entry sfEntry;
char shortFilename[FAT_SFN_SIZE_FULL];
int tailNum = 0;
int i;
// Allocate a new file handle
file = _allocate_file();
if (!file)
return 0;
// Clear filename
memset(file->path, '\0', sizeof(file->path));
memset(file->filename, '\0', sizeof(file->filename));
// Split full path into filename and directory path
if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
{
_free_file(file);
return 0;
}
// Check if file already open
if (_check_file_open(file))
{
_free_file(file);
return 0;
}
// If file is in the root dir
if (file->path[0] == 0)
file->parentcluster = fatfs_get_root_cluster(&_fs);
else
{
// Find parent directory start cluster
if (!_open_directory(file->path, &file->parentcluster))
{
_free_file(file);
return 0;
}
}
// Check if same filename exists in directory
if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1)
{
_free_file(file);
return 0;
}
file->startcluster = 0;
// Create the file space for the folder (at least one clusters worth!)
if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1))
{
_free_file(file);
return 0;
}
// Erase new directory cluster
memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
for (i=0;i<_fs.sectors_per_cluster;i++)
{
if (!fatfs_write_sector(&_fs, file->startcluster, i, file->file_data_sector))
{
_free_file(file);
return 0;
}
}
#if FATFS_INC_LFN_SUPPORT
// Generate a short filename & tail
tailNum = 0;
do
{
// Create a standard short filename (without tail)
fatfs_lfn_create_sfn(shortFilename, file->filename);
// If second hit or more, generate a ~n tail
if (tailNum != 0)
fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
// Try with no tail if first entry
else
memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
// Check if entry exists already or not
if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0)
break;
tailNum++;
}
while (tailNum < 9999);
// We reached the max number of duplicate short file names (unlikely!)
if (tailNum == 9999)
{
// Delete allocated space
fatfs_free_cluster_chain(&_fs, file->startcluster);
_free_file(file);
return 0;
}
#else
// Create a standard short filename (without tail)
if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
{
// Delete allocated space
fatfs_free_cluster_chain(&_fs, file->startcluster);
_free_file(file);
return 0;
}
// Copy to SFN space
memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
// Check if entry exists already
if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename))
{
// Delete allocated space
fatfs_free_cluster_chain(&_fs, file->startcluster);
_free_file(file);
return 0;
}
#endif
// Add file to disk
if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 1))
{
// Delete allocated space
fatfs_free_cluster_chain(&_fs, file->startcluster);
_free_file(file);
return 0;
}
// General
file->filelength = 0;
file->bytenum = 0;
file->file_data_address = 0xFFFFFFFF;
file->file_data_dirty = 0;
file->filelength_changed = 0;
// Quick lookup for next link in the chain
file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
fatfs_fat_purge(&_fs);
_free_file(file);
return 1;
}
#endif
//-----------------------------------------------------------------------------
// _open_file: Open a file for reading
//-----------------------------------------------------------------------------
static FL_FILE* _open_file(const char *path)
{
FL_FILE* file;
struct fat_dir_entry sfEntry;
// Allocate a new file handle
file = _allocate_file();
if (!file)
return NULL;
// Clear filename
memset(file->path, '\0', sizeof(file->path));
memset(file->filename, '\0', sizeof(file->filename));
// Split full path into filename and directory path
if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
{
_free_file(file);
return NULL;
}
// Check if file already open
if (_check_file_open(file))
{
_free_file(file);
return NULL;
}
// If file is in the root dir
if (file->path[0]==0)
file->parentcluster = fatfs_get_root_cluster(&_fs);
else
{
// Find parent directory start cluster
if (!_open_directory(file->path, &file->parentcluster))
{
_free_file(file);
return NULL;
}
}
// Using dir cluster address search for filename
if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry))
// Make sure entry is file not dir!
if (fatfs_entry_is_file(&sfEntry))
{
// Initialise file details
memcpy(file->shortfilename, sfEntry.Name, FAT_SFN_SIZE_FULL);
file->filelength = FAT_HTONL(sfEntry.FileSize);
file->bytenum = 0;
file->startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
file->file_data_address = 0xFFFFFFFF;
file->file_data_dirty = 0;
file->filelength_changed = 0;
// Quick lookup for next link in the chain
file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
fatfs_cache_init(&_fs, file);
fatfs_fat_purge(&_fs);
return file;
}
_free_file(file);
return NULL;
}
//-----------------------------------------------------------------------------
// _create_file: Create a new file
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
static FL_FILE* _create_file(const char *filename)
{
FL_FILE* file;
struct fat_dir_entry sfEntry;
char shortFilename[FAT_SFN_SIZE_FULL];
int tailNum = 0;
// No write access?
if (!_fs.disk_io.write_media)
return NULL;
// Allocate a new file handle
file = _allocate_file();
if (!file)
return NULL;
// Clear filename
memset(file->path, '\0', sizeof(file->path));
memset(file->filename, '\0', sizeof(file->filename));
// Split full path into filename and directory path
if (fatfs_split_path((char*)filename, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
{
_free_file(file);
return NULL;
}
// Check if file already open
if (_check_file_open(file))
{
_free_file(file);
return NULL;
}
// If file is in the root dir
if (file->path[0] == 0)
file->parentcluster = fatfs_get_root_cluster(&_fs);
else
{
// Find parent directory start cluster
if (!_open_directory(file->path, &file->parentcluster))
{
_free_file(file);
return NULL;
}
}
// Check if same filename exists in directory
if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1)
{
_free_file(file);
return NULL;
}
file->startcluster = 0;
// Create the file space for the file (at least one clusters worth!)
if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1))
{
_free_file(file);
return NULL;
}
#if FATFS_INC_LFN_SUPPORT
// Generate a short filename & tail
tailNum = 0;
do
{
// Create a standard short filename (without tail)
fatfs_lfn_create_sfn(shortFilename, file->filename);
// If second hit or more, generate a ~n tail
if (tailNum != 0)
fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
// Try with no tail if first entry
else
memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
// Check if entry exists already or not
if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0)
break;
tailNum++;
}
while (tailNum < 9999);
// We reached the max number of duplicate short file names (unlikely!)
if (tailNum == 9999)
{
// Delete allocated space
fatfs_free_cluster_chain(&_fs, file->startcluster);
_free_file(file);
return NULL;
}
#else
// Create a standard short filename (without tail)
if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
{
// Delete allocated space
fatfs_free_cluster_chain(&_fs, file->startcluster);
_free_file(file);
return NULL;
}
// Copy to SFN space
memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
// Check if entry exists already
if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename))
{
// Delete allocated space
fatfs_free_cluster_chain(&_fs, file->startcluster);
_free_file(file);
return NULL;
}
#endif
// Add file to disk
if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 0))
{
// Delete allocated space
fatfs_free_cluster_chain(&_fs, file->startcluster);
_free_file(file);
return NULL;
}
// General
file->filelength = 0;
file->bytenum = 0;
file->file_data_address = 0xFFFFFFFF;
file->file_data_dirty = 0;
file->filelength_changed = 0;
// Quick lookup for next link in the chain
file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
fatfs_cache_init(&_fs, file);
fatfs_fat_purge(&_fs);
return file;
}
#endif
//-----------------------------------------------------------------------------
// _read_sectors: Read sector(s) from disk to file
//-----------------------------------------------------------------------------
static uint32 _read_sectors(FL_FILE* file, uint32 offset, uint8 *buffer, uint32 count)
{
uint32 Sector = 0;
uint32 ClusterIdx = 0;
uint32 Cluster = 0;
uint32 i;
uint32 lba;
// Find cluster index within file & sector with cluster
ClusterIdx = offset / _fs.sectors_per_cluster;
Sector = offset - (ClusterIdx * _fs.sectors_per_cluster);
// Limit number of sectors read to the number remaining in this cluster
if ((Sector + count) > _fs.sectors_per_cluster)
count = _fs.sectors_per_cluster - Sector;
// Quick lookup for next link in the chain
if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
Cluster = file->last_fat_lookup.CurrentCluster;
// Else walk the chain
else
{
// Starting from last recorded cluster?
if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
{
i = file->last_fat_lookup.ClusterIdx;
Cluster = file->last_fat_lookup.CurrentCluster;
}
// Start searching from the beginning..
else
{
// Set start of cluster chain to initial value
i = 0;
Cluster = file->startcluster;
}
// Follow chain to find cluster to read
for ( ;i<ClusterIdx; i++)
{
uint32 nextCluster;
// Does the entry exist in the cache?
if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))
{
// Scan file linked list to find next entry
nextCluster = fatfs_find_next_cluster(&_fs, Cluster);
// Push entry into cache
fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
}
Cluster = nextCluster;
}
// Record current cluster lookup details (if valid)
if (Cluster != FAT32_LAST_CLUSTER)
{
file->last_fat_lookup.CurrentCluster = Cluster;
file->last_fat_lookup.ClusterIdx = ClusterIdx;
}
}
// If end of cluster chain then return false
if (Cluster == FAT32_LAST_CLUSTER)
return 0;
// Calculate sector address
lba = fatfs_lba_of_cluster(&_fs, Cluster) + Sector;
// Read sector of file
if (fatfs_sector_read(&_fs, lba, buffer, count))
return count;
else
return 0;
}
//-----------------------------------------------------------------------------
// External API
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// fl_init: Initialise library
//-----------------------------------------------------------------------------
void fl_init(void)
{
int i;
fat_list_init(&_free_file_list);
fat_list_init(&_open_file_list);
// Add all file objects to free list
for (i=0;i<FATFS_MAX_OPEN_FILES;i++)
fat_list_insert_last(&_free_file_list, &_files[i].list_node);
_filelib_init = 1;
}
//-----------------------------------------------------------------------------
// fl_attach_locks:
//-----------------------------------------------------------------------------
void fl_attach_locks(void (*lock)(void), void (*unlock)(void))
{
_fs.fl_lock = lock;
_fs.fl_unlock = unlock;
}
//-----------------------------------------------------------------------------
// fl_attach_media:
//-----------------------------------------------------------------------------
int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)
{
int res;
// If first call to library, initialise
CHECK_FL_INIT();
_fs.disk_io.read_media = rd;
_fs.disk_io.write_media = wr;
// Initialise FAT parameters
if ((res = fatfs_init(&_fs)) != FAT_INIT_OK)
{
FAT_PRINTF(("FAT_FS: Error could not load FAT details (%d)!\r\n", res));
return res;
}
_filelib_valid = 1;
return FAT_INIT_OK;
}
//-----------------------------------------------------------------------------
// fl_shutdown: Call before shutting down system
//-----------------------------------------------------------------------------
void fl_shutdown(void)
{
// If first call to library, initialise
CHECK_FL_INIT();
FL_LOCK(&_fs);
fatfs_fat_purge(&_fs);
FL_UNLOCK(&_fs);
}
//-----------------------------------------------------------------------------
// fopen: Open or Create a file for reading or writing
//-----------------------------------------------------------------------------
void* fl_fopen(const char *path, const char *mode)
{
int i;
FL_FILE* file;
uint8 flags = 0;
// If first call to library, initialise
CHECK_FL_INIT();
if (!_filelib_valid)
return NULL;
if (!path || !mode)
return NULL;
// Supported Modes:
// "r" Open a file for reading.
// The file must exist.
// "w" Create an empty file for writing.
// If a file with the same name already exists its content is erased and the file is treated as a new empty file.
// "a" Append to a file.
// Writing operations append data at the end of the file.
// The file is created if it does not exist.
// "r+" Open a file for update both reading and writing.
// The file must exist.
// "w+" Create an empty file for both reading and writing.
// If a file with the same name already exists its content is erased and the file is treated as a new empty file.
// "a+" Open a file for reading and appending.
// All writing operations are performed at the end of the file, protecting the previous content to be overwritten.
// You can reposition (fseek, rewind) the internal pointer to anywhere in the file for reading, but writing operations
// will move it back to the end of file.
// The file is created if it does not exist.
for (i=0;i<(int)strlen(mode);i++)
{
switch (mode[i])
{
case 'r':
case 'R':
flags |= FILE_READ;
break;
case 'w':
case 'W':
flags |= FILE_WRITE;
flags |= FILE_ERASE;
flags |= FILE_CREATE;
break;
case 'a':
case 'A':
flags |= FILE_WRITE;
flags |= FILE_APPEND;
flags |= FILE_CREATE;
break;
case '+':
if (flags & FILE_READ)
flags |= FILE_WRITE;
else if (flags & FILE_WRITE)
{
flags |= FILE_READ;
flags |= FILE_ERASE;
flags |= FILE_CREATE;
}
else if (flags & FILE_APPEND)
{
flags |= FILE_READ;
flags |= FILE_WRITE;
flags |= FILE_APPEND;
flags |= FILE_CREATE;
}
break;
case 'b':
case 'B':
flags |= FILE_BINARY;
break;
}
}
file = NULL;
#if FATFS_INC_WRITE_SUPPORT == 0
// No write support!
flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
#endif
// No write access - remove write/modify flags
if (!_fs.disk_io.write_media)
flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
FL_LOCK(&_fs);
// Read
if (flags & FILE_READ)
file = _open_file(path);
// Create New
#if FATFS_INC_WRITE_SUPPORT
if (!file && (flags & FILE_CREATE))
file = _create_file(path);
#endif
// Write Existing (and not open due to read or create)
if (!(flags & FILE_READ))
if ((flags & FILE_CREATE) && !file)
if (flags & (FILE_WRITE | FILE_APPEND))
file = _open_file(path);
if (file)
file->flags = flags;
FL_UNLOCK(&_fs);
return file;
}
//-----------------------------------------------------------------------------
// _write_sectors: Write sector(s) to disk
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
static uint32 _write_sectors(FL_FILE* file, uint32 offset, uint8 *buf, uint32 count)
{
uint32 SectorNumber = 0;
uint32 ClusterIdx = 0;
uint32 Cluster = 0;
uint32 LastCluster = FAT32_LAST_CLUSTER;
uint32 i;
uint32 lba;
uint32 TotalWriteCount = count;
// Find values for Cluster index & sector within cluster
ClusterIdx = offset / _fs.sectors_per_cluster;
SectorNumber = offset - (ClusterIdx * _fs.sectors_per_cluster);
// Limit number of sectors written to the number remaining in this cluster
if ((SectorNumber + count) > _fs.sectors_per_cluster)
count = _fs.sectors_per_cluster - SectorNumber;
// Quick lookup for next link in the chain
if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
Cluster = file->last_fat_lookup.CurrentCluster;
// Else walk the chain
else
{
// Starting from last recorded cluster?
if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
{
i = file->last_fat_lookup.ClusterIdx;
Cluster = file->last_fat_lookup.CurrentCluster;
}
// Start searching from the beginning..
else
{
// Set start of cluster chain to initial value
i = 0;
Cluster = file->startcluster;
}
// Follow chain to find cluster to read
for ( ;i<ClusterIdx; i++)
{
uint32 nextCluster;
// Does the entry exist in the cache?
if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))
{
// Scan file linked list to find next entry
nextCluster = fatfs_find_next_cluster(&_fs, Cluster);
// Push entry into cache
fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
}
LastCluster = Cluster;
Cluster = nextCluster;
// Dont keep following a dead end
if (Cluster == FAT32_LAST_CLUSTER)
break;
}
// If we have reached the end of the chain, allocate more!
if (Cluster == FAT32_LAST_CLUSTER)
{
// Add some more cluster(s) to the last good cluster chain
if (!fatfs_add_free_space(&_fs, &LastCluster, (TotalWriteCount + _fs.sectors_per_cluster -1) / _fs.sectors_per_cluster))
return 0;
Cluster = LastCluster;
}
// Record current cluster lookup details
file->last_fat_lookup.CurrentCluster = Cluster;
file->last_fat_lookup.ClusterIdx = ClusterIdx;
}
// Calculate write address
lba = fatfs_lba_of_cluster(&_fs, Cluster) + SectorNumber;
if (fatfs_sector_write(&_fs, lba, buf, count))
return count;
else
return 0;
}
#endif
//-----------------------------------------------------------------------------
// fl_fflush: Flush un-written data to the file
//-----------------------------------------------------------------------------
int fl_fflush(void *f)
{
#if FATFS_INC_WRITE_SUPPORT
FL_FILE *file = (FL_FILE *)f;
// If first call to library, initialise
CHECK_FL_INIT();
if (file)
{
FL_LOCK(&_fs);
// If some write data still in buffer
if (file->file_data_dirty)
{
// Write back current sector before loading next
if (_write_sectors(file, file->file_data_address, file->file_data_sector, 1))
file->file_data_dirty = 0;
}
FL_UNLOCK(&_fs);
}
#endif
return 0;
}
//-----------------------------------------------------------------------------
// fl_fclose: Close an open file
//-----------------------------------------------------------------------------
void fl_fclose(void *f)
{
FL_FILE *file = (FL_FILE *)f;
// If first call to library, initialise
CHECK_FL_INIT();
if (file)
{
FL_LOCK(&_fs);
// Flush un-written data to file
fl_fflush(f);
// File size changed?
if (file->filelength_changed)
{
#if FATFS_INC_WRITE_SUPPORT
// Update filesize in directory
fatfs_update_file_length(&_fs, file->parentcluster, (char*)file->shortfilename, file->filelength);
#endif
file->filelength_changed = 0;
}
file->bytenum = 0;
file->filelength = 0;
file->startcluster = 0;
file->file_data_address = 0xFFFFFFFF;
file->file_data_dirty = 0;
file->filelength_changed = 0;
// Free file handle
_free_file(file);
fatfs_fat_purge(&_fs);
FL_UNLOCK(&_fs);
}
}
//-----------------------------------------------------------------------------
// fl_fgetc: Get a character in the stream
//-----------------------------------------------------------------------------
int fl_fgetc(void *f)
{
int res;
uint8 data = 0;
res = fl_fread(&data, 1, 1, f);
if (res == 1)
return (int)data;
else
return res;
}
//-----------------------------------------------------------------------------
// fl_fgets: Get a string from a stream
//-----------------------------------------------------------------------------
char *fl_fgets(char *s, int n, void *f)
{
int idx = 0;
// Space for null terminator?
if (n > 0)
{
// While space (+space for null terminator)
while (idx < (n-1))
{
int ch = fl_fgetc(f);
// EOF / Error?
if (ch < 0)
break;
// Store character read from stream
s[idx++] = (char)ch;
// End of line?
if (ch == '\n')
break;
}
if (idx > 0)
s[idx] = '\0';
}
return (idx > 0) ? s : 0;
}
//-----------------------------------------------------------------------------
// fl_fread: Read a block of data from the file
//-----------------------------------------------------------------------------
int fl_fread(void * buffer, int size, int length, void *f )
{
uint32 sector;
uint32 offset;
int copyCount;
int count = size * length;
int bytesRead = 0;
FL_FILE *file = (FL_FILE *)f;
// If first call to library, initialise
CHECK_FL_INIT();
if (buffer==NULL || file==NULL)
return -1;
// No read permissions
if (!(file->flags & FILE_READ))
return -1;
// Nothing to be done
if (!count)
return 0;
// Check if read starts past end of file
if (file->bytenum >= file->filelength)
return -1;
// Limit to file size
if ( (file->bytenum + count) > file->filelength )
count = file->filelength - file->bytenum;
// Calculate start sector
sector = file->bytenum / FAT_SECTOR_SIZE;
// Offset to start copying data from first sector
offset = file->bytenum % FAT_SECTOR_SIZE;
while (bytesRead < count)
{
// Read whole sector, read from media directly into target buffer
if ((offset == 0) && ((count - bytesRead) >= FAT_SECTOR_SIZE))
{
// Read as many sectors as possible into target buffer
uint32 sectorsRead = _read_sectors(file, sector, (uint8*)((uint8*)buffer + bytesRead), (count - bytesRead) / FAT_SECTOR_SIZE);
if (sectorsRead)
{
// We have upto one sector to copy
copyCount = FAT_SECTOR_SIZE * sectorsRead;
// Move onto next sector and reset copy offset
sector+= sectorsRead;
offset = 0;
}
else
break;
}
else
{
// Do we need to re-read the sector?
if (file->file_data_address != sector)
{
// Flush un-written data to file
if (file->file_data_dirty)
fl_fflush(file);
// Get LBA of sector offset within file
if (!_read_sectors(file, sector, file->file_data_sector, 1))
// Read failed - out of range (probably)
break;
file->file_data_address = sector;
file->file_data_dirty = 0;
}
// We have upto one sector to copy
copyCount = FAT_SECTOR_SIZE - offset;
// Only require some of this sector?
if (copyCount > (count - bytesRead))
copyCount = (count - bytesRead);
// Copy to application buffer
memcpy( (uint8*)((uint8*)buffer + bytesRead), (uint8*)(file->file_data_sector + offset), copyCount);
// Move onto next sector and reset copy offset
sector++;
offset = 0;
}
// Increase total read count
bytesRead += copyCount;
// Increment file pointer
file->bytenum += copyCount;
}
return bytesRead;
}
//-----------------------------------------------------------------------------
// fl_fseek: Seek to a specific place in the file
//-----------------------------------------------------------------------------
int fl_fseek( void *f, long offset, int origin )
{
FL_FILE *file = (FL_FILE *)f;
int res = -1;
// If first call to library, initialise
CHECK_FL_INIT();
if (!file)
return -1;
if (origin == SEEK_END && offset != 0)
return -1;
FL_LOCK(&_fs);
// Invalidate file buffer
file->file_data_address = 0xFFFFFFFF;
file->file_data_dirty = 0;
if (origin == SEEK_SET)
{
file->bytenum = (uint32)offset;
if (file->bytenum > file->filelength)
file->bytenum = file->filelength;
res = 0;
}
else if (origin == SEEK_CUR)
{
// Positive shift
if (offset >= 0)
{
file->bytenum += offset;
if (file->bytenum > file->filelength)
file->bytenum = file->filelength;
}
// Negative shift
else
{
// Make shift positive
offset = -offset;
// Limit to negative shift to start of file
if ((uint32)offset > file->bytenum)
file->bytenum = 0;
else
file->bytenum-= offset;
}
res = 0;
}
else if (origin == SEEK_END)
{
file->bytenum = file->filelength;
res = 0;
}
else
res = -1;
FL_UNLOCK(&_fs);
return res;
}
//-----------------------------------------------------------------------------
// fl_fgetpos: Get the current file position
//-----------------------------------------------------------------------------
int fl_fgetpos(void *f , uint32 * position)
{
FL_FILE *file = (FL_FILE *)f;
if (!file)
return -1;
FL_LOCK(&_fs);
// Get position
*position = file->bytenum;
FL_UNLOCK(&_fs);
return 0;
}
//-----------------------------------------------------------------------------
// fl_ftell: Get the current file position
//-----------------------------------------------------------------------------
long fl_ftell(void *f)
{
uint32 pos = 0;
fl_fgetpos(f, &pos);
return (long)pos;
}
//-----------------------------------------------------------------------------
// fl_feof: Is the file pointer at the end of the stream?
//-----------------------------------------------------------------------------
int fl_feof(void *f)
{
FL_FILE *file = (FL_FILE *)f;
int res;
if (!file)
return -1;
FL_LOCK(&_fs);
if (file->bytenum == file->filelength)
res = EOF;
else
res = 0;
FL_UNLOCK(&_fs);
return res;
}
//-----------------------------------------------------------------------------
// fl_fputc: Write a character to the stream
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fl_fputc(int c, void *f)
{
uint8 data = (uint8)c;
int res;
res = fl_fwrite(&data, 1, 1, f);
if (res == 1)
return c;
else
return res;
}
#endif
//-----------------------------------------------------------------------------
// fl_fwrite: Write a block of data to the stream
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fl_fwrite(const void * data, int size, int count, void *f )
{
FL_FILE *file = (FL_FILE *)f;
uint32 sector;
uint32 offset;
uint32 length = (size*count);
uint8 *buffer = (uint8 *)data;
uint32 bytesWritten = 0;
uint32 copyCount;
// If first call to library, initialise
CHECK_FL_INIT();
if (!file)
return -1;
FL_LOCK(&_fs);
// No write permissions
if (!(file->flags & FILE_WRITE))
{
FL_UNLOCK(&_fs);
return -1;
}
// Append writes to end of file
if (file->flags & FILE_APPEND)
file->bytenum = file->filelength;
// Else write to current position
// Calculate start sector
sector = file->bytenum / FAT_SECTOR_SIZE;
// Offset to start copying data from first sector
offset = file->bytenum % FAT_SECTOR_SIZE;
while (bytesWritten < length)
{
// Whole sector or more to be written?
if ((offset == 0) && ((length - bytesWritten) >= FAT_SECTOR_SIZE))
{
uint32 sectorsWrote;
// Buffered sector, flush back to disk
if (file->file_data_address != 0xFFFFFFFF)
{
// Flush un-written data to file
if (file->file_data_dirty)
fl_fflush(file);
file->file_data_address = 0xFFFFFFFF;
file->file_data_dirty = 0;
}
// Write as many sectors as possible
sectorsWrote = _write_sectors(file, sector, (uint8*)(buffer + bytesWritten), (length - bytesWritten) / FAT_SECTOR_SIZE);
copyCount = FAT_SECTOR_SIZE * sectorsWrote;
// Increase total read count
bytesWritten += copyCount;
// Increment file pointer
file->bytenum += copyCount;
// Move onto next sector and reset copy offset
sector+= sectorsWrote;
offset = 0;
if (!sectorsWrote)
break;
}
else
{
// We have upto one sector to copy
copyCount = FAT_SECTOR_SIZE - offset;
// Only require some of this sector?
if (copyCount > (length - bytesWritten))
copyCount = (length - bytesWritten);
// Do we need to read a new sector?
if (file->file_data_address != sector)
{
// Flush un-written data to file
if (file->file_data_dirty)
fl_fflush(file);
// If we plan to overwrite the whole sector, we don't need to read it first!
if (copyCount != FAT_SECTOR_SIZE)
{
// NOTE: This does not have succeed; if last sector of file
// reached, no valid data will be read in, but write will
// allocate some more space for new data.
// Get LBA of sector offset within file
if (!_read_sectors(file, sector, file->file_data_sector, 1))
memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
}
file->file_data_address = sector;
file->file_data_dirty = 0;
}
// Copy from application buffer into sector buffer
memcpy((uint8*)(file->file_data_sector + offset), (uint8*)(buffer + bytesWritten), copyCount);
// Mark buffer as dirty
file->file_data_dirty = 1;
// Increase total read count
bytesWritten += copyCount;
// Increment file pointer
file->bytenum += copyCount;
// Move onto next sector and reset copy offset
sector++;
offset = 0;
}
}
// Write increased extent of the file?
if (file->bytenum > file->filelength)
{
// Increase file size to new point
file->filelength = file->bytenum;
// We are changing the file length and this
// will need to be writen back at some point
file->filelength_changed = 1;
}
#if FATFS_INC_TIME_DATE_SUPPORT
// If time & date support is enabled, always force directory entry to be
// written in-order to update file modify / access time & date.
file->filelength_changed = 1;
#endif
FL_UNLOCK(&_fs);
return (size*count);
}
#endif
//-----------------------------------------------------------------------------
// fl_fputs: Write a character string to the stream
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fl_fputs(const char * str, void *f)
{
int len = (int)strlen(str);
int res = fl_fwrite(str, 1, len, f);
if (res == len)
return len;
else
return res;
}
#endif
//-----------------------------------------------------------------------------
// fl_remove: Remove a file from the filesystem
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fl_remove( const char * filename )
{
FL_FILE* file;
int res = -1;
FL_LOCK(&_fs);
// Use read_file as this will check if the file is already open!
file = fl_fopen((char*)filename, "r");
if (file)
{
// Delete allocated space
if (fatfs_free_cluster_chain(&_fs, file->startcluster))
{
// Remove directory entries
if (fatfs_mark_file_deleted(&_fs, file->parentcluster, (char*)file->shortfilename))
{
// Close the file handle (this should not write anything to the file
// as we have not changed the file since opening it!)
fl_fclose(file);
res = 0;
}
}
}
FL_UNLOCK(&_fs);
return res;
}
#endif
//-----------------------------------------------------------------------------
// fl_createdirectory: Create a directory based on a path
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fl_createdirectory(const char *path)
{
int res;
// If first call to library, initialise
CHECK_FL_INIT();
FL_LOCK(&_fs);
res =_create_directory((char*)path);
FL_UNLOCK(&_fs);
return res;
}
#endif
//-----------------------------------------------------------------------------
// fl_listdirectory: List a directory based on a path
//-----------------------------------------------------------------------------
#if FATFS_DIR_LIST_SUPPORT
void fl_listdirectory(const char *path)
{
FL_DIR dirstat;
// If first call to library, initialise
CHECK_FL_INIT();
FL_LOCK(&_fs);
FAT_PRINTF(("\r\nDirectory %s\r\n", path));
if (fl_opendir(path, &dirstat))
{
struct fs_dir_ent dirent;
while (fl_readdir(&dirstat, &dirent) == 0)
{
#if FATFS_INC_TIME_DATE_SUPPORT
int d,m,y,h,mn,s;
fatfs_convert_from_fat_time(dirent.write_time, &h,&m,&s);
fatfs_convert_from_fat_date(dirent.write_date, &d,&mn,&y);
FAT_PRINTF(("%02d/%02d/%04d %02d:%02d ", d,mn,y,h,m));
#endif
if (dirent.is_dir)
{
FAT_PRINTF(("%s <DIR>\r\n", dirent.filename));
}
else
{
FAT_PRINTF(("%s [%d bytes]\r\n", dirent.filename, dirent.size));
}
}
fl_closedir(&dirstat);
}
FL_UNLOCK(&_fs);
}
#endif
//-----------------------------------------------------------------------------
// fl_opendir: Opens a directory for listing
//-----------------------------------------------------------------------------
#if FATFS_DIR_LIST_SUPPORT
FL_DIR* fl_opendir(const char* path, FL_DIR *dir)
{
int levels;
int res = 1;
uint32 cluster = FAT32_INVALID_CLUSTER;
// If first call to library, initialise
CHECK_FL_INIT();
FL_LOCK(&_fs);
levels = fatfs_total_path_levels((char*)path) + 1;
// If path is in the root dir
if (levels == 0)
cluster = fatfs_get_root_cluster(&_fs);
// Find parent directory start cluster
else
res = _open_directory((char*)path, &cluster);
if (res)
fatfs_list_directory_start(&_fs, dir, cluster);
FL_UNLOCK(&_fs);
return cluster != FAT32_INVALID_CLUSTER ? dir : 0;
}
#endif
//-----------------------------------------------------------------------------
// fl_readdir: Get next item in directory
//-----------------------------------------------------------------------------
#if FATFS_DIR_LIST_SUPPORT
int fl_readdir(FL_DIR *dirls, fl_dirent *entry)
{
int res = 0;
// If first call to library, initialise
CHECK_FL_INIT();
FL_LOCK(&_fs);
res = fatfs_list_directory_next(&_fs, dirls, entry);
FL_UNLOCK(&_fs);
return res ? 0 : -1;
}
#endif
//-----------------------------------------------------------------------------
// fl_closedir: Close directory after listing
//-----------------------------------------------------------------------------
#if FATFS_DIR_LIST_SUPPORT
int fl_closedir(FL_DIR* dir)
{
// Not used
return 0;
}
#endif
//-----------------------------------------------------------------------------
// fl_is_dir: Is this a directory?
//-----------------------------------------------------------------------------
#if FATFS_DIR_LIST_SUPPORT
int fl_is_dir(const char *path)
{
int res = 0;
FL_DIR dir;
if (fl_opendir(path, &dir))
{
res = 1;
fl_closedir(&dir);
}
return res;
}
#endif
//-----------------------------------------------------------------------------
// fl_format: Format a partition with either FAT16 or FAT32 based on size
//-----------------------------------------------------------------------------
#if FATFS_INC_FORMAT_SUPPORT
int fl_format(uint32 volume_sectors, const char *name)
{
return fatfs_format(&_fs, volume_sectors, name);
}
#endif /*FATFS_INC_FORMAT_SUPPORT*/
//-----------------------------------------------------------------------------
// fl_get_fs:
//-----------------------------------------------------------------------------
#ifdef FATFS_INC_TEST_HOOKS
struct fatfs* fl_get_fs(void)
{
return &_fs;
}
#endif
#ifndef __FAT_FILELIB_H__
#define __FAT_FILELIB_H__
#include "fat_opts.h"
#include "fat_access.h"
#include "fat_list.h"
//-----------------------------------------------------------------------------
// Defines
//-----------------------------------------------------------------------------
#ifndef SEEK_CUR
#define SEEK_CUR 1
#endif
#ifndef SEEK_END
#define SEEK_END 2
#endif
#ifndef SEEK_SET
#define SEEK_SET 0
#endif
#ifndef EOF
#define EOF (-1)
#endif
//-----------------------------------------------------------------------------
// Structures
//-----------------------------------------------------------------------------
struct sFL_FILE;
struct cluster_lookup
{
uint32 ClusterIdx;
uint32 CurrentCluster;
};
typedef struct sFL_FILE
{
uint32 parentcluster;
uint32 startcluster;
uint32 bytenum;
uint32 filelength;
int filelength_changed;
char path[FATFS_MAX_LONG_FILENAME];
char filename[FATFS_MAX_LONG_FILENAME];
uint8 shortfilename[11];
#ifdef FAT_CLUSTER_CACHE_ENTRIES
uint32 cluster_cache_idx[FAT_CLUSTER_CACHE_ENTRIES];
uint32 cluster_cache_data[FAT_CLUSTER_CACHE_ENTRIES];
#endif
// Cluster Lookup
struct cluster_lookup last_fat_lookup;
// Read/Write sector buffer
uint8 file_data_sector[FAT_SECTOR_SIZE];
uint32 file_data_address;
int file_data_dirty;
// File fopen flags
uint8 flags;
#define FILE_READ (1 << 0)
#define FILE_WRITE (1 << 1)
#define FILE_APPEND (1 << 2)
#define FILE_BINARY (1 << 3)
#define FILE_ERASE (1 << 4)
#define FILE_CREATE (1 << 5)
struct fat_node list_node;
} FL_FILE;
//-----------------------------------------------------------------------------
// Prototypes
//-----------------------------------------------------------------------------
// External
void fl_init(void);
void fl_attach_locks(void (*lock)(void), void (*unlock)(void));
int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr);
void fl_shutdown(void);
// Standard API
void* fl_fopen(const char *path, const char *modifiers);
void fl_fclose(void *file);
int fl_fflush(void *file);
int fl_fgetc(void *file);
char * fl_fgets(char *s, int n, void *f);
int fl_fputc(int c, void *file);
int fl_fputs(const char * str, void *file);
int fl_fwrite(const void * data, int size, int count, void *file );
int fl_fread(void * data, int size, int count, void *file );
int fl_fseek(void *file , long offset , int origin );
int fl_fgetpos(void *file , uint32 * position);
long fl_ftell(void *f);
int fl_feof(void *f);
int fl_remove(const char * filename);
// Equivelant dirent.h
typedef struct fs_dir_list_status FL_DIR;
typedef struct fs_dir_ent fl_dirent;
FL_DIR* fl_opendir(const char* path, FL_DIR *dir);
int fl_readdir(FL_DIR *dirls, fl_dirent *entry);
int fl_closedir(FL_DIR* dir);
// Extensions
void fl_listdirectory(const char *path);
int fl_createdirectory(const char *path);
int fl_is_dir(const char *path);
int fl_format(uint32 volume_sectors, const char *name);
// Test hooks
#ifdef FATFS_INC_TEST_HOOKS
struct fatfs* fl_get_fs(void);
#endif
//-----------------------------------------------------------------------------
// Stdio file I/O names
//-----------------------------------------------------------------------------
#ifdef USE_FILELIB_STDIO_COMPAT_NAMES
#define FILE FL_FILE
#define fopen(a,b) fl_fopen(a, b)
#define fclose(a) fl_fclose(a)
#define fflush(a) fl_fflush(a)
#define fgetc(a) fl_fgetc(a)
#define fgets(a,b,c) fl_fgets(a, b, c)
#define fputc(a,b) fl_fputc(a, b)
#define fputs(a,b) fl_fputs(a, b)
#define fwrite(a,b,c,d) fl_fwrite(a, b, c, d)
#define fread(a,b,c,d) fl_fread(a, b, c, d)
#define fseek(a,b,c) fl_fseek(a, b, c)
#define fgetpos(a,b) fl_fgetpos(a, b)
#define ftell(a) fl_ftell(a)
#define feof(a) fl_feof(a)
#define remove(a) fl_remove(a)
#define mkdir(a) fl_createdirectory(a)
#define rmdir(a) 0
#endif
#endif
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// FAT16/32 File IO Library
// V2.6
// Ultra-Embedded.com
// Copyright 2003 - 2012
//
// Email: admin@ultra-embedded.com
//
// License: GPL
// If you would like a version with a more permissive license for use in
// closed source commercial applications please contact me for details.
//-----------------------------------------------------------------------------
//
// This file is part of FAT File IO Library.
//
// FAT File IO Library is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// FAT File IO Library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with FAT File IO Library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#include <string.h>
#include "fat_defs.h"
#include "fat_access.h"
#include "fat_table.h"
#include "fat_write.h"
#include "fat_string.h"
#include "fat_misc.h"
#include "fat_format.h"
#if FATFS_INC_FORMAT_SUPPORT
//-----------------------------------------------------------------------------
// Tables
//-----------------------------------------------------------------------------
struct sec_per_clus_table
{
uint32 sectors;
uint8 sectors_per_cluster;
};
struct sec_per_clus_table _cluster_size_table16[] =
{
{ 32680, 2}, // 16MB - 1K
{ 262144, 4}, // 128MB - 2K
{ 524288, 8}, // 256MB - 4K
{ 1048576, 16}, // 512MB - 8K
{ 2097152, 32}, // 1GB - 16K
{ 4194304, 64}, // 2GB - 32K
{ 8388608, 128},// 2GB - 64K [Warning only supported by Windows XP onwards]
{ 0 , 0 } // Invalid
};
struct sec_per_clus_table _cluster_size_table32[] =
{
{ 532480, 1}, // 260MB - 512b
{ 16777216, 8}, // 8GB - 4K
{ 33554432, 16}, // 16GB - 8K
{ 67108864, 32}, // 32GB - 16K
{ 0xFFFFFFFF, 64},// >32GB - 32K
{ 0 , 0 } // Invalid
};
//-----------------------------------------------------------------------------
// fatfs_calc_cluster_size: Calculate what cluster size should be used
//-----------------------------------------------------------------------------
static uint8 fatfs_calc_cluster_size(uint32 sectors, int is_fat32)
{
int i;
if (!is_fat32)
{
for (i=0; _cluster_size_table16[i].sectors_per_cluster != 0;i++)
if (sectors <= _cluster_size_table16[i].sectors)
return _cluster_size_table16[i].sectors_per_cluster;
}
else
{
for (i=0; _cluster_size_table32[i].sectors_per_cluster != 0;i++)
if (sectors <= _cluster_size_table32[i].sectors)
return _cluster_size_table32[i].sectors_per_cluster;
}
return 0;
}
//-----------------------------------------------------------------------------
// fatfs_erase_sectors: Erase a number of sectors
//-----------------------------------------------------------------------------
static int fatfs_erase_sectors(struct fatfs *fs, uint32 lba, int count)
{
int i;
// Zero sector first
memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
for (i=0;i<count;i++)
if (!fs->disk_io.write_media(lba + i, fs->currentsector.sector, 1))
return 0;
return 1;
}
//-----------------------------------------------------------------------------
// fatfs_create_boot_sector: Create the boot sector
//-----------------------------------------------------------------------------
static int fatfs_create_boot_sector(struct fatfs *fs, uint32 boot_sector_lba, uint32 vol_sectors, const char *name, int is_fat32)
{
uint32 total_clusters;
int i;
// Zero sector initially
memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
// OEM Name & Jump Code
fs->currentsector.sector[0] = 0xEB;
fs->currentsector.sector[1] = 0x3C;
fs->currentsector.sector[2] = 0x90;
fs->currentsector.sector[3] = 0x4D;
fs->currentsector.sector[4] = 0x53;
fs->currentsector.sector[5] = 0x44;
fs->currentsector.sector[6] = 0x4F;
fs->currentsector.sector[7] = 0x53;
fs->currentsector.sector[8] = 0x35;
fs->currentsector.sector[9] = 0x2E;
fs->currentsector.sector[10] = 0x30;
// Bytes per sector
fs->currentsector.sector[11] = (FAT_SECTOR_SIZE >> 0) & 0xFF;
fs->currentsector.sector[12] = (FAT_SECTOR_SIZE >> 8) & 0xFF;
// Get sectors per cluster size for the disk
fs->sectors_per_cluster = fatfs_calc_cluster_size(vol_sectors, is_fat32);
if (!fs->sectors_per_cluster)
return 0; // Invalid disk size
// Sectors per cluster
fs->currentsector.sector[13] = fs->sectors_per_cluster;
// Reserved Sectors
if (!is_fat32)
fs->reserved_sectors = 8;
else
fs->reserved_sectors = 32;
fs->currentsector.sector[14] = (fs->reserved_sectors >> 0) & 0xFF;
fs->currentsector.sector[15] = (fs->reserved_sectors >> 8) & 0xFF;
// Number of FATS
fs->num_of_fats = 2;
fs->currentsector.sector[16] = fs->num_of_fats;
// Max entries in root dir (FAT16 only)
if (!is_fat32)
{
fs->root_entry_count = 512;
fs->currentsector.sector[17] = (fs->root_entry_count >> 0) & 0xFF;
fs->currentsector.sector[18] = (fs->root_entry_count >> 8) & 0xFF;
}
else
{
fs->root_entry_count = 0;
fs->currentsector.sector[17] = 0;
fs->currentsector.sector[18] = 0;
}
// [FAT16] Total sectors (use FAT32 count instead)
fs->currentsector.sector[19] = 0x00;
fs->currentsector.sector[20] = 0x00;
// Media type
fs->currentsector.sector[21] = 0xF8;
// FAT16 BS Details
if (!is_fat32)
{
// Count of sectors used by the FAT table (FAT16 only)
total_clusters = (vol_sectors / fs->sectors_per_cluster) + 1;
fs->fat_sectors = (total_clusters/(FAT_SECTOR_SIZE/2)) + 1;
fs->currentsector.sector[22] = (uint8)((fs->fat_sectors >> 0) & 0xFF);
fs->currentsector.sector[23] = (uint8)((fs->fat_sectors >> 8) & 0xFF);
// Sectors per track
fs->currentsector.sector[24] = 0x00;
fs->currentsector.sector[25] = 0x00;
// Heads
fs->currentsector.sector[26] = 0x00;
fs->currentsector.sector[27] = 0x00;
// Hidden sectors
fs->currentsector.sector[28] = 0x20;
fs->currentsector.sector[29] = 0x00;
fs->currentsector.sector[30] = 0x00;
fs->currentsector.sector[31] = 0x00;
// Total sectors for this volume
fs->currentsector.sector[32] = (uint8)((vol_sectors>>0)&0xFF);
fs->currentsector.sector[33] = (uint8)((vol_sectors>>8)&0xFF);
fs->currentsector.sector[34] = (uint8)((vol_sectors>>16)&0xFF);
fs->currentsector.sector[35] = (uint8)((vol_sectors>>24)&0xFF);
// Drive number
fs->currentsector.sector[36] = 0x00;
// Reserved
fs->currentsector.sector[37] = 0x00;
// Boot signature
fs->currentsector.sector[38] = 0x29;
// Volume ID
fs->currentsector.sector[39] = 0x12;
fs->currentsector.sector[40] = 0x34;
fs->currentsector.sector[41] = 0x56;
fs->currentsector.sector[42] = 0x78;
// Volume name
for (i=0;i<11;i++)
{
if (i < (int)strlen(name))
fs->currentsector.sector[i+43] = name[i];
else
fs->currentsector.sector[i+43] = ' ';
}
// File sys type
fs->currentsector.sector[54] = 'F';
fs->currentsector.sector[55] = 'A';
fs->currentsector.sector[56] = 'T';
fs->currentsector.sector[57] = '1';
fs->currentsector.sector[58] = '6';
fs->currentsector.sector[59] = ' ';
fs->currentsector.sector[60] = ' ';
fs->currentsector.sector[61] = ' ';
// Signature
fs->currentsector.sector[510] = 0x55;
fs->currentsector.sector[511] = 0xAA;
}
// FAT32 BS Details
else
{
// Count of sectors used by the FAT table (FAT16 only)
fs->currentsector.sector[22] = 0;
fs->currentsector.sector[23] = 0;
// Sectors per track (default)
fs->currentsector.sector[24] = 0x3F;
fs->currentsector.sector[25] = 0x00;
// Heads (default)
fs->currentsector.sector[26] = 0xFF;
fs->currentsector.sector[27] = 0x00;
// Hidden sectors
fs->currentsector.sector[28] = 0x00;
fs->currentsector.sector[29] = 0x00;
fs->currentsector.sector[30] = 0x00;
fs->currentsector.sector[31] = 0x00;
// Total sectors for this volume
fs->currentsector.sector[32] = (uint8)((vol_sectors>>0)&0xFF);
fs->currentsector.sector[33] = (uint8)((vol_sectors>>8)&0xFF);
fs->currentsector.sector[34] = (uint8)((vol_sectors>>16)&0xFF);
fs->currentsector.sector[35] = (uint8)((vol_sectors>>24)&0xFF);
total_clusters = (vol_sectors / fs->sectors_per_cluster) + 1;
fs->fat_sectors = (total_clusters/(FAT_SECTOR_SIZE/4)) + 1;
// BPB_FATSz32
fs->currentsector.sector[36] = (uint8)((fs->fat_sectors>>0)&0xFF);
fs->currentsector.sector[37] = (uint8)((fs->fat_sectors>>8)&0xFF);
fs->currentsector.sector[38] = (uint8)((fs->fat_sectors>>16)&0xFF);
fs->currentsector.sector[39] = (uint8)((fs->fat_sectors>>24)&0xFF);
// BPB_ExtFlags
fs->currentsector.sector[40] = 0;
fs->currentsector.sector[41] = 0;
// BPB_FSVer
fs->currentsector.sector[42] = 0;
fs->currentsector.sector[43] = 0;
// BPB_RootClus
fs->currentsector.sector[44] = (uint8)((fs->rootdir_first_cluster>>0)&0xFF);
fs->currentsector.sector[45] = (uint8)((fs->rootdir_first_cluster>>8)&0xFF);
fs->currentsector.sector[46] = (uint8)((fs->rootdir_first_cluster>>16)&0xFF);
fs->currentsector.sector[47] = (uint8)((fs->rootdir_first_cluster>>24)&0xFF);
// BPB_FSInfo
fs->currentsector.sector[48] = (uint8)((fs->fs_info_sector>>0)&0xFF);
fs->currentsector.sector[49] = (uint8)((fs->fs_info_sector>>8)&0xFF);
// BPB_BkBootSec
fs->currentsector.sector[50] = 6;
fs->currentsector.sector[51] = 0;
// Drive number
fs->currentsector.sector[64] = 0x00;
// Boot signature
fs->currentsector.sector[66] = 0x29;
// Volume ID
fs->currentsector.sector[67] = 0x12;
fs->currentsector.sector[68] = 0x34;
fs->currentsector.sector[69] = 0x56;
fs->currentsector.sector[70] = 0x78;
// Volume name
for (i=0;i<11;i++)
{
if (i < (int)strlen(name))
fs->currentsector.sector[i+71] = name[i];
else
fs->currentsector.sector[i+71] = ' ';
}
// File sys type
fs->currentsector.sector[82] = 'F';
fs->currentsector.sector[83] = 'A';
fs->currentsector.sector[84] = 'T';
fs->currentsector.sector[85] = '3';
fs->currentsector.sector[86] = '2';
fs->currentsector.sector[87] = ' ';
fs->currentsector.sector[88] = ' ';
fs->currentsector.sector[89] = ' ';
// Signature
fs->currentsector.sector[510] = 0x55;
fs->currentsector.sector[511] = 0xAA;
}
if (fs->disk_io.write_media(boot_sector_lba, fs->currentsector.sector, 1))
return 1;
else
return 0;
}
//-----------------------------------------------------------------------------
// fatfs_create_fsinfo_sector: Create the FSInfo sector (FAT32)
//-----------------------------------------------------------------------------
static int fatfs_create_fsinfo_sector(struct fatfs *fs, uint32 sector_lba)
{
// Zero sector initially
memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
// FSI_LeadSig
fs->currentsector.sector[0] = 0x52;
fs->currentsector.sector[1] = 0x52;
fs->currentsector.sector[2] = 0x61;
fs->currentsector.sector[3] = 0x41;
// FSI_StrucSig
fs->currentsector.sector[484] = 0x72;
fs->currentsector.sector[485] = 0x72;
fs->currentsector.sector[486] = 0x41;
fs->currentsector.sector[487] = 0x61;
// FSI_Free_Count
fs->currentsector.sector[488] = 0xFF;
fs->currentsector.sector[489] = 0xFF;
fs->currentsector.sector[490] = 0xFF;
fs->currentsector.sector[491] = 0xFF;
// FSI_Nxt_Free
fs->currentsector.sector[492] = 0xFF;
fs->currentsector.sector[493] = 0xFF;
fs->currentsector.sector[494] = 0xFF;
fs->currentsector.sector[495] = 0xFF;
// Signature
fs->currentsector.sector[510] = 0x55;
fs->currentsector.sector[511] = 0xAA;
if (fs->disk_io.write_media(sector_lba, fs->currentsector.sector, 1))
return 1;
else
return 0;
}
//-----------------------------------------------------------------------------
// fatfs_erase_fat: Erase FAT table using fs details in fs struct
//-----------------------------------------------------------------------------
static int fatfs_erase_fat(struct fatfs *fs, int is_fat32)
{
uint32 i;
// Zero sector initially
memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
// Initialise default allocate / reserved clusters
if (!is_fat32)
{
SET_16BIT_WORD(fs->currentsector.sector, 0, 0xFFF8);
SET_16BIT_WORD(fs->currentsector.sector, 2, 0xFFFF);
}
else
{
SET_32BIT_WORD(fs->currentsector.sector, 0, 0x0FFFFFF8);
SET_32BIT_WORD(fs->currentsector.sector, 4, 0xFFFFFFFF);
SET_32BIT_WORD(fs->currentsector.sector, 8, 0x0FFFFFFF);
}
if (!fs->disk_io.write_media(fs->fat_begin_lba + 0, fs->currentsector.sector, 1))
return 0;
// Zero remaining FAT sectors
memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
for (i=1;i<fs->fat_sectors*fs->num_of_fats;i++)
if (!fs->disk_io.write_media(fs->fat_begin_lba + i, fs->currentsector.sector, 1))
return 0;
return 1;
}
//-----------------------------------------------------------------------------
// fatfs_format_fat16: Format a FAT16 partition
//-----------------------------------------------------------------------------
int fatfs_format_fat16(struct fatfs *fs, uint32 volume_sectors, const char *name)
{
fs->currentsector.address = FAT32_INVALID_CLUSTER;
fs->currentsector.dirty = 0;
fs->next_free_cluster = 0; // Invalid
fatfs_fat_init(fs);
// Make sure we have read + write functions
if (!fs->disk_io.read_media || !fs->disk_io.write_media)
return FAT_INIT_MEDIA_ACCESS_ERROR;
// Volume is FAT16
fs->fat_type = FAT_TYPE_16;
// Not valid for FAT16
fs->fs_info_sector = 0;
fs->rootdir_first_cluster = 0;
// Sector 0: Boot sector
// NOTE: We don't need an MBR, it is a waste of a good sector!
fs->lba_begin = 0;
if (!fatfs_create_boot_sector(fs, fs->lba_begin, volume_sectors, name, 0))
return 0;
// For FAT16 (which this may be), rootdir_first_cluster is actuall rootdir_first_sector
fs->rootdir_first_sector = fs->reserved_sectors + (fs->num_of_fats * fs->fat_sectors);
fs->rootdir_sectors = ((fs->root_entry_count * 32) + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE;
// First FAT LBA address
fs->fat_begin_lba = fs->lba_begin + fs->reserved_sectors;
// The address of the first data cluster on this volume
fs->cluster_begin_lba = fs->fat_begin_lba + (fs->num_of_fats * fs->fat_sectors);
// Initialise FAT sectors
if (!fatfs_erase_fat(fs, 0))
return 0;
// Erase Root directory
if (!fatfs_erase_sectors(fs, fs->lba_begin + fs->rootdir_first_sector, fs->rootdir_sectors))
return 0;
return 1;
}
//-----------------------------------------------------------------------------
// fatfs_format_fat32: Format a FAT32 partition
//-----------------------------------------------------------------------------
int fatfs_format_fat32(struct fatfs *fs, uint32 volume_sectors, const char *name)
{
fs->currentsector.address = FAT32_INVALID_CLUSTER;
fs->currentsector.dirty = 0;
fs->next_free_cluster = 0; // Invalid
fatfs_fat_init(fs);
// Make sure we have read + write functions
if (!fs->disk_io.read_media || !fs->disk_io.write_media)
return FAT_INIT_MEDIA_ACCESS_ERROR;
// Volume is FAT32
fs->fat_type = FAT_TYPE_32;
// Basic defaults for normal FAT32 partitions
fs->fs_info_sector = 1;
fs->rootdir_first_cluster = 2;
// Sector 0: Boot sector
// NOTE: We don't need an MBR, it is a waste of a good sector!
fs->lba_begin = 0;
if (!fatfs_create_boot_sector(fs, fs->lba_begin, volume_sectors, name, 1))
return 0;
// First FAT LBA address
fs->fat_begin_lba = fs->lba_begin + fs->reserved_sectors;
// The address of the first data cluster on this volume
fs->cluster_begin_lba = fs->fat_begin_lba + (fs->num_of_fats * fs->fat_sectors);
// Initialise FSInfo sector
if (!fatfs_create_fsinfo_sector(fs, fs->fs_info_sector))
return 0;
// Initialise FAT sectors
if (!fatfs_erase_fat(fs, 1))
return 0;
// Erase Root directory
if (!fatfs_erase_sectors(fs, fatfs_lba_of_cluster(fs, fs->rootdir_first_cluster), fs->sectors_per_cluster))
return 0;
return 1;
}
//-----------------------------------------------------------------------------
// fatfs_format: Format a partition with either FAT16 or FAT32 based on size
//-----------------------------------------------------------------------------
int fatfs_format(struct fatfs *fs, uint32 volume_sectors, const char *name)
{
// 2GB - 32K limit for safe behaviour for FAT16
if (volume_sectors <= 4194304)
return fatfs_format_fat16(fs, volume_sectors, name);
else
return fatfs_format_fat32(fs, volume_sectors, name);
}
#endif /*FATFS_INC_FORMAT_SUPPORT*/
#ifndef __FAT_FORMAT_H__
#define __FAT_FORMAT_H__
#include "fat_defs.h"
#include "fat_opts.h"
#include "fat_access.h"
//-----------------------------------------------------------------------------
// Prototypes
//-----------------------------------------------------------------------------
int fatfs_format(struct fatfs *fs, uint32 volume_sectors, const char *name);
int fatfs_format_fat16(struct fatfs *fs, uint32 volume_sectors, const char *name);
int fatfs_format_fat32(struct fatfs *fs, uint32 volume_sectors, const char *name);
#endif
#ifndef __FAT_LIST_H__
#define __FAT_LIST_H__
#ifndef FAT_ASSERT
#define FAT_ASSERT(x)
#endif
#ifndef FAT_INLINE
#define FAT_INLINE
#endif
//-----------------------------------------------------------------
// Types
//-----------------------------------------------------------------
struct fat_list;
struct fat_node
{
struct fat_node *previous;
struct fat_node *next;
};
struct fat_list
{
struct fat_node *head;
struct fat_node *tail;
};
//-----------------------------------------------------------------
// Macros
//-----------------------------------------------------------------
#define fat_list_entry(p, t, m) p ? ((t *)((char *)(p)-(char*)(&((t *)0)->m))) : 0
#define fat_list_next(l, p) (p)->next
#define fat_list_prev(l, p) (p)->previous
#define fat_list_first(l) (l)->head
#define fat_list_last(l) (l)->tail
#define fat_list_for_each(l, p) for ((p) = (l)->head; (p); (p) = (p)->next)
//-----------------------------------------------------------------
// Inline Functions
//-----------------------------------------------------------------
//-----------------------------------------------------------------
// fat_list_init:
//-----------------------------------------------------------------
static FAT_INLINE void fat_list_init(struct fat_list *list)
{
FAT_ASSERT(list);
list->head = list->tail = 0;
}
//-----------------------------------------------------------------
// fat_list_remove:
//-----------------------------------------------------------------
static FAT_INLINE void fat_list_remove(struct fat_list *list, struct fat_node *node)
{
FAT_ASSERT(list);
FAT_ASSERT(node);
if(!node->previous)
list->head = node->next;
else
node->previous->next = node->next;
if(!node->next)
list->tail = node->previous;
else
node->next->previous = node->previous;
}
//-----------------------------------------------------------------
// fat_list_insert_after:
//-----------------------------------------------------------------
static FAT_INLINE void fat_list_insert_after(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
{
FAT_ASSERT(list);
FAT_ASSERT(node);
FAT_ASSERT(new_node);
new_node->previous = node;
new_node->next = node->next;
if (!node->next)
list->tail = new_node;
else
node->next->previous = new_node;
node->next = new_node;
}
//-----------------------------------------------------------------
// fat_list_insert_before:
//-----------------------------------------------------------------
static FAT_INLINE void fat_list_insert_before(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
{
FAT_ASSERT(list);
FAT_ASSERT(node);
FAT_ASSERT(new_node);
new_node->previous = node->previous;
new_node->next = node;
if (!node->previous)
list->head = new_node;
else
node->previous->next = new_node;
node->previous = new_node;
}
//-----------------------------------------------------------------
// fat_list_insert_first:
//-----------------------------------------------------------------
static FAT_INLINE void fat_list_insert_first(struct fat_list *list, struct fat_node *node)
{
FAT_ASSERT(list);
FAT_ASSERT(node);
if (!list->head)
{
list->head = node;
list->tail = node;
node->previous = 0;
node->next = 0;
}
else
fat_list_insert_before(list, list->head, node);
}
//-----------------------------------------------------------------
// fat_list_insert_last:
//-----------------------------------------------------------------
static FAT_INLINE void fat_list_insert_last(struct fat_list *list, struct fat_node *node)
{
FAT_ASSERT(list);
FAT_ASSERT(node);
if (!list->tail)
fat_list_insert_first(list, node);
else
fat_list_insert_after(list, list->tail, node);
}
//-----------------------------------------------------------------
// fat_list_is_empty:
//-----------------------------------------------------------------
static FAT_INLINE int fat_list_is_empty(struct fat_list *list)
{
FAT_ASSERT(list);
return !list->head;
}
//-----------------------------------------------------------------
// fat_list_pop_head:
//-----------------------------------------------------------------
static FAT_INLINE struct fat_node * fat_list_pop_head(struct fat_list *list)
{
struct fat_node * node;
FAT_ASSERT(list);
node = fat_list_first(list);
if (node)
fat_list_remove(list, node);
return node;
}
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment