Message ID | 20210325154623.7717-1-roland.gaudig-oss@weidmueller.com |
---|---|
State | Accepted |
Headers | show |
Series | [v2,1/5] fatfs: add FatFs library R0.14a by ChaN | expand |
Hi Roland, I have no specific comments to your series. I merged the series and I ran the coverity scan. I agree that this is imported code, and it does not matter if coding style differs from SWUpdate's. However, coverity discovers some topics in the code that I think they should fixes (with an additional patch). To be short: malloc() is called, no check if the pointer is Null. Can you check it ? Here the results: Hi, Please find the latest report on new defect(s) introduced to sbabic/swupdate found with Coverity Scan. 6 new defect(s) introduced to sbabic/swupdate found with Coverity Scan. New defect(s) Reported-by: Coverity Scan Showing 6 of 6 defect(s) ** CID 329979: Control flow issues (DEADCODE) /fatfs/ff.c: 5817 in create_partition()
Hi Roland, On 26.03.21 15:18, Stefano Babic wrote: > Hi Roland, > > I have no specific comments to your series. I merged the series and I > ran the coverity scan. I agree that this is imported code, and it does > not matter if coding style differs from SWUpdate's. However, coverity > discovers some topics in the code that I think they should fixes (with > an additional patch). To be short: malloc() is called, no check if the > pointer is Null. Can you check it ? > I was too fast to report the coverity's report : most of them are unrelated to this series. There is just: > Here the results: > > Hi, > > Please find the latest report on new defect(s) introduced to > sbabic/swupdate found with Coverity Scan. > > 6 new defect(s) introduced to sbabic/swupdate found with Coverity Scan. > > > New defect(s) Reported-by: Coverity Scan > Showing 6 of 6 defect(s) > > > ** CID 329979: Control flow issues (DEADCODE) > /fatfs/ff.c: 5817 in create_partition() > But this can be fixed later in a follow up code. I merge the series into master. Best regards, Stefano Babic > > ________________________________________________________________________________________________________ > > *** CID 329979: Control flow issues (DEADCODE) > /fatfs/ff.c: 5817 in create_partition() > 5811 } else > 5812 #endif > 5813 { /* Create partitions in MBR */ > 5814 sz_drv32 = (DWORD)sz_drv; > 5815 n_sc = N_SEC_TRACK; /* Determine drive CHS > without any consideration of the drive geometry */ > 5816 for (n_hd = 8; n_hd != 0 && sz_drv32 / n_hd / n_sc > > 1024; n_hd *= 2) ; > >>> CID 329979: Control flow issues (DEADCODE) > >>> Execution cannot reach this statement: "n_hd = 255;". > 5817 if (n_hd == 0) n_hd = 255; /* Number of heads needs > to be <256 */ > 5818 > 5819 mem_set(buf, 0, FF_MAX_SS); /* Clear MBR */ > 5820 pte = buf + MBR_Table; /* Partition table in the MBR */ > 5821 for (i = 0, s_lba32 = n_sc; i < 4 && s_lba32 != 0 && > s_lba32 < sz_drv32; i++, s_lba32 += n_lba32) { > 5822 n_lba32 = (DWORD)plst[i]; /* Get partition size */ > > ** CID 314959: Null pointer dereferences (NULL_RETURNS) > /mongoose/mongoose.c: 8474 in mg_file_upload_handler() > > > ________________________________________________________________________________________________________ > > *** CID 314959: Null pointer dereferences (NULL_RETURNS) > /mongoose/mongoose.c: 8474 in mg_file_upload_handler() > 8468 fus = (struct file_upload_state *) MG_CALLOC(1, > sizeof(*fus)); > 8469 if (fus == NULL) { > 8470 nc->flags |= MG_F_CLOSE_IMMEDIATELY; > 8471 return; > 8472 } > 8473 fus->lfn = (char *) MG_MALLOC(lfn.len + 1); > >>> CID 314959: Null pointer dereferences (NULL_RETURNS) > >>> Dereferencing a pointer that might be "NULL" "fus->lfn" when > calling "memcpy". > 8474 memcpy(fus->lfn, lfn.p, lfn.len); > 8475 fus->lfn[lfn.len] = '\0'; > 8476 if (lfn.p != mp->file_name) MG_FREE((char *) lfn.p); > 8477 LOG(LL_DEBUG, > 8478 ("%p Receiving file %s -> %s", nc, mp->file_name, > fus->lfn)); > 8479 fus->fp = mg_fopen(fus->lfn, "wb"); > > ** CID 314957: Null pointer dereferences (NULL_RETURNS) > /mongoose/mongoose.c: 2452 in mg_mgr_init_opt() > > > ________________________________________________________________________________________________________ > > *** CID 314957: Null pointer dereferences (NULL_RETURNS) > /mongoose/mongoose.c: 2452 in mg_mgr_init_opt() > 2446 opts.ifaces[MG_MAIN_IFACE] = opts.main_iface; > 2447 } > 2448 m->num_ifaces = opts.num_ifaces; > 2449 m->ifaces = > 2450 (struct mg_iface **) MG_MALLOC(sizeof(*m->ifaces) * > opts.num_ifaces); > 2451 for (i = 0; i < opts.num_ifaces; i++) { > >>> CID 314957: Null pointer dereferences (NULL_RETURNS) > >>> Dereferencing "m->ifaces", which is known to be "NULL". > 2452 m->ifaces[i] = mg_if_create_iface(opts.ifaces[i], m); > 2453 m->ifaces[i]->vtable->init(m->ifaces[i]); > 2454 } > 2455 } > 2456 if (opts.nameserver != NULL) { > 2457 m->nameserver = strdup(opts.nameserver); > > ** CID 314956: Null pointer dereferences (NULL_RETURNS) > > > ________________________________________________________________________________________________________ > > *** CID 314956: Null pointer dereferences (NULL_RETURNS) > /mongoose/mongoose.c: 12007 in mg_resolve_async_eh() > 12001 req->last_time = now; > 12002 req->retries++; > 12003 } > 12004 break; > 12005 case MG_EV_RECV: > 12006 msg = (struct mg_dns_message *) MG_MALLOC(sizeof(*msg)); > >>> CID 314956: Null pointer dereferences (NULL_RETURNS) > >>> Dereferencing a pointer that might be "NULL" "msg" when calling > "mg_parse_dns". > 12007 if (mg_parse_dns(nc->recv_mbuf.buf, *(int *) data, msg) > == 0 && > 12008 msg->num_answers > 0) { > 12009 req->callback(msg, req->data, MG_RESOLVE_OK); > 12010 nc->user_data = NULL; > 12011 MG_FREE(req); > 12012 } else { > > ** CID 314955: Null pointer dereferences (NULL_RETURNS) > /suricatta/server_hawkbit.c: 1485 in server_send_target_data() > > > ________________________________________________________________________________________________________ > > *** CID 314955: Null pointer dereferences (NULL_RETURNS) > /suricatta/server_hawkbit.c: 1485 in server_send_target_data() > 1479 if (!len) { > 1480 server_hawkbit.has_to_send_configData = false; > 1481 return SERVER_OK; > 1482 } > 1483 > 1484 char *configData = (char *)(malloc(len + 16)); > >>> CID 314955: Null pointer dereferences (NULL_RETURNS) > >>> Dereferencing a pointer that might be "NULL" "configData" when > calling "memset". > 1485 memset(configData, 0, len + 16); > 1486 > 1487 static const char* const config_data = STRINGIFY( > 1488 %c"%s": "%s" > 1489 ); > 1490 > > ** CID 314954: Null pointer dereferences (NULL_RETURNS) > > > ________________________________________________________________________________________________________ > > *** CID 314954: Null pointer dereferences (NULL_RETURNS) > /mongoose/mongoose.c: 7464 in mg_parse_http_basic_auth() > 7458 char fmt[64]; > 7459 int res = 0; > 7460 > 7461 if (mg_strncmp(*hdr, mg_mk_str("Basic "), 6) != 0) return -1; > 7462 > 7463 buf = (char *) MG_MALLOC(hdr->len); > >>> CID 314954: Null pointer dereferences (NULL_RETURNS) > >>> Dereferencing a pointer that might be "NULL" "buf" when calling > "cs_base64_decode". > 7464 cs_base64_decode((unsigned char *) hdr->p + 6, hdr->len, buf, > NULL); > 7465 > 7466 /* e.g. "%123[^:]:%321[^\n]" */ > 7467 snprintf(fmt, sizeof(fmt), "%%%" SIZE_T_FMT "[^:]:%%%" > SIZE_T_FMT "[^\n]", > 7468 user_len - 1, pass_len - 1); > 7469 if (sscanf(buf, fmt, user, pass) == 0) { > > Best regards, > Stefano Babic > > On 25.03.21 16:46, roland.gaudig-oss@weidmueller.com wrote: >> From: Roland Gaudig <roland.gaudig@weidmueller.com> >> >> FatFs is a FAT filesystem implementation for small embedded systems >> written >> by ChaN. For more information see: >> http://elm-chan.org/fsw/ff/00index_e.html >> >> Signed-off-by: Roland Gaudig <roland.gaudig@weidmueller.com> >> --- >> fatfs/diskio.c | 174 ++ >> fatfs/diskio.h | 96 + >> fatfs/ff.c | 6875 ++++++++++++++++++++++++++++++++++++++++++++++++ >> fatfs/ff.h | 425 +++ >> fatfs/ffconf.h | 317 +++ >> 5 files changed, 7887 insertions(+) >> create mode 100644 fatfs/diskio.c >> create mode 100644 fatfs/diskio.h >> create mode 100644 fatfs/ff.c >> create mode 100644 fatfs/ff.h >> create mode 100644 fatfs/ffconf.h >> >> diff --git a/fatfs/diskio.c b/fatfs/diskio.c >> new file mode 100644 >> index 0000000..1976add >> --- /dev/null >> +++ b/fatfs/diskio.c >> @@ -0,0 +1,174 @@ >> +/* >> + * Copyright (C) 2021 Weidmueller Interface GmbH & Co. KG >> + * Roland Gaudig <roland.gaudig@weidmueller.com> >> + * >> + * SPDX-License-Identifier: GPL-2.0-or-later >> + */ >> + >> +#include <errno.h> >> +#include <fcntl.h> >> +#include <time.h> >> +#include <unistd.h> >> +#include <linux/fs.h> >> +#include <sys/ioctl.h> >> + >> +#include "swupdate.h" >> +#include "util.h" >> + >> +#include "ff.h" >> +#include "diskio.h" >> + >> + >> +#define SECTOR_SIZE 512 >> + >> +static int file_descriptor; >> +static char device_name[MAX_VOLNAME]; >> +static bool init_status; >> + >> +int fatfs_init(char *device); >> +void fatfs_release(void); >> + >> + >> +int fatfs_init(char *device) >> +{ >> + if (strnlen(device_name, MAX_VOLNAME)) { >> + ERROR("Called fatfs_init second time without fatfs_release"); >> + return -1; >> + } >> + >> + strncpy(device_name, device, sizeof(device_name)); >> + file_descriptor = open(device_name, O_RDWR); >> + >> + if (file_descriptor < 0) { >> + ERROR("Device %s cannot be opened: %s", device_name, >> strerror(errno)); >> + return -ENODEV; >> + } >> + >> + return 0; >> +} >> + >> +void fatfs_release(void) >> +{ >> + (void)close(file_descriptor); >> + memset(device_name, 0, MAX_VOLNAME); >> + init_status = false; >> +} >> + >> +DSTATUS disk_status(BYTE pdrv) >> +{ >> + DSTATUS status = 0; >> + (void)pdrv; >> + >> + if (!strnlen(device_name, MAX_VOLNAME)) >> + status |= STA_NODISK; >> + >> + if (!init_status) >> + status |= STA_NOINIT; >> + >> + return status; >> +} >> + >> +DSTATUS disk_initialize(BYTE pdrv) >> +{ >> + init_status = true; >> + >> + return disk_status(pdrv); >> +} >> + >> +DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) >> +{ >> + (void)pdrv; >> + >> + if (!buff) >> + return RES_PARERR; >> + >> + if (disk_status(pdrv)) >> + return RES_NOTRDY; >> + >> + if (pread(file_descriptor, buff, count * SECTOR_SIZE, sector * >> SECTOR_SIZE) != count * SECTOR_SIZE) >> + return RES_ERROR; >> + >> + return RES_OK; >> +} >> + >> +#if FF_FS_READONLY == 0 >> +DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT >> count) >> +{ >> + (void)pdrv; >> + >> + if (!buff) >> + return RES_PARERR; >> + >> + if (disk_status(pdrv)) >> + return RES_NOTRDY; >> + >> + if (pwrite(file_descriptor, buff, count * SECTOR_SIZE, sector * >> SECTOR_SIZE) != count * SECTOR_SIZE) >> + return RES_ERROR; >> + >> + return RES_OK; >> +} >> +#endif >> + >> +DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) >> +{ >> + (void) pdrv; >> + >> + if (disk_status(pdrv)) >> + return RES_NOTRDY; >> + >> + switch (cmd) { >> + case CTRL_SYNC: >> + if (syncfs(file_descriptor) != 0) >> + return RES_ERROR; >> + break; >> + case GET_SECTOR_COUNT: >> + { >> + off_t size = lseek(file_descriptor, 0, SEEK_END) / SECTOR_SIZE; >> + >> + if (!buff) >> + return RES_PARERR; >> + >> + *(LBA_t*)buff = size; >> + break; >> + } >> + case GET_SECTOR_SIZE: >> + if (!buff) >> + return RES_PARERR; >> + >> + *(WORD*)buff = SECTOR_SIZE; >> + break; >> + case GET_BLOCK_SIZE: >> + /* Get erase block size of flash memories, return 1 if not a >> + * Flash memory or unknown. >> + */ >> + if (!buff) >> + return RES_PARERR; >> + >> + *(WORD*)buff = 1; >> + break; >> + default: >> + ERROR("cmd %d not implemented", cmd); >> + return RES_PARERR; >> + break; >> + } >> + >> + return RES_OK; >> +} >> + >> +DWORD get_fattime(void) >> +{ >> + time_t unix_time = time(NULL); >> + struct tm *t = gmtime(&unix_time); >> + >> + /* FatFs times are based on year 1980 */ >> + DWORD tdos = ((t->tm_year - 80) & 0x3F) << 25; >> + /* FatFs months start with 1 */ >> + tdos |= (t->tm_mon + 1) << 21; >> + tdos |= t->tm_mday << 16; >> + tdos |= t->tm_hour << 11; >> + tdos |= t->tm_min << 5; >> + /* Don't know how FatFs copes with leap seconds, therefore limit >> them */ >> + tdos |= (t->tm_sec % 60) / 2; >> + >> + return tdos; >> +} >> diff --git a/fatfs/diskio.h b/fatfs/diskio.h >> new file mode 100644 >> index 0000000..1f019a1 >> --- /dev/null >> +++ b/fatfs/diskio.h >> @@ -0,0 +1,96 @@ >> +/*-----------------------------------------------------------------------/ >> >> +/ Low level disk interface modlue include file (C)ChaN, >> 2019 / >> +/------------------------------------------------------------------------/ >> >> +/ >> +/ Copyright (C) 2019, ChaN, all right reserved. >> +/ >> +/ FatFs module is an open source software. Redistribution and use of >> FatFs in >> +/ source and binary forms, with or without modification, are >> permitted provided >> +/ that the following condition is met: >> +/ >> +/ 1. Redistributions of source code must retain the above copyright >> notice, >> +/ this condition and the following disclaimer. >> +/ >> +/ This software is provided by the copyright holder and contributors >> "AS IS" >> +/ and any warranties related to this software are DISCLAIMED. >> +/ The copyright owner or contributors be NOT LIABLE for any damages >> caused >> +/ by use of this software. >> +/ >> +/ SPDX-License-Identifier: BSD-1-Clause >> +/ >> +/-----------------------------------------------------------------------*/ >> >> + >> + >> +#ifndef _DISKIO_DEFINED >> +#define _DISKIO_DEFINED >> + >> +#ifdef __cplusplus >> +extern "C" { >> +#endif >> + >> +/* Status of Disk Functions */ >> +typedef BYTE DSTATUS; >> + >> +/* Results of Disk Functions */ >> +typedef enum { >> + RES_OK = 0, /* 0: Successful */ >> + RES_ERROR, /* 1: R/W Error */ >> + RES_WRPRT, /* 2: Write Protected */ >> + RES_NOTRDY, /* 3: Not Ready */ >> + RES_PARERR /* 4: Invalid Parameter */ >> +} DRESULT; >> + >> + >> +/*---------------------------------------*/ >> +/* Prototypes for disk control functions */ >> + >> + >> +DSTATUS disk_initialize (BYTE pdrv); >> +DSTATUS disk_status (BYTE pdrv); >> +DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count); >> +DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT >> count); >> +DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); >> + >> + >> +/* Disk Status Bits (DSTATUS) */ >> + >> +#define STA_NOINIT 0x01 /* Drive not initialized */ >> +#define STA_NODISK 0x02 /* No medium in the drive */ >> +#define STA_PROTECT 0x04 /* Write protected */ >> + >> + >> +/* Command code for disk_ioctrl fucntion */ >> + >> +/* Generic command (Used by FatFs) */ >> +#define CTRL_SYNC 0 /* Complete pending write process >> (needed at FF_FS_READONLY == 0) */ >> +#define GET_SECTOR_COUNT 1 /* Get media size (needed at >> FF_USE_MKFS == 1) */ >> +#define GET_SECTOR_SIZE 2 /* Get sector size (needed at >> FF_MAX_SS != FF_MIN_SS) */ >> +#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at >> FF_USE_MKFS == 1) */ >> +#define CTRL_TRIM 4 /* Inform device that the data on >> the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */ >> + >> +/* Generic command (Not used by FatFs) */ >> +#define CTRL_POWER 5 /* Get/Set power status */ >> +#define CTRL_LOCK 6 /* Lock/Unlock media removal */ >> +#define CTRL_EJECT 7 /* Eject media */ >> +#define CTRL_FORMAT 8 /* Create physical format on the >> media */ >> + >> +/* MMC/SDC specific ioctl command */ >> +#define MMC_GET_TYPE 10 /* Get card type */ >> +#define MMC_GET_CSD 11 /* Get CSD */ >> +#define MMC_GET_CID 12 /* Get CID */ >> +#define MMC_GET_OCR 13 /* Get OCR */ >> +#define MMC_GET_SDSTAT 14 /* Get SD status */ >> +#define ISDIO_READ 55 /* Read data form SD iSDIO >> register */ >> +#define ISDIO_WRITE 56 /* Write data to SD iSDIO >> register */ >> +#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO >> register */ >> + >> +/* ATA/CF specific ioctl command */ >> +#define ATA_GET_REV 20 /* Get F/W revision */ >> +#define ATA_GET_MODEL 21 /* Get model name */ >> +#define ATA_GET_SN 22 /* Get serial number */ >> + >> +#ifdef __cplusplus >> +} >> +#endif >> + >> +#endif >> diff --git a/fatfs/ff.c b/fatfs/ff.c >> new file mode 100644 >> index 0000000..9281ec8 >> --- /dev/null >> +++ b/fatfs/ff.c >> @@ -0,0 +1,6875 @@ >> +/*----------------------------------------------------------------------------/ >> >> +/ FatFs - Generic FAT Filesystem Module >> R0.14a / >> +/-----------------------------------------------------------------------------/ >> >> +/ >> +/ Copyright (C) 2020, ChaN, all right reserved. >> +/ >> +/ FatFs module is an open source software. Redistribution and use of >> FatFs in >> +/ source and binary forms, with or without modification, are >> permitted provided >> +/ that the following condition is met: >> +/ >> +/ 1. Redistributions of source code must retain the above copyright >> notice, >> +/ this condition and the following disclaimer. >> +/ >> +/ This software is provided by the copyright holder and contributors >> "AS IS" >> +/ and any warranties related to this software are DISCLAIMED. >> +/ The copyright owner or contributors be NOT LIABLE for any damages >> caused >> +/ by use of this software. >> +/ >> +/ SPDX-License-Identifier: BSD-1-Clause >> +/ >> +/----------------------------------------------------------------------------*/ >> >> + >> + >> +#include "ff.h" /* Declarations of FatFs API */ >> +#include "diskio.h" /* Declarations of device I/O functions */ >> + >> + >> +/*-------------------------------------------------------------------------- >> >> + >> + Module Private Definitions >> + >> +---------------------------------------------------------------------------*/ >> >> + >> +#if FF_DEFINED != 80196 /* Revision ID */ >> +#error Wrong include file (ff.h). >> +#endif >> + >> + >> +/* Limits and boundaries */ >> +#define MAX_DIR 0x200000 /* Max size of FAT directory */ >> +#define MAX_DIR_EX 0x10000000 /* Max size of exFAT >> directory */ >> +#define MAX_FAT12 0xFF5 /* Max FAT12 clusters (differs >> from specs, but right for real DOS/Windows behavior) */ >> +#define MAX_FAT16 0xFFF5 /* Max FAT16 clusters (differs >> from specs, but right for real DOS/Windows behavior) */ >> +#define MAX_FAT32 0x0FFFFFF5 /* Max FAT32 clusters (not >> specified, practical limit) */ >> +#define MAX_EXFAT 0x7FFFFFFD /* Max exFAT clusters (differs >> from specs, implementation limit) */ >> + >> + >> +/* Character code support macros */ >> +#define IsUpper(c) ((c) >= 'A' && (c) <= 'Z') >> +#define IsLower(c) ((c) >= 'a' && (c) <= 'z') >> +#define IsDigit(c) ((c) >= '0' && (c) <= '9') >> +#define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF) >> +#define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF) >> +#define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF) >> + >> + >> +/* Additional file access control and file status flags for internal >> use */ >> +#define FA_SEEKEND 0x20 /* Seek to end of the file on file open */ >> +#define FA_MODIFIED 0x40 /* File has been modified */ >> +#define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */ >> + >> + >> +/* Additional file attribute bits for internal use */ >> +#define AM_VOL 0x08 /* Volume label */ >> +#define AM_LFN 0x0F /* LFN entry */ >> +#define AM_MASK 0x3F /* Mask of defined bits */ >> + >> + >> +/* Name status flags in fn[11] */ >> +#define NSFLAG 11 /* Index of the name status byte */ >> +#define NS_LOSS 0x01 /* Out of 8.3 format */ >> +#define NS_LFN 0x02 /* Force to create LFN entry */ >> +#define NS_LAST 0x04 /* Last segment */ >> +#define NS_BODY 0x08 /* Lower case flag (body) */ >> +#define NS_EXT 0x10 /* Lower case flag (ext) */ >> +#define NS_DOT 0x20 /* Dot entry */ >> +#define NS_NOLFN 0x40 /* Do not find LFN */ >> +#define NS_NONAME 0x80 /* Not followed */ >> + >> + >> +/* exFAT directory entry types */ >> +#define ET_BITMAP 0x81 /* Allocation bitmap */ >> +#define ET_UPCASE 0x82 /* Up-case table */ >> +#define ET_VLABEL 0x83 /* Volume label */ >> +#define ET_FILEDIR 0x85 /* File and directory */ >> +#define ET_STREAM 0xC0 /* Stream extension */ >> +#define ET_FILENAME 0xC1 /* Name extension */ >> + >> + >> +/* FatFs refers the FAT structure as simple byte array instead of >> structure member >> +/ because the C structure is not binary compatible between different >> platforms */ >> + >> +#define BS_JmpBoot 0 /* x86 jump instruction >> (3-byte) */ >> +#define BS_OEMName 3 /* OEM name (8-byte) */ >> +#define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */ >> +#define BPB_SecPerClus 13 /* Cluster size [sector] >> (BYTE) */ >> +#define BPB_RsvdSecCnt 14 /* Size of reserved area >> [sector] (WORD) */ >> +#define BPB_NumFATs 16 /* Number of FATs (BYTE) */ >> +#define BPB_RootEntCnt 17 /* Size of root directory >> area for FAT [entry] (WORD) */ >> +#define BPB_TotSec16 19 /* Volume size (16-bit) >> [sector] (WORD) */ >> +#define BPB_Media 21 /* Media descriptor byte >> (BYTE) */ >> +#define BPB_FATSz16 22 /* FAT size (16-bit) >> [sector] (WORD) */ >> +#define BPB_SecPerTrk 24 /* Number of sectors per track >> for int13h [sector] (WORD) */ >> +#define BPB_NumHeads 26 /* Number of heads for int13h >> (WORD) */ >> +#define BPB_HiddSec 28 /* Volume offset from top of >> the drive (DWORD) */ >> +#define BPB_TotSec32 32 /* Volume size (32-bit) >> [sector] (DWORD) */ >> +#define BS_DrvNum 36 /* Physical drive number for >> int13h (BYTE) */ >> +#define BS_NTres 37 /* WindowsNT error flag (BYTE) */ >> +#define BS_BootSig 38 /* Extended boot signature >> (BYTE) */ >> +#define BS_VolID 39 /* Volume serial number (DWORD) */ >> +#define BS_VolLab 43 /* Volume label string >> (8-byte) */ >> +#define BS_FilSysType 54 /* Filesystem type string >> (8-byte) */ >> +#define BS_BootCode 62 /* Boot code (448-byte) */ >> +#define BS_55AA 510 /* Signature word (WORD) */ >> + >> +#define BPB_FATSz32 36 /* FAT32: FAT size [sector] >> (DWORD) */ >> +#define BPB_ExtFlags32 40 /* FAT32: Extended flags >> (WORD) */ >> +#define BPB_FSVer32 42 /* FAT32: Filesystem version >> (WORD) */ >> +#define BPB_RootClus32 44 /* FAT32: Root directory >> cluster (DWORD) */ >> +#define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO >> sector (WORD) */ >> +#define BPB_BkBootSec32 50 /* FAT32: Offset of backup >> boot sector (WORD) */ >> +#define BS_DrvNum32 64 /* FAT32: Physical drive >> number for int13h (BYTE) */ >> +#define BS_NTres32 65 /* FAT32: Error flag (BYTE) */ >> +#define BS_BootSig32 66 /* FAT32: Extended boot >> signature (BYTE) */ >> +#define BS_VolID32 67 /* FAT32: Volume serial >> number (DWORD) */ >> +#define BS_VolLab32 71 /* FAT32: Volume label >> string (8-byte) */ >> +#define BS_FilSysType32 82 /* FAT32: Filesystem type >> string (8-byte) */ >> +#define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */ >> + >> +#define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */ >> +#define BPB_VolOfsEx 64 /* exFAT: Volume offset from >> top of the drive [sector] (QWORD) */ >> +#define BPB_TotSecEx 72 /* exFAT: Volume size [sector] >> (QWORD) */ >> +#define BPB_FatOfsEx 80 /* exFAT: FAT offset from top >> of the volume [sector] (DWORD) */ >> +#define BPB_FatSzEx 84 /* exFAT: FAT size [sector] >> (DWORD) */ >> +#define BPB_DataOfsEx 88 /* exFAT: Data offset from top >> of the volume [sector] (DWORD) */ >> +#define BPB_NumClusEx 92 /* exFAT: Number of clusters >> (DWORD) */ >> +#define BPB_RootClusEx 96 /* exFAT: Root directory >> start cluster (DWORD) */ >> +#define BPB_VolIDEx 100 /* exFAT: Volume serial >> number (DWORD) */ >> +#define BPB_FSVerEx 104 /* exFAT: Filesystem >> version (WORD) */ >> +#define BPB_VolFlagEx 106 /* exFAT: Volume flags (WORD) */ >> +#define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size >> in unit of byte (BYTE) */ >> +#define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size >> in unit of sector (BYTE) */ >> +#define BPB_NumFATsEx 110 /* exFAT: Number of FATs >> (BYTE) */ >> +#define BPB_DrvNumEx 111 /* exFAT: Physical drive >> number for int13h (BYTE) */ >> +#define BPB_PercInUseEx 112 /* exFAT: Percent in use >> (BYTE) */ >> +#define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */ >> +#define BS_BootCodeEx 120 /* exFAT: Boot code >> (390-byte) */ >> + >> +#define DIR_Name 0 /* Short file name (11-byte) */ >> +#define DIR_Attr 11 /* Attribute (BYTE) */ >> +#define DIR_NTres 12 /* Lower case flag (BYTE) */ >> +#define DIR_CrtTime10 13 /* Created time sub-second >> (BYTE) */ >> +#define DIR_CrtTime 14 /* Created time (DWORD) */ >> +#define DIR_LstAccDate 18 /* Last accessed date (WORD) */ >> +#define DIR_FstClusHI 20 /* Higher 16-bit of first >> cluster (WORD) */ >> +#define DIR_ModTime 22 /* Modified time (DWORD) */ >> +#define DIR_FstClusLO 26 /* Lower 16-bit of first >> cluster (WORD) */ >> +#define DIR_FileSize 28 /* File size (DWORD) */ >> +#define LDIR_Ord 0 /* LFN: LFN order and LLE flag >> (BYTE) */ >> +#define LDIR_Attr 11 /* LFN: LFN attribute (BYTE) */ >> +#define LDIR_Type 12 /* LFN: Entry type (BYTE) */ >> +#define LDIR_Chksum 13 /* LFN: Checksum of the SFN >> (BYTE) */ >> +#define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD) */ >> +#define XDIR_Type 0 /* exFAT: Type of exFAT >> directory entry (BYTE) */ >> +#define XDIR_NumLabel 1 /* exFAT: Number of volume >> label characters (BYTE) */ >> +#define XDIR_Label 2 /* exFAT: Volume label >> (11-WORD) */ >> +#define XDIR_CaseSum 4 /* exFAT: Sum of case conversion >> table (DWORD) */ >> +#define XDIR_NumSec 1 /* exFAT: Number of secondary >> entries (BYTE) */ >> +#define XDIR_SetSum 2 /* exFAT: Sum of the set of >> directory entries (WORD) */ >> +#define XDIR_Attr 4 /* exFAT: File attribute (WORD) */ >> +#define XDIR_CrtTime 8 /* exFAT: Created time (DWORD) */ >> +#define XDIR_ModTime 12 /* exFAT: Modified time (DWORD) */ >> +#define XDIR_AccTime 16 /* exFAT: Last accessed time >> (DWORD) */ >> +#define XDIR_CrtTime10 20 /* exFAT: Created time >> subsecond (BYTE) */ >> +#define XDIR_ModTime10 21 /* exFAT: Modified time >> subsecond (BYTE) */ >> +#define XDIR_CrtTZ 22 /* exFAT: Created timezone >> (BYTE) */ >> +#define XDIR_ModTZ 23 /* exFAT: Modified timezone >> (BYTE) */ >> +#define XDIR_AccTZ 24 /* exFAT: Last accessed >> timezone (BYTE) */ >> +#define XDIR_GenFlags 33 /* exFAT: General secondary >> flags (BYTE) */ >> +#define XDIR_NumName 35 /* exFAT: Number of file name >> characters (BYTE) */ >> +#define XDIR_NameHash 36 /* exFAT: Hash of file name >> (WORD) */ >> +#define XDIR_ValidFileSize 40 /* exFAT: Valid file size >> (QWORD) */ >> +#define XDIR_FstClus 52 /* exFAT: First cluster of the >> file data (DWORD) */ >> +#define XDIR_FileSize 56 /* exFAT: File/Directory size >> (QWORD) */ >> + >> +#define SZDIRE 32 /* Size of a directory entry */ >> +#define DDEM 0xE5 /* Deleted directory entry mark >> set to DIR_Name[0] */ >> +#define RDDEM 0x05 /* Replacement of the character >> collides with DDEM */ >> +#define LLEF 0x40 /* Last long entry flag in >> LDIR_Ord */ >> + >> +#define FSI_LeadSig 0 /* FAT32 FSI: Leading >> signature (DWORD) */ >> +#define FSI_StrucSig 484 /* FAT32 FSI: Structure >> signature (DWORD) */ >> +#define FSI_Free_Count 488 /* FAT32 FSI: Number of free >> clusters (DWORD) */ >> +#define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated >> cluster (DWORD) */ >> + >> +#define MBR_Table 446 /* MBR: Offset of partition >> table in the MBR */ >> +#define SZ_PTE 16 /* MBR: Size of a partition >> table entry */ >> +#define PTE_Boot 0 /* MBR PTE: Boot indicator */ >> +#define PTE_StHead 1 /* MBR PTE: Start head */ >> +#define PTE_StSec 2 /* MBR PTE: Start sector */ >> +#define PTE_StCyl 3 /* MBR PTE: Start cylinder */ >> +#define PTE_System 4 /* MBR PTE: System ID */ >> +#define PTE_EdHead 5 /* MBR PTE: End head */ >> +#define PTE_EdSec 6 /* MBR PTE: End sector */ >> +#define PTE_EdCyl 7 /* MBR PTE: End cylinder */ >> +#define PTE_StLba 8 /* MBR PTE: Start in LBA */ >> +#define PTE_SizLba 12 /* MBR PTE: Size in LBA */ >> + >> +#define GPTH_Sign 0 /* GPT: Header signature >> (8-byte) */ >> +#define GPTH_Rev 8 /* GPT: Revision (DWORD) */ >> +#define GPTH_Size 12 /* GPT: Header size (DWORD) */ >> +#define GPTH_Bcc 16 /* GPT: Header BCC (DWORD) */ >> +#define GPTH_CurLba 24 /* GPT: Main header LBA >> (QWORD) */ >> +#define GPTH_BakLba 32 /* GPT: Backup header LBA >> (QWORD) */ >> +#define GPTH_FstLba 40 /* GPT: First LBA for >> partitions (QWORD) */ >> +#define GPTH_LstLba 48 /* GPT: Last LBA for >> partitions (QWORD) */ >> +#define GPTH_DskGuid 56 /* GPT: Disk GUID (16-byte) */ >> +#define GPTH_PtOfs 72 /* GPT: Partation table LBA >> (QWORD) */ >> +#define GPTH_PtNum 80 /* GPT: Number of table >> entries (DWORD) */ >> +#define GPTH_PteSize 84 /* GPT: Size of table entry >> (DWORD) */ >> +#define GPTH_PtBcc 88 /* GPT: Partation table BCC >> (DWORD) */ >> +#define SZ_GPTE 128 /* GPT: Size of partition >> table entry */ >> +#define GPTE_PtGuid 0 /* GPT PTE: Partition type >> GUID (16-byte) */ >> +#define GPTE_UpGuid 16 /* GPT PTE: Partition unique >> GUID (16-byte) */ >> +#define GPTE_FstLba 32 /* GPT PTE: First LBA >> (QWORD) */ >> +#define GPTE_LstLba 40 /* GPT PTE: Last LBA >> inclusive (QWORD) */ >> +#define GPTE_Flags 48 /* GPT PTE: Flags (QWORD) */ >> +#define GPTE_Name 56 /* GPT PTE: Name */ >> + >> + >> +/* Post process on fatal error in the file operations */ >> +#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, >> res); } >> + >> + >> +/* Re-entrancy related */ >> +#if FF_FS_REENTRANT >> +#if FF_USE_LFN == 1 >> +#error Static LFN work area cannot be used at thread-safe configuration >> +#endif >> +#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } >> +#else >> +#define LEAVE_FF(fs, res) return res >> +#endif >> + >> + >> +/* Definitions of logical drive - physical location conversion */ >> +#if FF_MULTI_PARTITION >> +#define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number */ >> +#define LD2PT(vol) VolToPart[vol].pt /* Get partition index */ >> +#else >> +#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is associated >> with the same physical drive number */ >> +#define LD2PT(vol) 0 /* Find first valid partition or in >> SFD */ >> +#endif >> + >> + >> +/* Definitions of sector size */ >> +#if (FF_MAX_SS < FF_MIN_SS) || (FF_MAX_SS != 512 && FF_MAX_SS != 1024 >> && FF_MAX_SS != 2048 && FF_MAX_SS != 4096) || (FF_MIN_SS != 512 && >> FF_MIN_SS != 1024 && FF_MIN_SS != 2048 && FF_MIN_SS != 4096) >> +#error Wrong sector size configuration >> +#endif >> +#if FF_MAX_SS == FF_MIN_SS >> +#define SS(fs) ((UINT)FF_MAX_SS) /* Fixed sector size */ >> +#else >> +#define SS(fs) ((fs)->ssize) /* Variable sector size */ >> +#endif >> + >> + >> +/* Timestamp */ >> +#if FF_FS_NORTC == 1 >> +#if FF_NORTC_YEAR < 1980 || FF_NORTC_YEAR > 2107 || FF_NORTC_MON < 1 >> || FF_NORTC_MON > 12 || FF_NORTC_MDAY < 1 || FF_NORTC_MDAY > 31 >> +#error Invalid FF_FS_NORTC settings >> +#endif >> +#define GET_FATTIME() ((DWORD)(FF_NORTC_YEAR - 1980) << 25 | >> (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16) >> +#else >> +#define GET_FATTIME() get_fattime() >> +#endif >> + >> + >> +/* File lock controls */ >> +#if FF_FS_LOCK != 0 >> +#if FF_FS_READONLY >> +#error FF_FS_LOCK must be 0 at read-only configuration >> +#endif >> +typedef struct { >> + FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */ >> + DWORD clu; /* Object ID 2, containing directory (0:root) */ >> + DWORD ofs; /* Object ID 3, offset in the directory */ >> + WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read >> mode open count, 0x100:write mode */ >> +} FILESEM; >> +#endif >> + >> + >> +/* SBCS up-case tables (\x80-\xFF) */ >> +#define TBL_CT437 >> {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, >> \ >> + >> 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >> \ >> + >> 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >> \ >> + >> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >> \ >> + >> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >> \ >> + >> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >> \ >> + >> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >> \ >> + >> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >> >> +#define TBL_CT720 >> {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >> \ >> + >> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >> \ >> + >> 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >> \ >> + >> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >> \ >> + >> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >> \ >> + >> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >> \ >> + >> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >> \ >> + >> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >> >> +#define TBL_CT737 >> {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >> \ >> + >> 0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, >> \ >> + >> 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, >> \ >> + >> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >> \ >> + >> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >> \ >> + >> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >> \ >> + >> 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >> \ >> + >> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >> >> +#define TBL_CT771 >> {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >> \ >> + >> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >> \ >> + >> 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >> \ >> + >> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >> \ >> + >> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >> \ >> + >> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, >> \ >> + >> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >> \ >> + >> 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF} >> >> +#define TBL_CT775 >> {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, >> \ >> + >> 0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, >> \ >> + >> 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >> \ >> + >> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >> \ >> + >> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >> \ >> + >> 0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >> \ >> + >> 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, >> \ >> + >> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >> >> +#define TBL_CT850 >> {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, >> \ >> + >> 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, >> \ >> + >> 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >> \ >> + >> 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >> \ >> + >> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >> \ >> + >> 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, >> \ >> + >> 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, >> \ >> + >> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >> >> +#define TBL_CT852 >> {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, >> \ >> + >> 0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, >> \ >> + >> 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, >> \ >> + >> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, >> \ >> + >> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >> \ >> + >> 0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >> \ >> + >> 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, >> \ >> + >> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} >> >> +#define TBL_CT855 >> {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, >> \ >> + >> 0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, >> \ >> + >> 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, >> \ >> + >> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, >> \ >> + >> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >> \ >> + >> 0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, >> \ >> + >> 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, >> \ >> + >> 0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} >> >> +#define TBL_CT857 >> {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, >> \ >> + >> 0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, >> \ >> + >> 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >> \ >> + >> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >> \ >> + >> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >> \ >> + >> 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >> \ >> + >> 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, >> \ >> + >> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >> >> +#define TBL_CT860 >> {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, >> \ >> + >> 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >> \ >> + >> 0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >> \ >> + >> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >> \ >> + >> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >> \ >> + >> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >> \ >> + >> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >> \ >> + >> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >> >> +#define TBL_CT861 >> {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, >> \ >> + >> 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, >> \ >> + >> 0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >> \ >> + >> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >> \ >> + >> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >> \ >> + >> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >> \ >> + >> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >> \ >> + >> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >> >> +#define TBL_CT862 >> {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >> \ >> + >> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >> \ >> + >> 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >> \ >> + >> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >> \ >> + >> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >> \ >> + >> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >> \ >> + >> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >> \ >> + >> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >> >> +#define TBL_CT863 >> {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, >> \ >> + >> 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, >> \ >> + >> 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >> \ >> + >> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >> \ >> + >> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >> \ >> + >> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >> \ >> + >> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >> \ >> + >> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >> >> +#define TBL_CT864 >> {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, >> \ >> + >> 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >> \ >> + >> 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >> \ >> + >> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >> \ >> + >> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >> \ >> + >> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >> \ >> + >> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >> \ >> + >> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >> >> +#define TBL_CT865 >> {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, >> \ >> + >> 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >> \ >> + >> 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >> \ >> + >> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >> \ >> + >> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >> \ >> + >> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >> \ >> + >> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >> \ >> + >> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >> >> +#define TBL_CT866 >> {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >> \ >> + >> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >> \ >> + >> 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >> \ >> + >> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >> \ >> + >> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >> \ >> + >> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >> \ >> + >> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >> \ >> + >> 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >> >> +#define TBL_CT869 >> {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >> \ >> + >> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, >> \ >> + >> 0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >> \ >> + >> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >> \ >> + >> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >> \ >> + >> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, >> \ >> + >> 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, >> \ >> + >> 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF} >> >> + >> + >> +/* DBCS code range |----- 1st byte -----| |----------- 2nd byte >> -----------| */ >> +/* <------> <------> <------> <------> >> <------> */ >> +#define TBL_DC932 {0x81, 0x9F, 0xE0, 0xFC, 0x40, 0x7E, 0x80, 0xFC, >> 0x00, 0x00} >> +#define TBL_DC936 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0x80, 0xFE, >> 0x00, 0x00} >> +#define TBL_DC949 {0x81, 0xFE, 0x00, 0x00, 0x41, 0x5A, 0x61, 0x7A, >> 0x81, 0xFE} >> +#define TBL_DC950 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0xA1, 0xFE, >> 0x00, 0x00} >> + >> + >> +/* Macros for table definitions */ >> +#define MERGE_2STR(a, b) a ## b >> +#define MKCVTBL(hd, cp) MERGE_2STR(hd, cp) >> + >> + >> + >> + >> +/*-------------------------------------------------------------------------- >> >> + >> + Module Private Work Area >> + >> +---------------------------------------------------------------------------*/ >> >> +/* Remark: Variables defined here without initial value shall be >> guaranteed >> +/ zero/null at start-up. If not, the linker option or start-up >> routine is >> +/ not compliance with C standard. */ >> + >> +/*--------------------------------*/ >> +/* File/Volume controls */ >> +/*--------------------------------*/ >> + >> +#if FF_VOLUMES < 1 || FF_VOLUMES > 10 >> +#error Wrong FF_VOLUMES setting >> +#endif >> +static FATFS* FatFs[FF_VOLUMES]; /* Pointer to the filesystem >> objects (logical drives) */ >> +static WORD Fsid; /* Filesystem mount ID */ >> + >> +#if FF_FS_RPATH != 0 >> +static BYTE CurrVol; /* Current drive */ >> +#endif >> + >> +#if FF_FS_LOCK != 0 >> +static FILESEM Files[FF_FS_LOCK]; /* Open object lock semaphores */ >> +#endif >> + >> +#if FF_STR_VOLUME_ID >> +#ifdef FF_VOLUME_STRS >> +static const char* const VolumeStr[FF_VOLUMES] = {FF_VOLUME_STRS}; >> /* Pre-defined volume ID */ >> +#endif >> +#endif >> + >> +#if FF_LBA64 >> +#if FF_MIN_GPT > 0x100000000 >> +#error Wrong FF_MIN_GPT setting >> +#endif >> +static const BYTE GUID_MS_Basic[16] = >> {0xA2,0xA0,0xD0,0xEB,0xE5,0xB9,0x33,0x44,0x87,0xC0,0x68,0xB6,0xB7,0x26,0x99,0xC7}; >> >> +#endif >> + >> + >> + >> +/*--------------------------------*/ >> +/* LFN/Directory working buffer */ >> +/*--------------------------------*/ >> + >> +#if FF_USE_LFN == 0 /* Non-LFN configuration */ >> +#if FF_FS_EXFAT >> +#error LFN must be enabled when enable exFAT >> +#endif >> +#define DEF_NAMBUF >> +#define INIT_NAMBUF(fs) >> +#define FREE_NAMBUF() >> +#define LEAVE_MKFS(res) return res >> + >> +#else /* LFN configurations */ >> +#if FF_MAX_LFN < 12 || FF_MAX_LFN > 255 >> +#error Wrong setting of FF_MAX_LFN >> +#endif >> +#if FF_LFN_BUF < FF_SFN_BUF || FF_SFN_BUF < 12 >> +#error Wrong setting of FF_LFN_BUF or FF_SFN_BUF >> +#endif >> +#if FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3 >> +#error Wrong setting of FF_LFN_UNICODE >> +#endif >> +static const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; >> /* FAT: Offset of LFN characters in the directory entry */ >> +#define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) /* exFAT: Size >> of directory entry block scratchpad buffer needed for the name length */ >> + >> +#if FF_USE_LFN == 1 /* LFN enabled with static working buffer */ >> +#if FF_FS_EXFAT >> +static BYTE DirBuf[MAXDIRB(FF_MAX_LFN)]; /* Directory entry >> block scratchpad buffer */ >> +#endif >> +static WCHAR LfnBuf[FF_MAX_LFN + 1]; /* LFN working buffer */ >> +#define DEF_NAMBUF >> +#define INIT_NAMBUF(fs) >> +#define FREE_NAMBUF() >> +#define LEAVE_MKFS(res) return res >> + >> +#elif FF_USE_LFN == 2 /* LFN enabled with dynamic working buffer >> on the stack */ >> +#if FF_FS_EXFAT >> +#define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; BYTE >> dbuf[MAXDIRB(FF_MAX_LFN)]; /* LFN working buffer and directory >> entry block scratchpad buffer */ >> +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; } >> +#define FREE_NAMBUF() >> +#else >> +#define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; /* LFN working >> buffer */ >> +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; } >> +#define FREE_NAMBUF() >> +#endif >> +#define LEAVE_MKFS(res) return res >> + >> +#elif FF_USE_LFN == 3 /* LFN enabled with dynamic working buffer >> on the heap */ >> +#if FF_FS_EXFAT >> +#define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working >> buffer and directory entry block scratchpad buffer */ >> +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2 + >> MAXDIRB(FF_MAX_LFN)); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); >> (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+FF_MAX_LFN+1); } >> +#define FREE_NAMBUF() ff_memfree(lfn) >> +#else >> +#define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working >> buffer */ >> +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2); if >> (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; } >> +#define FREE_NAMBUF() ff_memfree(lfn) >> +#endif >> +#define LEAVE_MKFS(res) { if (!work) ff_memfree(buf); return res; } >> +#define MAX_MALLOC 0x8000 /* Must be >=FF_MAX_SS */ >> + >> +#else >> +#error Wrong setting of FF_USE_LFN >> + >> +#endif /* FF_USE_LFN == 1 */ >> +#endif /* FF_USE_LFN == 0 */ >> + >> + >> + >> +/*--------------------------------*/ >> +/* Code conversion tables */ >> +/*--------------------------------*/ >> + >> +#if FF_CODE_PAGE == 0 /* Run-time code page configuration */ >> +#define CODEPAGE CodePage >> +static WORD CodePage; /* Current code page */ >> +static const BYTE *ExCvt, *DbcTbl; /* Pointer to current SBCS >> up-case table and DBCS code range table below */ >> + >> +static const BYTE Ct437[] = TBL_CT437; >> +static const BYTE Ct720[] = TBL_CT720; >> +static const BYTE Ct737[] = TBL_CT737; >> +static const BYTE Ct771[] = TBL_CT771; >> +static const BYTE Ct775[] = TBL_CT775; >> +static const BYTE Ct850[] = TBL_CT850; >> +static const BYTE Ct852[] = TBL_CT852; >> +static const BYTE Ct855[] = TBL_CT855; >> +static const BYTE Ct857[] = TBL_CT857; >> +static const BYTE Ct860[] = TBL_CT860; >> +static const BYTE Ct861[] = TBL_CT861; >> +static const BYTE Ct862[] = TBL_CT862; >> +static const BYTE Ct863[] = TBL_CT863; >> +static const BYTE Ct864[] = TBL_CT864; >> +static const BYTE Ct865[] = TBL_CT865; >> +static const BYTE Ct866[] = TBL_CT866; >> +static const BYTE Ct869[] = TBL_CT869; >> +static const BYTE Dc932[] = TBL_DC932; >> +static const BYTE Dc936[] = TBL_DC936; >> +static const BYTE Dc949[] = TBL_DC949; >> +static const BYTE Dc950[] = TBL_DC950; >> + >> +#elif FF_CODE_PAGE < 900 /* Static code page configuration (SBCS) */ >> +#define CODEPAGE FF_CODE_PAGE >> +static const BYTE ExCvt[] = MKCVTBL(TBL_CT, FF_CODE_PAGE); >> + >> +#else /* Static code page configuration (DBCS) */ >> +#define CODEPAGE FF_CODE_PAGE >> +static const BYTE DbcTbl[] = MKCVTBL(TBL_DC, FF_CODE_PAGE); >> + >> +#endif >> + >> + >> + >> + >> +/*-------------------------------------------------------------------------- >> >> + >> + Module Private Functions >> + >> +---------------------------------------------------------------------------*/ >> >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Load/Store multi-byte word in the FAT >> structure */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static WORD ld_word (const BYTE* ptr) /* Load a 2-byte >> little-endian word */ >> +{ >> + WORD rv; >> + >> + rv = ptr[1]; >> + rv = rv << 8 | ptr[0]; >> + return rv; >> +} >> + >> +static DWORD ld_dword (const BYTE* ptr) /* Load a 4-byte >> little-endian word */ >> +{ >> + DWORD rv; >> + >> + rv = ptr[3]; >> + rv = rv << 8 | ptr[2]; >> + rv = rv << 8 | ptr[1]; >> + rv = rv << 8 | ptr[0]; >> + return rv; >> +} >> + >> +#if FF_FS_EXFAT >> +static QWORD ld_qword (const BYTE* ptr) /* Load an 8-byte >> little-endian word */ >> +{ >> + QWORD rv; >> + >> + rv = ptr[7]; >> + rv = rv << 8 | ptr[6]; >> + rv = rv << 8 | ptr[5]; >> + rv = rv << 8 | ptr[4]; >> + rv = rv << 8 | ptr[3]; >> + rv = rv << 8 | ptr[2]; >> + rv = rv << 8 | ptr[1]; >> + rv = rv << 8 | ptr[0]; >> + return rv; >> +} >> +#endif >> + >> +#if !FF_FS_READONLY >> +static void st_word (BYTE* ptr, WORD val) /* Store a 2-byte word >> in little-endian */ >> +{ >> + *ptr++ = (BYTE)val; val >>= 8; >> + *ptr++ = (BYTE)val; >> +} >> + >> +static void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte word >> in little-endian */ >> +{ >> + *ptr++ = (BYTE)val; val >>= 8; >> + *ptr++ = (BYTE)val; val >>= 8; >> + *ptr++ = (BYTE)val; val >>= 8; >> + *ptr++ = (BYTE)val; >> +} >> + >> +#if FF_FS_EXFAT >> +static void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte >> word in little-endian */ >> +{ >> + *ptr++ = (BYTE)val; val >>= 8; >> + *ptr++ = (BYTE)val; val >>= 8; >> + *ptr++ = (BYTE)val; val >>= 8; >> + *ptr++ = (BYTE)val; val >>= 8; >> + *ptr++ = (BYTE)val; val >>= 8; >> + *ptr++ = (BYTE)val; val >>= 8; >> + *ptr++ = (BYTE)val; val >>= 8; >> + *ptr++ = (BYTE)val; >> +} >> +#endif >> +#endif /* !FF_FS_READONLY */ >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* String >> functions */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +/* Copy memory to memory */ >> +static void mem_cpy (void* dst, const void* src, UINT cnt) >> +{ >> + BYTE *d = (BYTE*)dst; >> + const BYTE *s = (const BYTE*)src; >> + >> + if (cnt != 0) { >> + do { >> + *d++ = *s++; >> + } while (--cnt); >> + } >> +} >> + >> + >> +/* Fill memory block */ >> +static void mem_set (void* dst, int val, UINT cnt) >> +{ >> + BYTE *d = (BYTE*)dst; >> + >> + do { >> + *d++ = (BYTE)val; >> + } while (--cnt); >> +} >> + >> + >> +/* Compare memory block */ >> +static int mem_cmp (const void* dst, const void* src, UINT cnt) /* >> ZR:same, NZ:different */ >> +{ >> + const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src; >> + int r = 0; >> + >> + do { >> + r = *d++ - *s++; >> + } while (--cnt && r == 0); >> + >> + return r; >> +} >> + >> + >> +/* Check if chr is contained in the string */ >> +static int chk_chr (const char* str, int chr) /* NZ:contained, >> ZR:not contained */ >> +{ >> + while (*str && *str != chr) str++; >> + return *str; >> +} >> + >> + >> +/* Test if the byte is DBC 1st byte */ >> +static int dbc_1st (BYTE c) >> +{ >> +#if FF_CODE_PAGE == 0 /* Variable code page */ >> + if (DbcTbl && c >= DbcTbl[0]) { >> + if (c <= DbcTbl[1]) return 1; /* 1st byte >> range 1 */ >> + if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1; /* 1st >> byte range 2 */ >> + } >> +#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */ >> + if (c >= DbcTbl[0]) { >> + if (c <= DbcTbl[1]) return 1; >> + if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1; >> + } >> +#else /* SBCS fixed code page */ >> + if (c != 0) return 0; /* Always false */ >> +#endif >> + return 0; >> +} >> + >> + >> +/* Test if the byte is DBC 2nd byte */ >> +static int dbc_2nd (BYTE c) >> +{ >> +#if FF_CODE_PAGE == 0 /* Variable code page */ >> + if (DbcTbl && c >= DbcTbl[4]) { >> + if (c <= DbcTbl[5]) return 1; /* 2nd byte >> range 1 */ >> + if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1; /* 2nd >> byte range 2 */ >> + if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1; /* 2nd >> byte range 3 */ >> + } >> +#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */ >> + if (c >= DbcTbl[4]) { >> + if (c <= DbcTbl[5]) return 1; >> + if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1; >> + if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1; >> + } >> +#else /* SBCS fixed code page */ >> + if (c != 0) return 0; /* Always false */ >> +#endif >> + return 0; >> +} >> + >> + >> +#if FF_USE_LFN >> + >> +/* Get a Unicode code point from the TCHAR string in defined API >> encodeing */ >> +static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding >> (>=0x10000 on surrogate pair, 0xFFFFFFFF on decode error) */ >> + const TCHAR** str /* Pointer to pointer to TCHAR string in >> configured encoding */ >> +) >> +{ >> + DWORD uc; >> + const TCHAR *p = *str; >> + >> +#if FF_LFN_UNICODE == 1 /* UTF-16 input */ >> + WCHAR wc; >> + >> + uc = *p++; /* Get a unit */ >> + if (IsSurrogate(uc)) { /* Surrogate? */ >> + wc = *p++; /* Get low surrogate */ >> + if (!IsSurrogateH(uc) || !IsSurrogateL(wc)) return >> 0xFFFFFFFF; /* Wrong surrogate? */ >> + uc = uc << 16 | wc; >> + } >> + >> +#elif FF_LFN_UNICODE == 2 /* UTF-8 input */ >> + BYTE b; >> + int nf; >> + >> + uc = (BYTE)*p++; /* Get an encoding unit */ >> + if (uc & 0x80) { /* Multiple byte code? */ >> + if ((uc & 0xE0) == 0xC0) { /* 2-byte sequence? */ >> + uc &= 0x1F; nf = 1; >> + } else { >> + if ((uc & 0xF0) == 0xE0) { /* 3-byte sequence? */ >> + uc &= 0x0F; nf = 2; >> + } else { >> + if ((uc & 0xF8) == 0xF0) { /* 4-byte sequence? */ >> + uc &= 0x07; nf = 3; >> + } else { /* Wrong sequence */ >> + return 0xFFFFFFFF; >> + } >> + } >> + } >> + do { /* Get trailing bytes */ >> + b = (BYTE)*p++; >> + if ((b & 0xC0) != 0x80) return 0xFFFFFFFF; /* Wrong >> sequence? */ >> + uc = uc << 6 | (b & 0x3F); >> + } while (--nf != 0); >> + if (uc < 0x80 || IsSurrogate(uc) || uc >= 0x110000) return >> 0xFFFFFFFF; /* Wrong code? */ >> + if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & >> 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */ >> + } >> + >> +#elif FF_LFN_UNICODE == 3 /* UTF-32 input */ >> + uc = (TCHAR)*p++; /* Get a unit */ >> + if (uc >= 0x110000 || IsSurrogate(uc)) return 0xFFFFFFFF; /* >> Wrong code? */ >> + if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & >> 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */ >> + >> +#else /* ANSI/OEM input */ >> + BYTE b; >> + WCHAR wc; >> + >> + wc = (BYTE)*p++; /* Get a byte */ >> + if (dbc_1st((BYTE)wc)) { /* Is it a DBC 1st byte? */ >> + b = (BYTE)*p++; /* Get 2nd byte */ >> + if (!dbc_2nd(b)) return 0xFFFFFFFF; /* Invalid code? */ >> + wc = (wc << 8) + b; /* Make a DBC */ >> + } >> + if (wc != 0) { >> + wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM ==> Unicode */ >> + if (wc == 0) return 0xFFFFFFFF; /* Invalid code? */ >> + } >> + uc = wc; >> + >> +#endif >> + *str = p; /* Next read pointer */ >> + return uc; >> +} >> + >> + >> +/* Output a TCHAR string in defined API encoding */ >> +static BYTE put_utf ( /* Returns number of encoding units written >> (0:buffer overflow or wrong encoding) */ >> + DWORD chr, /* UTF-16 encoded character (Surrogate pair if >> >=0x10000) */ >> + TCHAR* buf, /* Output buffer */ >> + UINT szb /* Size of the buffer */ >> +) >> +{ >> +#if FF_LFN_UNICODE == 1 /* UTF-16 output */ >> + WCHAR hs, wc; >> + >> + hs = (WCHAR)(chr >> 16); >> + wc = (WCHAR)chr; >> + if (hs == 0) { /* Single encoding unit? */ >> + if (szb < 1 || IsSurrogate(wc)) return 0; /* Buffer >> overflow or wrong code? */ >> + *buf = wc; >> + return 1; >> + } >> + if (szb < 2 || !IsSurrogateH(hs) || !IsSurrogateL(wc)) return >> 0; /* Buffer overflow or wrong surrogate? */ >> + *buf++ = hs; >> + *buf++ = wc; >> + return 2; >> + >> +#elif FF_LFN_UNICODE == 2 /* UTF-8 output */ >> + DWORD hc; >> + >> + if (chr < 0x80) { /* Single byte code? */ >> + if (szb < 1) return 0; /* Buffer overflow? */ >> + *buf = (TCHAR)chr; >> + return 1; >> + } >> + if (chr < 0x800) { /* 2-byte sequence? */ >> + if (szb < 2) return 0; /* Buffer overflow? */ >> + *buf++ = (TCHAR)(0xC0 | (chr >> 6 & 0x1F)); >> + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); >> + return 2; >> + } >> + if (chr < 0x10000) { /* 3-byte sequence? */ >> + if (szb < 3 || IsSurrogate(chr)) return 0; /* Buffer >> overflow or wrong code? */ >> + *buf++ = (TCHAR)(0xE0 | (chr >> 12 & 0x0F)); >> + *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F)); >> + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); >> + return 3; >> + } >> + /* 4-byte sequence */ >> + if (szb < 4) return 0; /* Buffer overflow? */ >> + hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high 10 >> bits */ >> + chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 >> bits */ >> + if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong >> surrogate? */ >> + chr = (hc | chr) + 0x10000; >> + *buf++ = (TCHAR)(0xF0 | (chr >> 18 & 0x07)); >> + *buf++ = (TCHAR)(0x80 | (chr >> 12 & 0x3F)); >> + *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F)); >> + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); >> + return 4; >> + >> +#elif FF_LFN_UNICODE == 3 /* UTF-32 output */ >> + DWORD hc; >> + >> + if (szb < 1) return 0; /* Buffer overflow? */ >> + if (chr >= 0x10000) { /* Out of BMP? */ >> + hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high >> 10 bits */ >> + chr = (chr & 0xFFFF) - 0xDC00; /* Get low >> 10 bits */ >> + if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong >> surrogate? */ >> + chr = (hc | chr) + 0x10000; >> + } >> + *buf++ = (TCHAR)chr; >> + return 1; >> + >> +#else /* ANSI/OEM output */ >> + WCHAR wc; >> + >> + wc = ff_uni2oem(chr, CODEPAGE); >> + if (wc >= 0x100) { /* Is this a DBC? */ >> + if (szb < 2) return 0; >> + *buf++ = (char)(wc >> 8); /* Store DBC 1st byte */ >> + *buf++ = (TCHAR)wc; /* Store DBC 2nd byte */ >> + return 2; >> + } >> + if (wc == 0 || szb < 1) return 0; /* Invalid char or buffer >> overflow? */ >> + *buf++ = (TCHAR)wc; /* Store the character */ >> + return 1; >> +#endif >> +} >> +#endif /* FF_USE_LFN */ >> + >> + >> +#if FF_FS_REENTRANT >> +/*-----------------------------------------------------------------------*/ >> >> +/* Request/Release grant to access the >> volume */ >> +/*-----------------------------------------------------------------------*/ >> >> +static int lock_fs ( /* 1:Ok, 0:timeout */ >> + FATFS* fs /* Filesystem object */ >> +) >> +{ >> + return ff_req_grant(fs->sobj); >> +} >> + >> + >> +static void unlock_fs ( >> + FATFS* fs, /* Filesystem object */ >> + FRESULT res /* Result code to be returned */ >> +) >> +{ >> + if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res >> != FR_TIMEOUT) { >> + ff_rel_grant(fs->sobj); >> + } >> +} >> + >> +#endif >> + >> + >> + >> +#if FF_FS_LOCK != 0 >> +/*-----------------------------------------------------------------------*/ >> >> +/* File lock control >> functions */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static FRESULT chk_lock ( /* Check if the file can be accessed */ >> + DIR* dp, /* Directory object pointing the file to be >> checked */ >> + int acc /* Desired access type (0:Read mode open, >> 1:Write mode open, 2:Delete or rename) */ >> +) >> +{ >> + UINT i, be; >> + >> + /* Search open object table for the object */ >> + be = 0; >> + for (i = 0; i < FF_FS_LOCK; i++) { >> + if (Files[i].fs) { /* Existing entry */ >> + if (Files[i].fs == dp->obj.fs && /* Check if the >> object matches with an open object */ >> + Files[i].clu == dp->obj.sclust && >> + Files[i].ofs == dp->dptr) break; >> + } else { /* Blank entry */ >> + be = 1; >> + } >> + } >> + if (i == FF_FS_LOCK) { /* The object has not been opened */ >> + return (!be && acc != 2) ? FR_TOO_MANY_OPEN_FILES : FR_OK; >> /* Is there a blank entry for new object? */ >> + } >> + >> + /* The object was opened. Reject any open against writing file >> and all write mode open */ >> + return (acc != 0 || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; >> +} >> + >> + >> +static int enq_lock (void) /* Check if an entry is available for a >> new object */ >> +{ >> + UINT i; >> + >> + for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ; >> + return (i == FF_FS_LOCK) ? 0 : 1; >> +} >> + >> + >> +static UINT inc_lock ( /* Increment object open counter and >> returns its index (0:Internal error) */ >> + DIR* dp, /* Directory object pointing the file to register or >> increment */ >> + int acc /* Desired access (0:Read, 1:Write, >> 2:Delete/Rename) */ >> +) >> +{ >> + UINT i; >> + >> + >> + for (i = 0; i < FF_FS_LOCK; i++) { /* Find the object */ >> + if (Files[i].fs == dp->obj.fs >> + && Files[i].clu == dp->obj.sclust >> + && Files[i].ofs == dp->dptr) break; >> + } >> + >> + if (i == FF_FS_LOCK) { /* Not opened. Register it >> as new. */ >> + for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ; >> + if (i == FF_FS_LOCK) return 0; /* No free entry to >> register (int err) */ >> + Files[i].fs = dp->obj.fs; >> + Files[i].clu = dp->obj.sclust; >> + Files[i].ofs = dp->dptr; >> + Files[i].ctr = 0; >> + } >> + >> + if (acc >= 1 && Files[i].ctr) return 0; /* Access violation >> (int err) */ >> + >> + Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set >> semaphore value */ >> + >> + return i + 1; /* Index number origin from 1 */ >> +} >> + >> + >> +static FRESULT dec_lock ( /* Decrement object open counter */ >> + UINT i /* Semaphore index (1..) */ >> +) >> +{ >> + WORD n; >> + FRESULT res; >> + >> + >> + if (--i < FF_FS_LOCK) { /* Index number origin from 0 */ >> + n = Files[i].ctr; >> + if (n == 0x100) n = 0; /* If write mode open, delete >> the entry */ >> + if (n > 0) n--; /* Decrement read mode open >> count */ >> + Files[i].ctr = n; >> + if (n == 0) Files[i].fs = 0; /* Delete the entry if open >> count gets zero */ >> + res = FR_OK; >> + } else { >> + res = FR_INT_ERR; /* Invalid index nunber */ >> + } >> + return res; >> +} >> + >> + >> +static void clear_lock ( /* Clear lock entries of the volume */ >> + FATFS *fs >> +) >> +{ >> + UINT i; >> + >> + for (i = 0; i < FF_FS_LOCK; i++) { >> + if (Files[i].fs == fs) Files[i].fs = 0; >> + } >> +} >> + >> +#endif /* FF_FS_LOCK != 0 */ >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Move/Flush disk access window in the filesystem >> object */ >> +/*-----------------------------------------------------------------------*/ >> >> +#if !FF_FS_READONLY >> +static FRESULT sync_window ( /* Returns FR_OK or FR_DISK_ERR */ >> + FATFS* fs /* Filesystem object */ >> +) >> +{ >> + FRESULT res = FR_OK; >> + >> + >> + if (fs->wflag) { /* Is the disk access window dirty? */ >> + if (disk_write(fs->pdrv, fs->win, fs->winsect, 1) == RES_OK) >> { /* Write it back into the volume */ >> + fs->wflag = 0; /* Clear window dirty flag */ >> + if (fs->winsect - fs->fatbase < fs->fsize) { /* Is it >> in the 1st FAT? */ >> + if (fs->n_fats == 2) disk_write(fs->pdrv, fs->win, >> fs->winsect + fs->fsize, 1); /* Reflect it to 2nd FAT if needed */ >> + } >> + } else { >> + res = FR_DISK_ERR; >> + } >> + } >> + return res; >> +} >> +#endif >> + >> + >> +static FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERR */ >> + FATFS* fs, /* Filesystem object */ >> + LBA_t sect /* Sector LBA to make appearance in the >> fs->win[] */ >> +) >> +{ >> + FRESULT res = FR_OK; >> + >> + >> + if (sect != fs->winsect) { /* Window offset changed? */ >> +#if !FF_FS_READONLY >> + res = sync_window(fs); /* Flush the window */ >> +#endif >> + if (res == FR_OK) { /* Fill sector window with new >> data */ >> + if (disk_read(fs->pdrv, fs->win, sect, 1) != RES_OK) { >> + sect = (LBA_t)0 - 1; /* Invalidate window if read >> data is not valid */ >> + res = FR_DISK_ERR; >> + } >> + fs->winsect = sect; >> + } >> + } >> + return res; >> +} >> + >> + >> + >> + >> +#if !FF_FS_READONLY >> +/*-----------------------------------------------------------------------*/ >> >> +/* Synchronize filesystem and data on the >> storage */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */ >> + FATFS* fs /* Filesystem object */ >> +) >> +{ >> + FRESULT res; >> + >> + >> + res = sync_window(fs); >> + if (res == FR_OK) { >> + if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { /* >> FAT32: Update FSInfo sector if needed */ >> + /* Create FSInfo structure */ >> + mem_set(fs->win, 0, sizeof fs->win); >> + st_word(fs->win + BS_55AA, 0xAA55); /* >> Boot signature */ >> + st_dword(fs->win + FSI_LeadSig, 0x41615252); /* >> Leading signature */ >> + st_dword(fs->win + FSI_StrucSig, 0x61417272); /* >> Structure signature */ >> + st_dword(fs->win + FSI_Free_Count, fs->free_clst); /* >> Number of free clusters */ >> + st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); /* >> Last allocated culuster */ >> + fs->winsect = fs->volbase + 1; /* >> Write it into the FSInfo sector (Next to VBR) */ >> + disk_write(fs->pdrv, fs->win, fs->winsect, 1); >> + fs->fsi_flag = 0; >> + } >> + /* Make sure that no pending write process in the lower layer */ >> + if (disk_ioctl(fs->pdrv, CTRL_SYNC, 0) != RES_OK) res = >> FR_DISK_ERR; >> + } >> + >> + return res; >> +} >> + >> +#endif >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Get physical sector number from cluster >> number */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static LBA_t clst2sect ( /* !=0:Sector number, 0:Failed (invalid >> cluster#) */ >> + FATFS* fs, /* Filesystem object */ >> + DWORD clst /* Cluster# to be converted */ >> +) >> +{ >> + clst -= 2; /* Cluster number is origin from 2 */ >> + if (clst >= fs->n_fatent - 2) return 0; /* Is it invalid >> cluster number? */ >> + return fs->database + (LBA_t)fs->csize * clst; /* Start sector >> number of the cluster */ >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* FAT access - Read value of a FAT >> entry */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal >> error, 2..0x7FFFFFFF:Cluster status */ >> + FFOBJID* obj, /* Corresponding object */ >> + DWORD clst /* Cluster number to get the value */ >> +) >> +{ >> + UINT wc, bc; >> + DWORD val; >> + FATFS *fs = obj->fs; >> + >> + >> + if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid >> range */ >> + val = 1; /* Internal error */ >> + >> + } else { >> + val = 0xFFFFFFFF; /* Default value falls on disk error */ >> + >> + switch (fs->fs_type) { >> + case FS_FAT12 : >> + bc = (UINT)clst; bc += bc / 2; >> + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != >> FR_OK) break; >> + wc = fs->win[bc++ % SS(fs)]; /* Get 1st byte of >> the entry */ >> + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != >> FR_OK) break; >> + wc |= fs->win[bc % SS(fs)] << 8; /* Merge 2nd byte of >> the entry */ >> + val = (clst & 1) ? (wc >> 4) : (wc & 0xFFF); /* Adjust >> bit position */ >> + break; >> + >> + case FS_FAT16 : >> + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) >> != FR_OK) break; >> + val = ld_word(fs->win + clst * 2 % SS(fs)); /* >> Simple WORD array */ >> + break; >> + >> + case FS_FAT32 : >> + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) >> != FR_OK) break; >> + val = ld_dword(fs->win + clst * 4 % SS(fs)) & >> 0x0FFFFFFF; /* Simple DWORD array but mask out upper 4 bits */ >> + break; >> +#if FF_FS_EXFAT >> + case FS_EXFAT : >> + if ((obj->objsize != 0 && obj->sclust != 0) || obj->stat >> == 0) { /* Object except root dir must have valid data length */ >> + DWORD cofs = clst - obj->sclust; /* Offset from >> start cluster */ >> + DWORD clen = (DWORD)((LBA_t)((obj->objsize - 1) / >> SS(fs)) / fs->csize); /* Number of clusters - 1 */ >> + >> + if (obj->stat == 2 && cofs <= clen) { /* Is it a >> contiguous chain? */ >> + val = (cofs == clen) ? 0x7FFFFFFF : clst + 1; >> /* No data on the FAT, generate the value */ >> + break; >> + } >> + if (obj->stat == 3 && cofs < obj->n_cont) { /* Is >> it in the 1st fragment? */ >> + val = clst + 1; /* Generate the value */ >> + break; >> + } >> + if (obj->stat != 2) { /* Get value from FAT if FAT >> chain is valid */ >> + if (obj->n_frag != 0) { /* Is it on the >> growing edge? */ >> + val = 0x7FFFFFFF; /* Generate EOC */ >> + } else { >> + if (move_window(fs, fs->fatbase + (clst / >> (SS(fs) / 4))) != FR_OK) break; >> + val = ld_dword(fs->win + clst * 4 % SS(fs)) & >> 0x7FFFFFFF; >> + } >> + break; >> + } >> + } >> + val = 1; /* Internal error */ >> + break; >> +#endif >> + default: >> + val = 1; /* Internal error */ >> + } >> + } >> + >> + return val; >> +} >> + >> + >> + >> + >> +#if !FF_FS_READONLY >> +/*-----------------------------------------------------------------------*/ >> >> +/* FAT access - Change value of a FAT >> entry */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ >> + FATFS* fs, /* Corresponding filesystem object */ >> + DWORD clst, /* FAT index number (cluster number) to be >> changed */ >> + DWORD val /* New value to be set to the entry */ >> +) >> +{ >> + UINT bc; >> + BYTE *p; >> + FRESULT res = FR_INT_ERR; >> + >> + >> + if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid >> range */ >> + switch (fs->fs_type) { >> + case FS_FAT12: >> + bc = (UINT)clst; bc += bc / 2; /* bc: byte offset of >> the entry */ >> + res = move_window(fs, fs->fatbase + (bc / SS(fs))); >> + if (res != FR_OK) break; >> + p = fs->win + bc++ % SS(fs); >> + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : >> (BYTE)val; /* Update 1st byte */ >> + fs->wflag = 1; >> + res = move_window(fs, fs->fatbase + (bc / SS(fs))); >> + if (res != FR_OK) break; >> + p = fs->win + bc % SS(fs); >> + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | >> ((BYTE)(val >> 8) & 0x0F)); /* Update 2nd byte */ >> + fs->wflag = 1; >> + break; >> + >> + case FS_FAT16: >> + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); >> + if (res != FR_OK) break; >> + st_word(fs->win + clst * 2 % SS(fs), (WORD)val); /* >> Simple WORD array */ >> + fs->wflag = 1; >> + break; >> + >> + case FS_FAT32: >> +#if FF_FS_EXFAT >> + case FS_EXFAT: >> +#endif >> + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); >> + if (res != FR_OK) break; >> + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { >> + val = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst * >> 4 % SS(fs)) & 0xF0000000); >> + } >> + st_dword(fs->win + clst * 4 % SS(fs), val); >> + fs->wflag = 1; >> + break; >> + } >> + } >> + return res; >> +} >> + >> +#endif /* !FF_FS_READONLY */ >> + >> + >> + >> + >> +#if FF_FS_EXFAT && !FF_FS_READONLY >> +/*-----------------------------------------------------------------------*/ >> >> +/* exFAT: Accessing FAT and Allocation >> Bitmap */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +/*--------------------------------------*/ >> +/* Find a contiguous free cluster block */ >> +/*--------------------------------------*/ >> + >> +static DWORD find_bitmap ( /* 0:Not found, 2..:Cluster block >> found, 0xFFFFFFFF:Disk error */ >> + FATFS* fs, /* Filesystem object */ >> + DWORD clst, /* Cluster number to scan from */ >> + DWORD ncl /* Number of contiguous clusters to find (1..) */ >> +) >> +{ >> + BYTE bm, bv; >> + UINT i; >> + DWORD val, scl, ctr; >> + >> + >> + clst -= 2; /* The first bit in the bitmap corresponds to >> cluster #2 */ >> + if (clst >= fs->n_fatent - 2) clst = 0; >> + scl = val = clst; ctr = 0; >> + for (;;) { >> + if (move_window(fs, fs->bitbase + val / 8 / SS(fs)) != FR_OK) >> return 0xFFFFFFFF; >> + i = val / 8 % SS(fs); bm = 1 << (val % 8); >> + do { >> + do { >> + bv = fs->win[i] & bm; bm <<= 1; /* Get bit >> value */ >> + if (++val >= fs->n_fatent - 2) { /* Next cluster >> (with wrap-around) */ >> + val = 0; bm = 0; i = SS(fs); >> + } >> + if (bv == 0) { /* Is it a free cluster? */ >> + if (++ctr == ncl) return scl + 2; /* Check if >> run length is sufficient for required */ >> + } else { >> + scl = val; ctr = 0; /* Encountered a >> cluster in-use, restart to scan */ >> + } >> + if (val == clst) return 0; /* All cluster scanned? */ >> + } while (bm != 0); >> + bm = 1; >> + } while (++i < SS(fs)); >> + } >> +} >> + >> + >> +/*----------------------------------------*/ >> +/* Set/Clear a block of allocation bitmap */ >> +/*----------------------------------------*/ >> + >> +static FRESULT change_bitmap ( >> + FATFS* fs, /* Filesystem object */ >> + DWORD clst, /* Cluster number to change from */ >> + DWORD ncl, /* Number of clusters to be changed */ >> + int bv /* bit value to be set (0 or 1) */ >> +) >> +{ >> + BYTE bm; >> + UINT i; >> + LBA_t sect; >> + >> + >> + clst -= 2; /* The first bit corresponds to cluster #2 */ >> + sect = fs->bitbase + clst / 8 / SS(fs); /* Sector address */ >> + i = clst / 8 % SS(fs); /* Byte offset in the >> sector */ >> + bm = 1 << (clst % 8); /* Bit mask in the byte */ >> + for (;;) { >> + if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR; >> + do { >> + do { >> + if (bv == (int)((fs->win[i] & bm) != 0)) return >> FR_INT_ERR; /* Is the bit expected value? */ >> + fs->win[i] ^= bm; /* Flip the bit */ >> + fs->wflag = 1; >> + if (--ncl == 0) return FR_OK; /* All bits >> processed? */ >> + } while (bm <<= 1); /* Next bit */ >> + bm = 1; >> + } while (++i < SS(fs)); /* Next byte */ >> + i = 0; >> + } >> +} >> + >> + >> +/*---------------------------------------------*/ >> +/* Fill the first fragment of the FAT chain */ >> +/*---------------------------------------------*/ >> + >> +static FRESULT fill_first_frag ( >> + FFOBJID* obj /* Pointer to the corresponding object */ >> +) >> +{ >> + FRESULT res; >> + DWORD cl, n; >> + >> + >> + if (obj->stat == 3) { /* Has the object been changed >> 'fragmented' in this session? */ >> + for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) { /* >> Create cluster chain on the FAT */ >> + res = put_fat(obj->fs, cl, cl + 1); >> + if (res != FR_OK) return res; >> + } >> + obj->stat = 0; /* Change status 'FAT chain is valid' */ >> + } >> + return FR_OK; >> +} >> + >> + >> +/*---------------------------------------------*/ >> +/* Fill the last fragment of the FAT chain */ >> +/*---------------------------------------------*/ >> + >> +static FRESULT fill_last_frag ( >> + FFOBJID* obj, /* Pointer to the corresponding object */ >> + DWORD lcl, /* Last cluster of the fragment */ >> + DWORD term /* Value to set the last FAT entry */ >> +) >> +{ >> + FRESULT res; >> + >> + >> + while (obj->n_frag > 0) { /* Create the chain of last fragment */ >> + res = put_fat(obj->fs, lcl - obj->n_frag + 1, (obj->n_frag > >> 1) ? lcl - obj->n_frag + 2 : term); >> + if (res != FR_OK) return res; >> + obj->n_frag--; >> + } >> + return FR_OK; >> +} >> + >> +#endif /* FF_FS_EXFAT && !FF_FS_READONLY */ >> + >> + >> + >> +#if !FF_FS_READONLY >> +/*-----------------------------------------------------------------------*/ >> >> +/* FAT handling - Remove a cluster >> chain */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ >> + FFOBJID* obj, /* Corresponding object */ >> + DWORD clst, /* Cluster to remove a chain from */ >> + DWORD pclst /* Previous cluster of clst (0 if entire >> chain) */ >> +) >> +{ >> + FRESULT res = FR_OK; >> + DWORD nxt; >> + FATFS *fs = obj->fs; >> +#if FF_FS_EXFAT || FF_USE_TRIM >> + DWORD scl = clst, ecl = clst; >> +#endif >> +#if FF_USE_TRIM >> + LBA_t rt[2]; >> +#endif >> + >> + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* >> Check if in valid range */ >> + >> + /* Mark the previous cluster 'EOC' on the FAT if it exists */ >> + if (pclst != 0 && (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT || >> obj->stat != 2)) { >> + res = put_fat(fs, pclst, 0xFFFFFFFF); >> + if (res != FR_OK) return res; >> + } >> + >> + /* Remove the chain */ >> + do { >> + nxt = get_fat(obj, clst); /* Get cluster status */ >> + if (nxt == 0) break; /* Empty cluster? */ >> + if (nxt == 1) return FR_INT_ERR; /* Internal error? */ >> + if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error? */ >> + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { >> + res = put_fat(fs, clst, 0); /* Mark the cluster >> 'free' on the FAT */ >> + if (res != FR_OK) return res; >> + } >> + if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */ >> + fs->free_clst++; >> + fs->fsi_flag |= 1; >> + } >> +#if FF_FS_EXFAT || FF_USE_TRIM >> + if (ecl + 1 == nxt) { /* Is next cluster contiguous? */ >> + ecl = nxt; >> + } else { /* End of contiguous cluster block */ >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { >> + res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* >> Mark the cluster block 'free' on the bitmap */ >> + if (res != FR_OK) return res; >> + } >> +#endif >> +#if FF_USE_TRIM >> + rt[0] = clst2sect(fs, scl); /* Start >> of data area to be freed */ >> + rt[1] = clst2sect(fs, ecl) + fs->csize - 1; /* End of >> data area to be freed */ >> + disk_ioctl(fs->pdrv, CTRL_TRIM, rt); /* Inform >> storage device that the data in the block may be erased */ >> +#endif >> + scl = ecl = nxt; >> + } >> +#endif >> + clst = nxt; /* Next cluster */ >> + } while (clst < fs->n_fatent); /* Repeat while not the last >> link */ >> + >> +#if FF_FS_EXFAT >> + /* Some post processes for chain status */ >> + if (fs->fs_type == FS_EXFAT) { >> + if (pclst == 0) { /* Has the entire chain been removed? */ >> + obj->stat = 0; /* Change the chain status >> 'initial' */ >> + } else { >> + if (obj->stat == 0) { /* Is it a fragmented chain from >> the beginning of this session? */ >> + clst = obj->sclust; /* Follow the chain to >> check if it gets contiguous */ >> + while (clst != pclst) { >> + nxt = get_fat(obj, clst); >> + if (nxt < 2) return FR_INT_ERR; >> + if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; >> + if (nxt != clst + 1) break; /* Not contiguous? */ >> + clst++; >> + } >> + if (clst == pclst) { /* Has the chain got >> contiguous again? */ >> + obj->stat = 2; /* Change the chain status >> 'contiguous' */ >> + } >> + } else { >> + if (obj->stat == 3 && pclst >= obj->sclust && pclst >> <= obj->sclust + obj->n_cont) { /* Was the chain fragmented in this >> session and got contiguous again? */ >> + obj->stat = 2; /* Change the chain status >> 'contiguous' */ >> + } >> + } >> + } >> + } >> +#endif >> + return FR_OK; >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* FAT handling - Stretch a chain or Create a new >> chain */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static DWORD create_chain ( /* 0:No free cluster, 1:Internal >> error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ >> + FFOBJID* obj, /* Corresponding object */ >> + DWORD clst /* Cluster# to stretch, 0:Create a new >> chain */ >> +) >> +{ >> + DWORD cs, ncl, scl; >> + FRESULT res; >> + FATFS *fs = obj->fs; >> + >> + >> + if (clst == 0) { /* Create a new chain */ >> + scl = fs->last_clst; /* Suggested cluster to >> start to find */ >> + if (scl == 0 || scl >= fs->n_fatent) scl = 1; >> + } >> + else { /* Stretch a chain */ >> + cs = get_fat(obj, clst); /* Check the cluster >> status */ >> + if (cs < 2) return 1; /* Test for insanity */ >> + if (cs == 0xFFFFFFFF) return cs; /* Test for disk error */ >> + if (cs < fs->n_fatent) return cs; /* It is already >> followed by next cluster */ >> + scl = clst; /* Cluster to start to >> find */ >> + } >> + if (fs->free_clst == 0) return 0; /* No free cluster */ >> + >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ >> + ncl = find_bitmap(fs, scl, 1); /* Find a free >> cluster */ >> + if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl; /* No free >> cluster or hard error? */ >> + res = change_bitmap(fs, ncl, 1, 1); /* Mark the >> cluster 'in use' */ >> + if (res == FR_INT_ERR) return 1; >> + if (res == FR_DISK_ERR) return 0xFFFFFFFF; >> + if (clst == 0) { /* Is it a new >> chain? */ >> + obj->stat = 2; /* Set status >> 'contiguous' */ >> + } else { /* It is a >> stretched chain */ >> + if (obj->stat == 2 && ncl != scl + 1) { /* Is the >> chain got fragmented? */ >> + obj->n_cont = scl - obj->sclust; /* Set size of >> the contiguous part */ >> + obj->stat = 3; /* Change >> status 'just fragmented' */ >> + } >> + } >> + if (obj->stat != 2) { /* Is the file non-contiguous? */ >> + if (ncl == clst + 1) { /* Is the cluster next to >> previous one? */ >> + obj->n_frag = obj->n_frag ? obj->n_frag + 1 : 2; >> /* Increment size of last framgent */ >> + } else { /* New fragment */ >> + if (obj->n_frag == 0) obj->n_frag = 1; >> + res = fill_last_frag(obj, clst, ncl); /* Fill last >> fragment on the FAT and link it to new one */ >> + if (res == FR_OK) obj->n_frag = 1; >> + } >> + } >> + } else >> +#endif >> + { /* On the FAT/FAT32 volume */ >> + ncl = 0; >> + if (scl == clst) { /* Stretching an >> existing chain? */ >> + ncl = scl + 1; /* Test if next >> cluster is free */ >> + if (ncl >= fs->n_fatent) ncl = 2; >> + cs = get_fat(obj, ncl); /* Get next >> cluster status */ >> + if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* Test >> for error */ >> + if (cs != 0) { /* Not free? */ >> + cs = fs->last_clst; /* Start at >> suggested cluster if it is valid */ >> + if (cs >= 2 && cs < fs->n_fatent) scl = cs; >> + ncl = 0; >> + } >> + } >> + if (ncl == 0) { /* The new cluster cannot be contiguous >> and find another fragment */ >> + ncl = scl; /* Start cluster */ >> + for (;;) { >> + ncl++; /* Next cluster */ >> + if (ncl >= fs->n_fatent) { /* Check >> wrap-around */ >> + ncl = 2; >> + if (ncl > scl) return 0; /* No free cluster >> found? */ >> + } >> + cs = get_fat(obj, ncl); /* Get the cluster >> status */ >> + if (cs == 0) break; /* Found a free >> cluster? */ >> + if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* >> Test for error */ >> + if (ncl == scl) return 0; /* No free cluster >> found? */ >> + } >> + } >> + res = put_fat(fs, ncl, 0xFFFFFFFF); /* Mark the new >> cluster 'EOC' */ >> + if (res == FR_OK && clst != 0) { >> + res = put_fat(fs, clst, ncl); /* Link it from the >> previous one if needed */ >> + } >> + } >> + >> + if (res == FR_OK) { /* Update FSINFO if function >> succeeded. */ >> + fs->last_clst = ncl; >> + if (fs->free_clst <= fs->n_fatent - 2) fs->free_clst--; >> + fs->fsi_flag |= 1; >> + } else { >> + ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; /* Failed. >> Generate error status */ >> + } >> + >> + return ncl; /* Return new cluster number or error status */ >> +} >> + >> +#endif /* !FF_FS_READONLY */ >> + >> + >> + >> + >> +#if FF_USE_FASTSEEK >> +/*-----------------------------------------------------------------------*/ >> >> +/* FAT handling - Convert offset into cluster with link map >> table */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ >> + FIL* fp, /* Pointer to the file object */ >> + FSIZE_t ofs /* File offset to be converted to cluster# */ >> +) >> +{ >> + DWORD cl, ncl, *tbl; >> + FATFS *fs = fp->obj.fs; >> + >> + >> + tbl = fp->cltbl + 1; /* Top of CLMT */ >> + cl = (DWORD)(ofs / SS(fs) / fs->csize); /* Cluster order from >> top of the file */ >> + for (;;) { >> + ncl = *tbl++; /* Number of cluters in the fragment */ >> + if (ncl == 0) return 0; /* End of table? (error) */ >> + if (cl < ncl) break; /* In this fragment? */ >> + cl -= ncl; tbl++; /* Next fragment */ >> + } >> + return cl + *tbl; /* Return the cluster number */ >> +} >> + >> +#endif /* FF_USE_FASTSEEK */ >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Directory handling - Fill a cluster with >> zeros */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +#if !FF_FS_READONLY >> +static FRESULT dir_clear ( /* Returns FR_OK or FR_DISK_ERR */ >> + FATFS *fs, /* Filesystem object */ >> + DWORD clst /* Directory table to clear */ >> +) >> +{ >> + LBA_t sect; >> + UINT n, szb; >> + BYTE *ibuf; >> + >> + >> + if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush >> disk access window */ >> + sect = clst2sect(fs, clst); /* Top of the cluster */ >> + fs->winsect = sect; /* Set window to top of the >> cluster */ >> + mem_set(fs->win, 0, sizeof fs->win); /* Clear window buffer */ >> +#if FF_USE_LFN == 3 /* Quick table clear by using multi-secter >> write */ >> + /* Allocate a temporary buffer */ >> + for (szb = ((DWORD)fs->csize * SS(fs) >= MAX_MALLOC) ? MAX_MALLOC >> : fs->csize * SS(fs), ibuf = 0; szb > SS(fs) && (ibuf = >> ff_memalloc(szb)) == 0; szb /= 2) ; >> + if (szb > SS(fs)) { /* Buffer allocated? */ >> + mem_set(ibuf, 0, szb); >> + szb /= SS(fs); /* Bytes -> Sectors */ >> + for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect >> + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */ >> + ff_memfree(ibuf); >> + } else >> +#endif >> + { >> + ibuf = fs->win; szb = 1; /* Use window buffer (many >> single-sector writes may take a time) */ >> + for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect >> + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */ >> + } >> + return (n == fs->csize) ? FR_OK : FR_DISK_ERR; >> +} >> +#endif /* !FF_FS_READONLY */ >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Directory handling - Set directory >> index */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */ >> + DIR* dp, /* Pointer to directory object */ >> + DWORD ofs /* Offset of directory table */ >> +) >> +{ >> + DWORD csz, clst; >> + FATFS *fs = dp->obj.fs; >> + >> + >> + if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? >> MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) { /* Check range of offset >> and alignment */ >> + return FR_INT_ERR; >> + } >> + dp->dptr = ofs; /* Set current offset */ >> + clst = dp->obj.sclust; /* Table start cluster (0:root) */ >> + if (clst == 0 && fs->fs_type >= FS_FAT32) { /* Replace >> cluster# 0 with root cluster# */ >> + clst = (DWORD)fs->dirbase; >> + if (FF_FS_EXFAT) dp->obj.stat = 0; /* exFAT: Root dir has >> an FAT chain */ >> + } >> + >> + if (clst == 0) { /* Static table (root-directory on the FAT >> volume) */ >> + if (ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR; /* >> Is index out of range? */ >> + dp->sect = fs->dirbase; >> + >> + } else { /* Dynamic table (sub-directory or >> root-directory on the FAT32/exFAT volume) */ >> + csz = (DWORD)fs->csize * SS(fs); /* Bytes per cluster */ >> + while (ofs >= csz) { /* Follow cluster chain */ >> + clst = get_fat(&dp->obj, clst); /* Get >> next cluster */ >> + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk >> error */ >> + if (clst < 2 || clst >= fs->n_fatent) return >> FR_INT_ERR; /* Reached to end of table or internal error */ >> + ofs -= csz; >> + } >> + dp->sect = clst2sect(fs, clst); >> + } >> + dp->clust = clst; /* Current cluster# */ >> + if (dp->sect == 0) return FR_INT_ERR; >> + dp->sect += ofs / SS(fs); /* Sector# of the directory >> entry */ >> + dp->dir = fs->win + (ofs % SS(fs)); /* Pointer to the entry in >> the win[] */ >> + >> + return FR_OK; >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Directory handling - Move directory table index >> next */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of >> table, FR_DENIED:Could not stretch */ >> + DIR* dp, /* Pointer to the directory object */ >> + int stretch /* 0: Do not stretch table, 1: Stretch >> table if needed */ >> +) >> +{ >> + DWORD ofs, clst; >> + FATFS *fs = dp->obj.fs; >> + >> + >> + ofs = dp->dptr + SZDIRE; /* Next entry */ >> + if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? >> MAX_DIR_EX : MAX_DIR)) dp->sect = 0; /* Disable it if the offset >> reached the max value */ >> + if (dp->sect == 0) return FR_NO_FILE; /* Report EOT if it has >> been disabled */ >> + >> + if (ofs % SS(fs) == 0) { /* Sector changed? */ >> + dp->sect++; /* Next sector */ >> + >> + if (dp->clust == 0) { /* Static table */ >> + if (ofs / SZDIRE >= fs->n_rootdir) { /* Report EOT if >> it reached end of static table */ >> + dp->sect = 0; return FR_NO_FILE; >> + } >> + } >> + else { /* Dynamic table */ >> + if ((ofs / SS(fs) & (fs->csize - 1)) == 0) { /* >> Cluster changed? */ >> + clst = get_fat(&dp->obj, dp->clust); /* Get >> next cluster */ >> + if (clst <= 1) return FR_INT_ERR; /* >> Internal error */ >> + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* >> Disk error */ >> + if (clst >= fs->n_fatent) { /* It >> reached end of dynamic table */ >> +#if !FF_FS_READONLY >> + if (!stretch) { /* >> If no stretch, report EOT */ >> + dp->sect = 0; return FR_NO_FILE; >> + } >> + clst = create_chain(&dp->obj, dp->clust); /* >> Allocate a cluster */ >> + if (clst == 0) return FR_DENIED; /* No >> free cluster */ >> + if (clst == 1) return FR_INT_ERR; /* >> Internal error */ >> + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* >> Disk error */ >> + if (dir_clear(fs, clst) != FR_OK) return >> FR_DISK_ERR; /* Clean up the stretched table */ >> + if (FF_FS_EXFAT) dp->obj.stat |= 4; /* >> exFAT: The directory has been stretched */ >> +#else >> + if (!stretch) dp->sect = 0; /* >> (this line is to suppress compiler warning) */ >> + dp->sect = 0; return FR_NO_FILE; /* >> Report EOT */ >> +#endif >> + } >> + dp->clust = clst; /* Initialize data for new >> cluster */ >> + dp->sect = clst2sect(fs, clst); >> + } >> + } >> + } >> + dp->dptr = ofs; /* Current entry */ >> + dp->dir = fs->win + ofs % SS(fs); /* Pointer to the entry in >> the win[] */ >> + >> + return FR_OK; >> +} >> + >> + >> + >> + >> +#if !FF_FS_READONLY >> +/*-----------------------------------------------------------------------*/ >> >> +/* Directory handling - Reserve a block of directory >> entries */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ >> + DIR* dp, /* Pointer to the directory object */ >> + UINT n_ent /* Number of contiguous entries to >> allocate */ >> +) >> +{ >> + FRESULT res; >> + UINT n; >> + FATFS *fs = dp->obj.fs; >> + >> + >> + res = dir_sdi(dp, 0); >> + if (res == FR_OK) { >> + n = 0; >> + do { >> + res = move_window(fs, dp->sect); >> + if (res != FR_OK) break; >> +#if FF_FS_EXFAT >> + if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] >> & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] >> == 0)) { /* Is the entry free? */ >> +#else >> + if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) >> { /* Is the entry free? */ >> +#endif >> + if (++n == n_ent) break; /* Is a block of >> contiguous free entries found? */ >> + } else { >> + n = 0; /* Not a free entry, restart to >> search */ >> + } >> + res = dir_next(dp, 1); /* Next entry with table >> stretch enabled */ >> + } while (res == FR_OK); >> + } >> + >> + if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry >> to allocate */ >> + return res; >> +} >> + >> +#endif /* !FF_FS_READONLY */ >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* FAT: Directory handling - Load/Store start cluster >> number */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static DWORD ld_clust ( /* Returns the top cluster value of the >> SFN entry */ >> + FATFS* fs, /* Pointer to the fs object */ >> + const BYTE* dir /* Pointer to the key entry */ >> +) >> +{ >> + DWORD cl; >> + >> + cl = ld_word(dir + DIR_FstClusLO); >> + if (fs->fs_type == FS_FAT32) { >> + cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16; >> + } >> + >> + return cl; >> +} >> + >> + >> +#if !FF_FS_READONLY >> +static void st_clust ( >> + FATFS* fs, /* Pointer to the fs object */ >> + BYTE* dir, /* Pointer to the key entry */ >> + DWORD cl /* Value to be set */ >> +) >> +{ >> + st_word(dir + DIR_FstClusLO, (WORD)cl); >> + if (fs->fs_type == FS_FAT32) { >> + st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16)); >> + } >> +} >> +#endif >> + >> + >> + >> +#if FF_USE_LFN >> +/*--------------------------------------------------------*/ >> +/* FAT-LFN: Compare a part of file name with an LFN entry */ >> +/*--------------------------------------------------------*/ >> + >> +static int cmp_lfn ( /* 1:matched, 0:not matched */ >> + const WCHAR* lfnbuf, /* Pointer to the LFN working buffer to >> be compared */ >> + BYTE* dir /* Pointer to the directory entry >> containing the part of LFN */ >> +) >> +{ >> + UINT i, s; >> + WCHAR wc, uc; >> + >> + >> + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check >> LDIR_FstClusLO */ >> + >> + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN >> buffer */ >> + >> + for (wc = 1, s = 0; s < 13; s++) { /* Process all >> characters in the entry */ >> + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN >> character */ >> + if (wc != 0) { >> + if (i >= FF_MAX_LFN + 1 || ff_wtoupper(uc) != >> ff_wtoupper(lfnbuf[i++])) { /* Compare it */ >> + return 0; /* Not matched */ >> + } >> + wc = uc; >> + } else { >> + if (uc != 0xFFFF) return 0; /* Check filler */ >> + } >> + } >> + >> + if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) return 0; /* >> Last segment matched but different length */ >> + >> + return 1; /* The part of LFN matched */ >> +} >> + >> + >> +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || >> FF_FS_EXFAT >> +/*-----------------------------------------------------*/ >> +/* FAT-LFN: Pick a part of file name from an LFN entry */ >> +/*-----------------------------------------------------*/ >> + >> +static int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid >> LFN entry */ >> + WCHAR* lfnbuf, /* Pointer to the LFN working buffer */ >> + BYTE* dir /* Pointer to the LFN entry */ >> +) >> +{ >> + UINT i, s; >> + WCHAR wc, uc; >> + >> + >> + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check >> LDIR_FstClusLO is 0 */ >> + >> + i = ((dir[LDIR_Ord] & ~LLEF) - 1) * 13; /* Offset in the LFN >> buffer */ >> + >> + for (wc = 1, s = 0; s < 13; s++) { /* Process all >> characters in the entry */ >> + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN >> character */ >> + if (wc != 0) { >> + if (i >= FF_MAX_LFN + 1) return 0; /* Buffer overflow? */ >> + lfnbuf[i++] = wc = uc; /* Store it */ >> + } else { >> + if (uc != 0xFFFF) return 0; /* Check filler */ >> + } >> + } >> + >> + if (dir[LDIR_Ord] & LLEF && wc != 0) { /* Put terminator if it >> is the last LFN part and not terminated */ >> + if (i >= FF_MAX_LFN + 1) return 0; /* Buffer overflow? */ >> + lfnbuf[i] = 0; >> + } >> + >> + return 1; /* The part of LFN is valid */ >> +} >> +#endif >> + >> + >> +#if !FF_FS_READONLY >> +/*-----------------------------------------*/ >> +/* FAT-LFN: Create an entry of LFN entries */ >> +/*-----------------------------------------*/ >> + >> +static void put_lfn ( >> + const WCHAR* lfn, /* Pointer to the LFN */ >> + BYTE* dir, /* Pointer to the LFN entry to be created */ >> + BYTE ord, /* LFN order (1-20) */ >> + BYTE sum /* Checksum of the corresponding SFN */ >> +) >> +{ >> + UINT i, s; >> + WCHAR wc; >> + >> + >> + dir[LDIR_Chksum] = sum; /* Set checksum */ >> + dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ >> + dir[LDIR_Type] = 0; >> + st_word(dir + LDIR_FstClusLO, 0); >> + >> + i = (ord - 1) * 13; /* Get offset in the LFN >> working buffer */ >> + s = wc = 0; >> + do { >> + if (wc != 0xFFFF) wc = lfn[i++]; /* Get an effective >> character */ >> + st_word(dir + LfnOfs[s], wc); /* Put it */ >> + if (wc == 0) wc = 0xFFFF; /* Padding characters for >> following items */ >> + } while (++s < 13); >> + if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part is >> the start of LFN sequence */ >> + dir[LDIR_Ord] = ord; /* Set the LFN order */ >> +} >> + >> +#endif /* !FF_FS_READONLY */ >> +#endif /* FF_USE_LFN */ >> + >> + >> + >> +#if FF_USE_LFN && !FF_FS_READONLY >> +/*-----------------------------------------------------------------------*/ >> >> +/* FAT-LFN: Create a Numbered >> SFN */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static void gen_numname ( >> + BYTE* dst, /* Pointer to the buffer to store numbered >> SFN */ >> + const BYTE* src, /* Pointer to SFN */ >> + const WCHAR* lfn, /* Pointer to LFN */ >> + UINT seq /* Sequence number */ >> +) >> +{ >> + BYTE ns[8], c; >> + UINT i, j; >> + WCHAR wc; >> + DWORD sreg; >> + >> + >> + mem_cpy(dst, src, 11); >> + >> + if (seq > 5) { /* In case of many collisions, generate a hash >> number instead of sequential number */ >> + sreg = seq; >> + while (*lfn) { /* Create a CRC as hash value */ >> + wc = *lfn++; >> + for (i = 0; i < 16; i++) { >> + sreg = (sreg << 1) + (wc & 1); >> + wc >>= 1; >> + if (sreg & 0x10000) sreg ^= 0x11021; >> + } >> + } >> + seq = (UINT)sreg; >> + } >> + >> + /* itoa (hexdecimal) */ >> + i = 7; >> + do { >> + c = (BYTE)((seq % 16) + '0'); >> + if (c > '9') c += 7; >> + ns[i--] = c; >> + seq /= 16; >> + } while (seq); >> + ns[i] = '~'; >> + >> + /* Append the number to the SFN body */ >> + for (j = 0; j < i && dst[j] != ' '; j++) { >> + if (dbc_1st(dst[j])) { >> + if (j == i - 1) break; >> + j++; >> + } >> + } >> + do { >> + dst[j++] = (i < 8) ? ns[i++] : ' '; >> + } while (j < 8); >> +} >> +#endif /* FF_USE_LFN && !FF_FS_READONLY */ >> + >> + >> + >> +#if FF_USE_LFN >> +/*-----------------------------------------------------------------------*/ >> >> +/* FAT-LFN: Calculate checksum of an SFN >> entry */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static BYTE sum_sfn ( >> + const BYTE* dir /* Pointer to the SFN entry */ >> +) >> +{ >> + BYTE sum = 0; >> + UINT n = 11; >> + >> + do { >> + sum = (sum >> 1) + (sum << 7) + *dir++; >> + } while (--n); >> + return sum; >> +} >> + >> +#endif /* FF_USE_LFN */ >> + >> + >> + >> +#if FF_FS_EXFAT >> +/*-----------------------------------------------------------------------*/ >> >> +/* exFAT: >> Checksum */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static WORD xdir_sum ( /* Get checksum of the directoly entry >> block */ >> + const BYTE* dir /* Directory entry block to be calculated */ >> +) >> +{ >> + UINT i, szblk; >> + WORD sum; >> + >> + >> + szblk = (dir[XDIR_NumSec] + 1) * SZDIRE; /* Number of bytes of >> the entry block */ >> + for (i = sum = 0; i < szblk; i++) { >> + if (i == XDIR_SetSum) { /* Skip 2-byte sum field */ >> + i++; >> + } else { >> + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i]; >> + } >> + } >> + return sum; >> +} >> + >> + >> + >> +static WORD xname_sum ( /* Get check sum (to be used as hash) of >> the file name */ >> + const WCHAR* name /* File name to be calculated */ >> +) >> +{ >> + WCHAR chr; >> + WORD sum = 0; >> + >> + >> + while ((chr = *name++) != 0) { >> + chr = (WCHAR)ff_wtoupper(chr); /* File name needs to >> be up-case converted */ >> + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF); >> + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8); >> + } >> + return sum; >> +} >> + >> + >> +#if !FF_FS_READONLY && FF_USE_MKFS >> +static DWORD xsum32 ( /* Returns 32-bit checksum */ >> + BYTE dat, /* Byte to be calculated (byte-by-byte >> processing) */ >> + DWORD sum /* Previous sum value */ >> +) >> +{ >> + sum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat; >> + return sum; >> +} >> +#endif >> + >> + >> +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 >> +/*------------------------------------------------------*/ >> +/* exFAT: Get object information from a directory block */ >> +/*------------------------------------------------------*/ >> + >> +static void get_xfileinfo ( >> + BYTE* dirb, /* Pointer to the direcotry entry block >> 85+C0+C1s */ >> + FILINFO* fno /* Buffer to store the extracted file >> information */ >> +) >> +{ >> + WCHAR wc, hs; >> + UINT di, si, nc; >> + >> + /* Get file name from the entry block */ >> + si = SZDIRE * 2; /* 1st C1 entry */ >> + nc = 0; hs = 0; di = 0; >> + while (nc < dirb[XDIR_NumName]) { >> + if (si >= MAXDIRB(FF_MAX_LFN)) { di = 0; break; } /* >> Truncated directory block? */ >> + if ((si % SZDIRE) == 0) si += 2; /* Skip entry type >> field */ >> + wc = ld_word(dirb + si); si += 2; nc++; /* Get a character */ >> + if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ >> + hs = wc; continue; /* Get low surrogate */ >> + } >> + wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], >> FF_LFN_BUF - di); /* Store it in API encoding */ >> + if (wc == 0) { di = 0; break; } /* Buffer overflow or >> wrong encoding? */ >> + di += wc; >> + hs = 0; >> + } >> + if (hs != 0) di = 0; /* Broken surrogate pair? */ >> + if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object >> name? */ >> + fno->fname[di] = 0; /* Terminate the name */ >> + fno->altname[0] = 0; /* exFAT does not support >> SFN */ >> + >> + fno->fattrib = dirb[XDIR_Attr]; /* Attribute */ >> + fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + >> XDIR_FileSize); /* Size */ >> + fno->ftime = ld_word(dirb + XDIR_ModTime + 0); /* Time */ >> + fno->fdate = ld_word(dirb + XDIR_ModTime + 2); /* Date */ >> +} >> + >> +#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */ >> + >> + >> +/*-----------------------------------*/ >> +/* exFAT: Get a directry entry block */ >> +/*-----------------------------------*/ >> + >> +static FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ >> + DIR* dp /* Reading direcotry object pointing >> top of the entry block to load */ >> +) >> +{ >> + FRESULT res; >> + UINT i, sz_ent; >> + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory >> direcotry entry block 85+C0+C1s */ >> + >> + >> + /* Load file-directory entry */ >> + res = move_window(dp->obj.fs, dp->sect); >> + if (res != FR_OK) return res; >> + if (dp->dir[XDIR_Type] != ET_FILEDIR) return FR_INT_ERR; /* >> Invalid order */ >> + mem_cpy(dirb + 0 * SZDIRE, dp->dir, SZDIRE); >> + sz_ent = (dirb[XDIR_NumSec] + 1) * SZDIRE; >> + if (sz_ent < 3 * SZDIRE || sz_ent > 19 * SZDIRE) return FR_INT_ERR; >> + >> + /* Load stream-extension entry */ >> + res = dir_next(dp, 0); >> + if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */ >> + if (res != FR_OK) return res; >> + res = move_window(dp->obj.fs, dp->sect); >> + if (res != FR_OK) return res; >> + if (dp->dir[XDIR_Type] != ET_STREAM) return FR_INT_ERR; /* >> Invalid order */ >> + mem_cpy(dirb + 1 * SZDIRE, dp->dir, SZDIRE); >> + if (MAXDIRB(dirb[XDIR_NumName]) > sz_ent) return FR_INT_ERR; >> + >> + /* Load file-name entries */ >> + i = 2 * SZDIRE; /* Name offset to load */ >> + do { >> + res = dir_next(dp, 0); >> + if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */ >> + if (res != FR_OK) return res; >> + res = move_window(dp->obj.fs, dp->sect); >> + if (res != FR_OK) return res; >> + if (dp->dir[XDIR_Type] != ET_FILENAME) return FR_INT_ERR; >> /* Invalid order */ >> + if (i < MAXDIRB(FF_MAX_LFN)) mem_cpy(dirb + i, dp->dir, SZDIRE); >> + } while ((i += SZDIRE) < sz_ent); >> + >> + /* Sanity check (do it for only accessible object) */ >> + if (i <= MAXDIRB(FF_MAX_LFN)) { >> + if (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return >> FR_INT_ERR; >> + } >> + return FR_OK; >> +} >> + >> + >> +/*------------------------------------------------------------------*/ >> +/* exFAT: Initialize object allocation info with loaded entry block */ >> +/*------------------------------------------------------------------*/ >> + >> +static void init_alloc_info ( >> + FATFS* fs, /* Filesystem object */ >> + FFOBJID* obj /* Object allocation information to be >> initialized */ >> +) >> +{ >> + obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* >> Start cluster */ >> + obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); /* Size */ >> + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; /* >> Allocation status */ >> + obj->n_frag = 0; /* No >> last fragment info */ >> +} >> + >> + >> + >> +#if !FF_FS_READONLY || FF_FS_RPATH != 0 >> +/*------------------------------------------------*/ >> +/* exFAT: Load the object's directory entry block */ >> +/*------------------------------------------------*/ >> + >> +static FRESULT load_obj_xdir ( >> + DIR* dp, /* Blank directory object to be used to >> access containing direcotry */ >> + const FFOBJID* obj /* Object with its containing directory >> information */ >> +) >> +{ >> + FRESULT res; >> + >> + /* Open object containing directory */ >> + dp->obj.fs = obj->fs; >> + dp->obj.sclust = obj->c_scl; >> + dp->obj.stat = (BYTE)obj->c_size; >> + dp->obj.objsize = obj->c_size & 0xFFFFFF00; >> + dp->obj.n_frag = 0; >> + dp->blk_ofs = obj->c_ofs; >> + >> + res = dir_sdi(dp, dp->blk_ofs); /* Goto object's entry block */ >> + if (res == FR_OK) { >> + res = load_xdir(dp); /* Load the object's entry block */ >> + } >> + return res; >> +} >> +#endif >> + >> + >> +#if !FF_FS_READONLY >> +/*----------------------------------------*/ >> +/* exFAT: Store the directory entry block */ >> +/*----------------------------------------*/ >> + >> +static FRESULT store_xdir ( >> + DIR* dp /* Pointer to the direcotry object */ >> +) >> +{ >> + FRESULT res; >> + UINT nent; >> + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry >> entry block 85+C0+C1s */ >> + >> + /* Create set sum */ >> + st_word(dirb + XDIR_SetSum, xdir_sum(dirb)); >> + nent = dirb[XDIR_NumSec] + 1; >> + >> + /* Store the direcotry entry block to the directory */ >> + res = dir_sdi(dp, dp->blk_ofs); >> + while (res == FR_OK) { >> + res = move_window(dp->obj.fs, dp->sect); >> + if (res != FR_OK) break; >> + mem_cpy(dp->dir, dirb, SZDIRE); >> + dp->obj.fs->wflag = 1; >> + if (--nent == 0) break; >> + dirb += SZDIRE; >> + res = dir_next(dp, 0); >> + } >> + return (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR; >> +} >> + >> + >> + >> +/*-------------------------------------------*/ >> +/* exFAT: Create a new directory enrty block */ >> +/*-------------------------------------------*/ >> + >> +static void create_xdir ( >> + BYTE* dirb, /* Pointer to the direcotry entry block >> buffer */ >> + const WCHAR* lfn /* Pointer to the object name */ >> +) >> +{ >> + UINT i; >> + BYTE nc1, nlen; >> + WCHAR wc; >> + >> + >> + /* Create file-directory and stream-extension entry */ >> + mem_set(dirb, 0, 2 * SZDIRE); >> + dirb[0 * SZDIRE + XDIR_Type] = ET_FILEDIR; >> + dirb[1 * SZDIRE + XDIR_Type] = ET_STREAM; >> + >> + /* Create file-name entries */ >> + i = SZDIRE * 2; /* Top of file_name entries */ >> + nlen = nc1 = 0; wc = 1; >> + do { >> + dirb[i++] = ET_FILENAME; dirb[i++] = 0; >> + do { /* Fill name field */ >> + if (wc != 0 && (wc = lfn[nlen]) != 0) nlen++; /* Get a >> character if exist */ >> + st_word(dirb + i, wc); /* Store it */ >> + i += 2; >> + } while (i % SZDIRE != 0); >> + nc1++; >> + } while (lfn[nlen]); /* Fill next entry if any char follows */ >> + >> + dirb[XDIR_NumName] = nlen; /* Set name length */ >> + dirb[XDIR_NumSec] = 1 + nc1; /* Set secondary count (C0 + C1s) */ >> + st_word(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name >> hash */ >> +} >> + >> +#endif /* !FF_FS_READONLY */ >> +#endif /* FF_FS_EXFAT */ >> + >> + >> + >> +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || >> FF_FS_EXFAT >> +/*-----------------------------------------------------------------------*/ >> >> +/* Read an object from the >> directory */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +#define DIR_READ_FILE(dp) dir_read(dp, 0) >> +#define DIR_READ_LABEL(dp) dir_read(dp, 1) >> + >> +static FRESULT dir_read ( >> + DIR* dp, /* Pointer to the directory object */ >> + int vol /* Filtered by 0:file/directory or 1:volume >> label */ >> +) >> +{ >> + FRESULT res = FR_NO_FILE; >> + FATFS *fs = dp->obj.fs; >> + BYTE attr, b; >> +#if FF_USE_LFN >> + BYTE ord = 0xFF, sum = 0xFF; >> +#endif >> + >> + while (dp->sect) { >> + res = move_window(fs, dp->sect); >> + if (res != FR_OK) break; >> + b = dp->dir[DIR_Name]; /* Test for the entry type */ >> + if (b == 0) { >> + res = FR_NO_FILE; break; /* Reached to end of the >> directory */ >> + } >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ >> + if (FF_USE_LABEL && vol) { >> + if (b == ET_VLABEL) break; /* Volume label entry? */ >> + } else { >> + if (b == ET_FILEDIR) { /* Start of the file >> entry block? */ >> + dp->blk_ofs = dp->dptr; /* Get location of the >> block */ >> + res = load_xdir(dp); /* Load the entry block */ >> + if (res == FR_OK) { >> + dp->obj.attr = fs->dirbuf[XDIR_Attr] & >> AM_MASK; /* Get attribute */ >> + } >> + break; >> + } >> + } >> + } else >> +#endif >> + { /* On the FAT/FAT32 volume */ >> + dp->obj.attr = attr = dp->dir[DIR_Attr] & AM_MASK; /* >> Get attribute */ >> +#if FF_USE_LFN /* LFN configuration */ >> + if (b == DDEM || b == '.' || (int)((attr & ~AM_ARC) == >> AM_VOL) != vol) { /* An entry without valid data */ >> + ord = 0xFF; >> + } else { >> + if (attr == AM_LFN) { /* An LFN entry is >> found */ >> + if (b & LLEF) { /* Is it start of an >> LFN sequence? */ >> + sum = dp->dir[LDIR_Chksum]; >> + b &= (BYTE)~LLEF; ord = b; >> + dp->blk_ofs = dp->dptr; >> + } >> + /* Check LFN validity and capture it */ >> + ord = (b == ord && sum == dp->dir[LDIR_Chksum] && >> pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; >> + } else { /* An SFN entry is found */ >> + if (ord != 0 || sum != sum_sfn(dp->dir)) { /* >> Is there a valid LFN? */ >> + dp->blk_ofs = 0xFFFFFFFF; /* It >> has no LFN. */ >> + } >> + break; >> + } >> + } >> +#else /* Non LFN configuration */ >> + if (b != DDEM && b != '.' && attr != AM_LFN && >> (int)((attr & ~AM_ARC) == AM_VOL) == vol) { /* Is it a valid entry? */ >> + break; >> + } >> +#endif >> + } >> + res = dir_next(dp, 0); /* Next entry */ >> + if (res != FR_OK) break; >> + } >> + >> + if (res != FR_OK) dp->sect = 0; /* Terminate the read >> operation on error or EOT */ >> + return res; >> +} >> + >> +#endif /* FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_RPATH >= 2 */ >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Directory handling - Find an object in the >> directory */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ >> + DIR* dp /* Pointer to the directory object >> with the file name */ >> +) >> +{ >> + FRESULT res; >> + FATFS *fs = dp->obj.fs; >> + BYTE c; >> +#if FF_USE_LFN >> + BYTE a, ord, sum; >> +#endif >> + >> + res = dir_sdi(dp, 0); /* Rewind directory object */ >> + if (res != FR_OK) return res; >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ >> + BYTE nc; >> + UINT di, ni; >> + WORD hash = xname_sum(fs->lfnbuf); /* Hash value of >> the name to find */ >> + >> + while ((res = DIR_READ_FILE(dp)) == FR_OK) { /* Read an >> item */ >> +#if FF_MAX_LFN < 255 >> + if (fs->dirbuf[XDIR_NumName] > FF_MAX_LFN) >> continue; /* Skip comparison if inaccessible object name */ >> +#endif >> + if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) >> continue; /* Skip comparison if hash mismatched */ >> + for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = >> 0; nc; nc--, di += 2, ni++) { /* Compare the name */ >> + if ((di % SZDIRE) == 0) di += 2; >> + if (ff_wtoupper(ld_word(fs->dirbuf + di)) != >> ff_wtoupper(fs->lfnbuf[ni])) break; >> + } >> + if (nc == 0 && !fs->lfnbuf[ni]) break; /* Name >> matched? */ >> + } >> + return res; >> + } >> +#endif >> + /* On the FAT/FAT32 volume */ >> +#if FF_USE_LFN >> + ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN >> sequence */ >> +#endif >> + do { >> + res = move_window(fs, dp->sect); >> + if (res != FR_OK) break; >> + c = dp->dir[DIR_Name]; >> + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end >> of table */ >> +#if FF_USE_LFN /* LFN configuration */ >> + dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; >> + if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An >> entry without valid data */ >> + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN >> sequence */ >> + } else { >> + if (a == AM_LFN) { /* An LFN entry is found */ >> + if (!(dp->fn[NSFLAG] & NS_NOLFN)) { >> + if (c & LLEF) { /* Is it start of LFN >> sequence? */ >> + sum = dp->dir[LDIR_Chksum]; >> + c &= (BYTE)~LLEF; ord = c; /* LFN start >> order */ >> + dp->blk_ofs = dp->dptr; /* Start offset of >> LFN */ >> + } >> + /* Check validity of the LFN entry and compare it >> with given name */ >> + ord = (c == ord && sum == dp->dir[LDIR_Chksum] && >> cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; >> + } >> + } else { /* An SFN entry is found */ >> + if (ord == 0 && sum == sum_sfn(dp->dir)) break; /* >> LFN matched? */ >> + if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, >> dp->fn, 11)) break; /* SFN matched? */ >> + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN >> sequence */ >> + } >> + } >> +#else /* Non LFN configuration */ >> + dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK; >> + if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, >> dp->fn, 11)) break; /* Is it a valid entry? */ >> +#endif >> + res = dir_next(dp, 0); /* Next entry */ >> + } while (res == FR_OK); >> + >> + return res; >> +} >> + >> + >> + >> + >> +#if !FF_FS_READONLY >> +/*-----------------------------------------------------------------------*/ >> >> +/* Register an object to the >> directory */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no >> free entry or too many SFN collision, FR_DISK_ERR:disk error */ >> + DIR* dp /* Target directory with object >> name to be created */ >> +) >> +{ >> + FRESULT res; >> + FATFS *fs = dp->obj.fs; >> +#if FF_USE_LFN /* LFN configuration */ >> + UINT n, len, n_ent; >> + BYTE sn[12], sum; >> + >> + >> + if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return >> FR_INVALID_NAME; /* Check name validity */ >> + for (len = 0; fs->lfnbuf[len]; len++) ; /* Get lfn length */ >> + >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ >> + n_ent = (len + 14) / 15 + 2; /* Number of entries to >> allocate (85+C0+C1s) */ >> + res = dir_alloc(dp, n_ent); /* Allocate directory >> entries */ >> + if (res != FR_OK) return res; >> + dp->blk_ofs = dp->dptr - SZDIRE * (n_ent - 1); /* Set the >> allocated entry block offset */ >> + >> + if (dp->obj.stat & 4) { /* Has the directory been >> stretched by new allocation? */ >> + dp->obj.stat &= ~4; >> + res = fill_first_frag(&dp->obj); /* Fill the first >> fragment on the FAT if needed */ >> + if (res != FR_OK) return res; >> + res = fill_last_frag(&dp->obj, dp->clust, 0xFFFFFFFF); >> /* Fill the last fragment on the FAT if needed */ >> + if (res != FR_OK) return res; >> + if (dp->obj.sclust != 0) { /* Is it a >> sub-directory? */ >> + DIR dj; >> + >> + res = load_obj_xdir(&dj, &dp->obj); /* Load the >> object status */ >> + if (res != FR_OK) return res; >> + dp->obj.objsize += (DWORD)fs->csize * >> SS(fs); /* Increase the directory size by cluster size */ >> + st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize); >> + st_qword(fs->dirbuf + XDIR_ValidFileSize, >> dp->obj.objsize); >> + fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | >> 1; /* Update the allocation status */ >> + res = store_xdir(&dj); /* Store the >> object status */ >> + if (res != FR_OK) return res; >> + } >> + } >> + >> + create_xdir(fs->dirbuf, fs->lfnbuf); /* Create on-memory >> directory block to be written later */ >> + return FR_OK; >> + } >> +#endif >> + /* On the FAT/FAT32 volume */ >> + mem_cpy(sn, dp->fn, 12); >> + if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 >> format, generate a numbered name */ >> + dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */ >> + for (n = 1; n < 100; n++) { >> + gen_numname(dp->fn, sn, fs->lfnbuf, n); /* Generate a >> numbered name */ >> + res = dir_find(dp); /* Check if the name >> collides with existing SFN */ >> + if (res != FR_OK) break; >> + } >> + if (n == 100) return FR_DENIED; /* Abort if too many >> collisions */ >> + if (res != FR_NO_FILE) return res; /* Abort if the result >> is other than 'not collided' */ >> + dp->fn[NSFLAG] = sn[NSFLAG]; >> + } >> + >> + /* Create an SFN with/without LFNs. */ >> + n_ent = (sn[NSFLAG] & NS_LFN) ? (len + 12) / 13 + 1 : 1; /* >> Number of entries to allocate */ >> + res = dir_alloc(dp, n_ent); /* Allocate entries */ >> + if (res == FR_OK && --n_ent) { /* Set LFN entry if needed */ >> + res = dir_sdi(dp, dp->dptr - n_ent * SZDIRE); >> + if (res == FR_OK) { >> + sum = sum_sfn(dp->fn); /* Checksum value of the SFN >> tied to the LFN */ >> + do { /* Store LFN entries in bottom >> first */ >> + res = move_window(fs, dp->sect); >> + if (res != FR_OK) break; >> + put_lfn(fs->lfnbuf, dp->dir, (BYTE)n_ent, sum); >> + fs->wflag = 1; >> + res = dir_next(dp, 0); /* Next entry */ >> + } while (res == FR_OK && --n_ent); >> + } >> + } >> + >> +#else /* Non LFN configuration */ >> + res = dir_alloc(dp, 1); /* Allocate an entry for SFN */ >> + >> +#endif >> + >> + /* Set SFN entry */ >> + if (res == FR_OK) { >> + res = move_window(fs, dp->sect); >> + if (res == FR_OK) { >> + mem_set(dp->dir, 0, SZDIRE); /* Clean the entry */ >> + mem_cpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */ >> +#if FF_USE_LFN >> + dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | >> NS_EXT); /* Put NT flag */ >> +#endif >> + fs->wflag = 1; >> + } >> + } >> + >> + return res; >> +} >> + >> +#endif /* !FF_FS_READONLY */ >> + >> + >> + >> +#if !FF_FS_READONLY && FF_FS_MINIMIZE == 0 >> +/*-----------------------------------------------------------------------*/ >> >> +/* Remove an object from the >> directory */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk >> error */ >> + DIR* dp /* Directory object pointing the entry >> to be removed */ >> +) >> +{ >> + FRESULT res; >> + FATFS *fs = dp->obj.fs; >> +#if FF_USE_LFN /* LFN configuration */ >> + DWORD last = dp->dptr; >> + >> + res = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, >> dp->blk_ofs); /* Goto top of the entry block if LFN is exist */ >> + if (res == FR_OK) { >> + do { >> + res = move_window(fs, dp->sect); >> + if (res != FR_OK) break; >> + if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On >> the exFAT volume */ >> + dp->dir[XDIR_Type] &= 0x7F; /* Clear the entry >> InUse flag. */ >> + } else { /* On the >> FAT/FAT32 volume */ >> + dp->dir[DIR_Name] = DDEM; /* Mark the entry >> 'deleted'. */ >> + } >> + fs->wflag = 1; >> + if (dp->dptr >= last) break; /* If reached last entry >> then all entries of the object has been deleted. */ >> + res = dir_next(dp, 0); /* Next entry */ >> + } while (res == FR_OK); >> + if (res == FR_NO_FILE) res = FR_INT_ERR; >> + } >> +#else /* Non LFN configuration */ >> + >> + res = move_window(fs, dp->sect); >> + if (res == FR_OK) { >> + dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'.*/ >> + fs->wflag = 1; >> + } >> +#endif >> + >> + return res; >> +} >> + >> +#endif /* !FF_FS_READONLY && FF_FS_MINIMIZE == 0 */ >> + >> + >> + >> +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 >> +/*-----------------------------------------------------------------------*/ >> >> +/* Get file information from directory >> entry */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static void get_fileinfo ( >> + DIR* dp, /* Pointer to the directory object */ >> + FILINFO* fno /* Pointer to the file information to be >> filled */ >> +) >> +{ >> + UINT si, di; >> +#if FF_USE_LFN >> + BYTE lcf; >> + WCHAR wc, hs; >> + FATFS *fs = dp->obj.fs; >> +#else >> + TCHAR c; >> +#endif >> + >> + >> + fno->fname[0] = 0; /* Invaidate file info */ >> + if (dp->sect == 0) return; /* Exit if read pointer has reached >> end of directory */ >> + >> +#if FF_USE_LFN /* LFN configuration */ >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ >> + get_xfileinfo(fs->dirbuf, fno); >> + return; >> + } else >> +#endif >> + { /* On the FAT/FAT32 volume */ >> + if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */ >> + si = di = hs = 0; >> + while (fs->lfnbuf[si] != 0) { >> + wc = fs->lfnbuf[si++]; /* Get an LFN character >> (UTF-16) */ >> + if (hs == 0 && IsSurrogate(wc)) { /* Is it a >> surrogate? */ >> + hs = wc; continue; /* Get low surrogate */ >> + } >> + wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], >> FF_LFN_BUF - di); /* Store it in UTF-16 or UTF-8 encoding */ >> + if (wc == 0) { di = 0; break; } /* Invalid char or >> buffer overflow? */ >> + di += wc; >> + hs = 0; >> + } >> + if (hs != 0) di = 0; /* Broken surrogate pair? */ >> + fno->fname[di] = 0; /* Terminate the LFN (null >> string means LFN is invalid) */ >> + } >> + } >> + >> + si = di = 0; >> + while (si < 11) { /* Get SFN from SFN entry */ >> + wc = dp->dir[si++]; /* Get a char */ >> + if (wc == ' ') continue; /* Skip padding spaces */ >> + if (wc == RDDEM) wc = DDEM; /* Restore replaced DDEM >> character */ >> + if (si == 9 && di < FF_SFN_BUF) fno->altname[di++] = '.'; >> /* Insert a . if extension is exist */ >> +#if FF_LFN_UNICODE >= 1 /* Unicode output */ >> + if (dbc_1st((BYTE)wc) && si != 8 && si != 11 && >> dbc_2nd(dp->dir[si])) { /* Make a DBC if needed */ >> + wc = wc << 8 | dp->dir[si++]; >> + } >> + wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM -> Unicode */ >> + if (wc == 0) { di = 0; break; } /* Wrong char in the >> current code page? */ >> + wc = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di); /* >> Store it in Unicode */ >> + if (wc == 0) { di = 0; break; } /* Buffer overflow? */ >> + di += wc; >> +#else /* ANSI/OEM output */ >> + fno->altname[di++] = (TCHAR)wc; /* Store it without any >> conversion */ >> +#endif >> + } >> + fno->altname[di] = 0; /* Terminate the SFN (null string means >> SFN is invalid) */ >> + >> + if (fno->fname[0] == 0) { /* If LFN is invalid, altname[] >> needs to be copied to fname[] */ >> + if (di == 0) { /* If LFN and SFN both are invalid, this >> object is inaccesible */ >> + fno->fname[di++] = '?'; >> + } else { >> + for (si = di = 0, lcf = NS_BODY; fno->altname[si]; si++, >> di++) { /* Copy altname[] to fname[] with case information */ >> + wc = (WCHAR)fno->altname[si]; >> + if (wc == '.') lcf = NS_EXT; >> + if (IsUpper(wc) && (dp->dir[DIR_NTres] & lcf)) wc += >> 0x20; >> + fno->fname[di] = (TCHAR)wc; >> + } >> + } >> + fno->fname[di] = 0; /* Terminate the LFN */ >> + if (!dp->dir[DIR_NTres]) fno->altname[0] = 0; /* Altname >> is not needed if neither LFN nor case info is exist. */ >> + } >> + >> +#else /* Non-LFN configuration */ >> + si = di = 0; >> + while (si < 11) { /* Copy name body and extension */ >> + c = (TCHAR)dp->dir[si++]; >> + if (c == ' ') continue; /* Skip padding spaces */ >> + if (c == RDDEM) c = DDEM; /* Restore replaced DDEM >> character */ >> + if (si == 9) fno->fname[di++] = '.';/* Insert a . if >> extension is exist */ >> + fno->fname[di++] = c; >> + } >> + fno->fname[di] = 0; >> +#endif >> + >> + fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */ >> + fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ >> + fno->ftime = ld_word(dp->dir + DIR_ModTime + 0); /* Time */ >> + fno->fdate = ld_word(dp->dir + DIR_ModTime + 2); /* Date */ >> +} >> + >> +#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */ >> + >> + >> + >> +#if FF_USE_FIND && FF_FS_MINIMIZE <= 1 >> +/*-----------------------------------------------------------------------*/ >> >> +/* Pattern >> matching */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +#define FIND_RECURS 4 /* Maximum number of wildcard terms in >> the pattern to limit recursion */ >> + >> + >> +static DWORD get_achar ( /* Get a character and advance ptr */ >> + const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or >> Unicode string */ >> +) >> +{ >> + DWORD chr; >> + >> + >> +#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode input */ >> + chr = tchar2uni(ptr); >> + if (chr == 0xFFFFFFFF) chr = 0; /* Wrong UTF encoding is >> recognized as end of the string */ >> + chr = ff_wtoupper(chr); >> + >> +#else /* ANSI/OEM input */ >> + chr = (BYTE)*(*ptr)++; /* Get a byte */ >> + if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */ >> +#if FF_CODE_PAGE == 0 >> + if (ExCvt && chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper >> SBCS extended char */ >> +#elif FF_CODE_PAGE < 900 >> + if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS >> extended char */ >> +#endif >> +#if FF_CODE_PAGE == 0 || FF_CODE_PAGE >= 900 >> + if (dbc_1st((BYTE)chr)) { /* Get DBC 2nd byte if needed */ >> + chr = dbc_2nd((BYTE)**ptr) ? chr << 8 | (BYTE)*(*ptr)++ : 0; >> + } >> +#endif >> + >> +#endif >> + return chr; >> +} >> + >> + >> +static int pattern_match ( /* 0:mismatched, 1:matched */ >> + const TCHAR* pat, /* Matching pattern */ >> + const TCHAR* nam, /* String to be tested */ >> + UINT skip, /* Number of pre-skip chars (number of ?s, >> b8:infinite (* specified)) */ >> + UINT recur /* Recursion count */ >> +) >> +{ >> + const TCHAR *pptr, *nptr; >> + DWORD pchr, nchr; >> + UINT sk; >> + >> + >> + while ((skip & 0xFF) != 0) { /* Pre-skip name chars */ >> + if (!get_achar(&nam)) return 0; /* Branch mismatched if >> less name chars */ >> + skip--; >> + } >> + if (*pat == 0 && skip) return 1; /* Matched? (short circuit) */ >> + >> + do { >> + pptr = pat; nptr = nam; /* Top of pattern and name >> to match */ >> + for (;;) { >> + if (*pptr == '?' || *pptr == '*') { /* Wildcard term? */ >> + if (recur == 0) return 0; /* Too many wildcard >> terms? */ >> + sk = 0; >> + do { /* Analyze the wildcard term */ >> + if (*pptr++ == '?') sk++; else sk |= 0x100; >> + } while (*pptr == '?' || *pptr == '*'); >> + if (pattern_match(pptr, nptr, sk, recur - 1)) return >> 1; /* Test new branch (recursive call) */ >> + nchr = *nptr; break; /* Branch mismatched */ >> + } >> + pchr = get_achar(&pptr); /* Get a pattern char */ >> + nchr = get_achar(&nptr); /* Get a name char */ >> + if (pchr != nchr) break; /* Branch mismatched? */ >> + if (pchr == 0) return 1; /* Branch matched? (matched >> at end of both strings) */ >> + } >> + get_achar(&nam); /* nam++ */ >> + } while (skip && nchr); /* Retry until end of name if >> infinite search is specified */ >> + >> + return 0; >> +} >> + >> +#endif /* FF_USE_FIND && FF_FS_MINIMIZE <= 1 */ >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Pick a top segment and create the object name in directory >> form */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static FRESULT create_name ( /* FR_OK: successful, >> FR_INVALID_NAME: could not create */ >> + DIR* dp, /* Pointer to the directory object */ >> + const TCHAR** path /* Pointer to pointer to the >> segment in the path string */ >> +) >> +{ >> +#if FF_USE_LFN /* LFN configuration */ >> + BYTE b, cf; >> + WCHAR wc, *lfn; >> + DWORD uc; >> + UINT i, ni, si, di; >> + const TCHAR *p; >> + >> + >> + /* Create LFN into LFN working buffer */ >> + p = *path; lfn = dp->obj.fs->lfnbuf; di = 0; >> + for (;;) { >> + uc = tchar2uni(&p); /* Get a character */ >> + if (uc == 0xFFFFFFFF) return FR_INVALID_NAME; /* >> Invalid code or UTF decode error */ >> + if (uc >= 0x10000) lfn[di++] = (WCHAR)(uc >> 16); /* Store >> high surrogate if needed */ >> + wc = (WCHAR)uc; >> + if (wc < ' ' || wc == '/' || wc == '\\') break; /* Break >> if end of the path or a separator is found */ >> + if (wc < 0x80 && chk_chr("\"*:<>\?|\x7F", wc)) return >> FR_INVALID_NAME; /* Reject illegal characters for LFN */ >> + if (di >= FF_MAX_LFN) return FR_INVALID_NAME; /* Reject >> too long name */ >> + lfn[di++] = wc; /* Store the Unicode >> character */ >> + } >> + if (wc < ' ') { /* End of path? */ >> + cf = NS_LAST; /* Set last segment flag */ >> + } else { >> + cf = 0; /* Next segment follows */ >> + while (*p == '/' || *p == '\\') p++; /* Skip duplicated >> separators if exist */ >> + } >> + *path = p; /* Return pointer to the next >> segment */ >> + >> +#if FF_FS_RPATH != 0 >> + if ((di == 1 && lfn[di - 1] == '.') || >> + (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { >> /* Is this segment a dot name? */ >> + lfn[di] = 0; >> + for (i = 0; i < 11; i++) { /* Create dot name for SFN >> entry */ >> + dp->fn[i] = (i < di) ? '.' : ' '; >> + } >> + dp->fn[i] = cf | NS_DOT; /* This is a dot entry */ >> + return FR_OK; >> + } >> +#endif >> + while (di) { /* Snip off trailing spaces >> and dots if exist */ >> + wc = lfn[di - 1]; >> + if (wc != ' ' && wc != '.') break; >> + di--; >> + } >> + lfn[di] = 0; /* LFN is created into >> the working buffer */ >> + if (di == 0) return FR_INVALID_NAME; /* Reject null name */ >> + >> + /* Create SFN in directory form */ >> + for (si = 0; lfn[si] == ' '; si++) ; /* Remove leading spaces */ >> + if (si > 0 || lfn[si] == '.') cf |= NS_LOSS | NS_LFN; /* Is >> there any leading space or dot? */ >> + while (di > 0 && lfn[di - 1] != '.') di--; /* Find last dot >> (di<=si: no extension) */ >> + >> + mem_set(dp->fn, ' ', 11); >> + i = b = 0; ni = 8; >> + for (;;) { >> + wc = lfn[si++]; /* Get an LFN character */ >> + if (wc == 0) break; /* Break on end of the LFN */ >> + if (wc == ' ' || (wc == '.' && si != di)) { /* Remove >> embedded spaces and dots */ >> + cf |= NS_LOSS | NS_LFN; >> + continue; >> + } >> + >> + if (i >= ni || si == di) { /* End of field? */ >> + if (ni == 11) { /* Name extension >> overflow? */ >> + cf |= NS_LOSS | NS_LFN; >> + break; >> + } >> + if (si != di) cf |= NS_LOSS | NS_LFN; /* Name body >> overflow? */ >> + if (si > di) break; /* No name >> extension? */ >> + si = di; i = 8; ni = 11; b <<= 2; /* Enter name >> extension */ >> + continue; >> + } >> + >> + if (wc >= 0x80) { /* Is this a non-ASCII character? */ >> + cf |= NS_LFN; /* LFN entry needs to be created */ >> +#if FF_CODE_PAGE == 0 >> + if (ExCvt) { /* At SBCS */ >> + wc = ff_uni2oem(wc, CODEPAGE); /* Unicode >> ==> ANSI/OEM code */ >> + if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert >> extended character to upper (SBCS) */ >> + } else { /* At DBCS */ >> + wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* >> Unicode ==> Upper convert ==> ANSI/OEM code */ >> + } >> +#elif FF_CODE_PAGE < 900 /* SBCS cfg */ >> + wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> >> ANSI/OEM code */ >> + if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert >> extended character to upper (SBCS) */ >> +#else /* DBCS cfg */ >> + wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode >> ==> Upper convert ==> ANSI/OEM code */ >> +#endif >> + } >> + >> + if (wc >= 0x100) { /* Is this a DBC? */ >> + if (i >= ni - 1) { /* Field overflow? */ >> + cf |= NS_LOSS | NS_LFN; >> + i = ni; continue; /* Next field */ >> + } >> + dp->fn[i++] = (BYTE)(wc >> 8); /* Put 1st byte */ >> + } else { /* SBC */ >> + if (wc == 0 || chk_chr("+,;=[]", wc)) { /* Replace >> illegal characters for SFN if needed */ >> + wc = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ >> + } else { >> + if (IsUpper(wc)) { /* ASCII upper case? */ >> + b |= 2; >> + } >> + if (IsLower(wc)) { /* ASCII lower case? */ >> + b |= 1; wc -= 0x20; >> + } >> + } >> + } >> + dp->fn[i++] = (BYTE)wc; >> + } >> + >> + if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first >> character collides with DDEM, replace it with RDDEM */ >> + >> + if (ni == 8) b <<= 2; /* Shift capital flags if no >> extension */ >> + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; /* >> LFN entry needs to be created if composite capitals */ >> + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format >> without extended character, NT flags are created */ >> + if (b & 0x01) cf |= NS_EXT; /* NT flag (Extension has >> small capital letters only) */ >> + if (b & 0x04) cf |= NS_BODY; /* NT flag (Body has small >> capital letters only) */ >> + } >> + >> + dp->fn[NSFLAG] = cf; /* SFN is created into dp->fn[] */ >> + >> + return FR_OK; >> + >> + >> +#else /* FF_USE_LFN : Non-LFN configuration */ >> + BYTE c, d, *sfn; >> + UINT ni, si, i; >> + const char *p; >> + >> + /* Create file name in directory form */ >> + p = *path; sfn = dp->fn; >> + mem_set(sfn, ' ', 11); >> + si = i = 0; ni = 8; >> +#if FF_FS_RPATH != 0 >> + if (p[si] == '.') { /* Is this a dot entry? */ >> + for (;;) { >> + c = (BYTE)p[si++]; >> + if (c != '.' || si >= 3) break; >> + sfn[i++] = c; >> + } >> + if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; >> + *path = p + si; /* Return >> pointer to the next segment */ >> + sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* >> Set last segment flag if end of the path */ >> + return FR_OK; >> + } >> +#endif >> + for (;;) { >> + c = (BYTE)p[si++]; /* Get a byte */ >> + if (c <= ' ') break; /* Break if end of the path >> name */ >> + if (c == '/' || c == '\\') { /* Break if a separator is >> found */ >> + while (p[si] == '/' || p[si] == '\\') si++; /* Skip >> duplicated separator if exist */ >> + break; >> + } >> + if (c == '.' || i >= ni) { /* End of body or field >> overflow? */ >> + if (ni == 11 || c != '.') return FR_INVALID_NAME; /* >> Field overflow or invalid dot? */ >> + i = 8; ni = 11; /* Enter file extension >> field */ >> + continue; >> + } >> +#if FF_CODE_PAGE == 0 >> + if (ExCvt && c >= 0x80) { /* Is SBC extended >> character? */ >> + c = ExCvt[c & 0x7F]; /* To upper SBC extended >> character */ >> + } >> +#elif FF_CODE_PAGE < 900 >> + if (c >= 0x80) { /* Is SBC extended character? */ >> + c = ExCvt[c & 0x7F]; /* To upper SBC extended >> character */ >> + } >> +#endif >> + if (dbc_1st(c)) { /* Check if it is a DBC 1st >> byte */ >> + d = (BYTE)p[si++]; /* Get 2nd byte */ >> + if (!dbc_2nd(d) || i >= ni - 1) return >> FR_INVALID_NAME; /* Reject invalid DBC */ >> + sfn[i++] = c; >> + sfn[i++] = d; >> + } else { /* SBC */ >> + if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return >> FR_INVALID_NAME; /* Reject illegal chrs for SFN */ >> + if (IsLower(c)) c -= 0x20; /* To upper */ >> + sfn[i++] = c; >> + } >> + } >> + *path = p + si; /* Return pointer to the >> next segment */ >> + if (i == 0) return FR_INVALID_NAME; /* Reject nul string */ >> + >> + if (sfn[0] == DDEM) sfn[0] = RDDEM; /* If the first character >> collides with DDEM, replace it with RDDEM */ >> + sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0; /* Set last >> segment flag if end of the path */ >> + >> + return FR_OK; >> +#endif /* FF_USE_LFN */ >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Follow a file >> path */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error >> code */ >> + DIR* dp, /* Directory object to return last >> directory and found object */ >> + const TCHAR* path /* Full-path string to find a file >> or directory */ >> +) >> +{ >> + FRESULT res; >> + BYTE ns; >> + FATFS *fs = dp->obj.fs; >> + >> + >> +#if FF_FS_RPATH != 0 >> + if (*path != '/' && *path != '\\') { /* Without heading >> separator */ >> + dp->obj.sclust = fs->cdir; /* Start from >> current directory */ >> + } else >> +#endif >> + { /* With heading >> separator */ >> + while (*path == '/' || *path == '\\') path++; /* Strip >> heading separator */ >> + dp->obj.sclust = 0; /* Start from root >> directory */ >> + } >> +#if FF_FS_EXFAT >> + dp->obj.n_frag = 0; /* Invalidate last fragment counter of the >> object */ >> +#if FF_FS_RPATH != 0 >> + if (fs->fs_type == FS_EXFAT && dp->obj.sclust) { /* exFAT: >> Retrieve the sub-directory's status */ >> + DIR dj; >> + >> + dp->obj.c_scl = fs->cdc_scl; >> + dp->obj.c_size = fs->cdc_size; >> + dp->obj.c_ofs = fs->cdc_ofs; >> + res = load_obj_xdir(&dj, &dp->obj); >> + if (res != FR_OK) return res; >> + dp->obj.objsize = ld_dword(fs->dirbuf + XDIR_FileSize); >> + dp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; >> + } >> +#endif >> +#endif >> + >> + if ((UINT)*path < ' ') { /* Null path name is the >> origin directory itself */ >> + dp->fn[NSFLAG] = NS_NONAME; >> + res = dir_sdi(dp, 0); >> + >> + } else { /* Follow path */ >> + for (;;) { >> + res = create_name(dp, &path); /* Get a segment name of >> the path */ >> + if (res != FR_OK) break; >> + res = dir_find(dp); /* Find an object with >> the segment name */ >> + ns = dp->fn[NSFLAG]; >> + if (res != FR_OK) { /* Failed to find the >> object */ >> + if (res == FR_NO_FILE) { /* Object is not found */ >> + if (FF_FS_RPATH && (ns & NS_DOT)) { /* If dot >> entry is not exist, stay there */ >> + if (!(ns & NS_LAST)) continue; /* Continue >> to follow if not last segment */ >> + dp->fn[NSFLAG] = NS_NONAME; >> + res = FR_OK; >> + } else { /* Could not >> find the object */ >> + if (!(ns & NS_LAST)) res = FR_NO_PATH; /* >> Adjust error code if not last segment */ >> + } >> + } >> + break; >> + } >> + if (ns & NS_LAST) break; /* Last segment >> matched. Function completed. */ >> + /* Get into the sub-directory */ >> + if (!(dp->obj.attr & AM_DIR)) { /* It is not a >> sub-directory and cannot follow */ >> + res = FR_NO_PATH; break; >> + } >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { /* Save containing >> directory information for next dir */ >> + dp->obj.c_scl = dp->obj.sclust; >> + dp->obj.c_size = ((DWORD)dp->obj.objsize & >> 0xFFFFFF00) | dp->obj.stat; >> + dp->obj.c_ofs = dp->blk_ofs; >> + init_alloc_info(fs, &dp->obj); /* Open next >> directory */ >> + } else >> +#endif >> + { >> + dp->obj.sclust = ld_clust(fs, fs->win + dp->dptr % >> SS(fs)); /* Open next directory */ >> + } >> + } >> + } >> + >> + return res; >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Get logical drive number from path >> name */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static int get_ldnumber ( /* Returns logical drive number >> (-1:invalid drive number or null pointer) */ >> + const TCHAR** path /* Pointer to pointer to the path name */ >> +) >> +{ >> + const TCHAR *tp, *tt; >> + TCHAR tc; >> + int i, vol = -1; >> +#if FF_STR_VOLUME_ID /* Find string volume ID */ >> + const char *sp; >> + char c; >> +#endif >> + >> + tt = tp = *path; >> + if (!tp) return vol; /* Invalid path name? */ >> + do tc = *tt++; while ((UINT)tc >= (FF_USE_LFN ? ' ' : '!') && tc >> != ':'); /* Find a colon in the path */ >> + >> + if (tc == ':') { /* DOS/Windows style volume ID? */ >> + i = FF_VOLUMES; >> + if (IsDigit(*tp) && tp + 2 == tt) { /* Is there a numeric >> volume ID + colon? */ >> + i = (int)*tp - '0'; /* Get the LD number */ >> + } >> +#if FF_STR_VOLUME_ID == 1 /* Arbitrary string is enabled */ >> + else { >> + i = 0; >> + do { >> + sp = VolumeStr[i]; tp = *path; /* This string >> volume ID and path name */ >> + do { /* Compare the volume ID with path name */ >> + c = *sp++; tc = *tp++; >> + if (IsLower(c)) c -= 0x20; >> + if (IsLower(tc)) tc -= 0x20; >> + } while (c && (TCHAR)c == tc); >> + } while ((c || tp != tt) && ++i < FF_VOLUMES); /* >> Repeat for each id until pattern match */ >> + } >> +#endif >> + if (i < FF_VOLUMES) { /* If a volume ID is found, get the >> drive number and strip it */ >> + vol = i; /* Drive number */ >> + *path = tt; /* Snip the drive prefix off */ >> + } >> + return vol; >> + } >> +#if FF_STR_VOLUME_ID == 2 /* Unix style volume ID is enabled */ >> + if (*tp == '/') { >> + i = 0; >> + do { >> + sp = VolumeStr[i]; tp = *path; /* This string volume >> ID and path name */ >> + do { /* Compare the volume ID with path name */ >> + c = *sp++; tc = *(++tp); >> + if (IsLower(c)) c -= 0x20; >> + if (IsLower(tc)) tc -= 0x20; >> + } while (c && (TCHAR)c == tc); >> + } while ((c || (tc != '/' && (UINT)tc >= (FF_USE_LFN ? ' ' : >> '!'))) && ++i < FF_VOLUMES); /* Repeat for each ID until pattern >> match */ >> + if (i < FF_VOLUMES) { /* If a volume ID is found, get the >> drive number and strip it */ >> + vol = i; /* Drive number */ >> + *path = tp; /* Snip the drive prefix off */ >> + return vol; >> + } >> + } >> +#endif >> + /* No drive prefix is found */ >> +#if FF_FS_RPATH != 0 >> + vol = CurrVol; /* Default drive is current drive */ >> +#else >> + vol = 0; /* Default drive is 0 */ >> +#endif >> + return vol; /* Return the default drive */ >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* GPT support >> functions */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +#if FF_LBA64 >> + >> +/* Calculate CRC32 in byte-by-byte */ >> + >> +static DWORD crc32 ( /* Returns next CRC value */ >> + DWORD crc, /* Current CRC value */ >> + BYTE d /* A byte to be processed */ >> +) >> +{ >> + BYTE b; >> + >> + >> + for (b = 1; b; b <<= 1) { >> + crc ^= (d & b) ? 1 : 0; >> + crc = (crc & 1) ? crc >> 1 ^ 0xEDB88320 : crc >> 1; >> + } >> + return crc; >> +} >> + >> + >> +/* Check validity of GPT header */ >> + >> +static int test_gpt_header ( /* 0:Invalid, 1:Valid */ >> + const BYTE* gpth /* Pointer to the GPT header */ >> +) >> +{ >> + UINT i; >> + DWORD bcc; >> + >> + >> + if (mem_cmp(gpth + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", >> 16)) return 0; /* Check sign, version (1.0) and length (92) */ >> + for (i = 0, bcc = 0xFFFFFFFF; i < 92; i++) { /* Check >> header BCC */ >> + bcc = crc32(bcc, i - GPTH_Bcc < 4 ? 0 : gpth[i]); >> + } >> + if (~bcc != ld_dword(gpth + GPTH_Bcc)) return 0; >> + if (ld_dword(gpth + GPTH_PteSize) != SZ_GPTE) return 0; /* >> Table entry size (must be SZ_GPTE bytes) */ >> + if (ld_dword(gpth + GPTH_PtNum) > 128) return 0; /* Table size >> (must be 128 entries or less) */ >> + >> + return 1; >> +} >> + >> +#if !FF_FS_READONLY && FF_USE_MKFS >> + >> +/* Generate random value */ >> +static DWORD make_rand ( >> + DWORD seed, /* Seed value */ >> + BYTE* buff, /* Output buffer */ >> + UINT n /* Data length */ >> +) >> +{ >> + UINT r; >> + >> + >> + if (seed == 0) seed = 1; >> + do { >> + for (r = 0; r < 8; r++) seed = seed & 1 ? seed >> 1 ^ >> 0xA3000000 : seed >> 1; /* Shift 8 bits the 32-bit LFSR */ >> + *buff++ = (BYTE)seed; >> + } while (--n); >> + return seed; >> +} >> + >> +#endif >> +#endif >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Load a sector and check if it is an FAT >> VBR */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +/* Check what the sector is */ >> + >> +static UINT check_fs ( /* 0:FAT VBR, 1:exFAT VBR, 2:Not FAT and >> valid BS, 3:Not FAT and invalid BS, 4:Disk error */ >> + FATFS* fs, /* Filesystem object */ >> + LBA_t sect /* Sector to load and check if it is an >> FAT-VBR or not */ >> +) >> +{ >> + WORD w, sign; >> + BYTE b; >> + >> + >> + fs->wflag = 0; fs->winsect = (LBA_t)0 - 1; /* Invaidate >> window */ >> + if (move_window(fs, sect) != FR_OK) return 4; /* Load the boot >> sector */ >> + sign = ld_word(fs->win + BS_55AA); >> +#if FF_FS_EXFAT >> + if (sign == 0xAA55 && !mem_cmp(fs->win + BS_JmpBoot, >> "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* It is an exFAT VBR */ >> +#endif >> + b = fs->win[BS_JmpBoot]; >> + if (b == 0xEB || b == 0xE9 || b == 0xE8) { /* Valid JumpBoot >> code? (short jump, near jump or near call) */ >> + if (sign == 0xAA55 && !mem_cmp(fs->win + BS_FilSysType32, >> "FAT32 ", 8)) return 0; /* It is an FAT32 VBR */ >> + /* FAT volumes formatted with early MS-DOS lack boot >> signature and FAT string, so that we need to identify the FAT VBR >> without them. */ >> + w = ld_word(fs->win + BPB_BytsPerSec); >> + if ((w & (w - 1)) == 0 && w >= FF_MIN_SS && w <= FF_MAX_SS) >> { /* Properness of sector size */ >> + b = fs->win[BPB_SecPerClus]; >> + if (b != 0 && (b & (b - 1)) == 0 >> /* Properness of cluster size */ >> + && (fs->win[BPB_NumFATs] == 1 || fs->win[BPB_NumFATs] == >> 2) /* Properness of number of FATs */ >> + && ld_word(fs->win + BPB_RootEntCnt) != 0 >> /* Properness of root entry count */ >> + && ld_word(fs->win + BPB_FATSz16) != 0) { >> /* Properness of FAT size */ >> + return 0; /* Sector can be presumed an FAT VBR */ >> + } >> + } >> + } >> + return sign == 0xAA55 ? 2 : 3; /* Not an FAT VBR (valid or >> invalid BS) */ >> +} >> + >> + >> +/* Find an FAT volume */ >> +/* (It supports only generic partitioning rules, MBR, GPT and SFD) */ >> + >> +static UINT find_volume ( /* Returns BS status found in the >> hosting drive */ >> + FATFS* fs, /* Filesystem object */ >> + UINT part /* Partition to fined = 0:auto, 1..:forced */ >> +) >> +{ >> + UINT fmt, i; >> + DWORD mbr_pt[4]; >> + >> + >> + fmt = check_fs(fs, 0); /* Load sector 0 and check >> if it is an FAT VBR as SFD */ >> + if (fmt != 2 && (fmt >= 3 || part == 0)) return fmt; /* >> Returns if it is a FAT VBR as auto scan, not a BS or disk error */ >> + >> + /* Sector 0 is not an FAT VBR or forced partition number wants a >> partition */ >> + >> +#if FF_LBA64 >> + if (fs->win[MBR_Table + PTE_System] == 0xEE) { /* GPT >> protective MBR? */ >> + DWORD n_ent, v_ent, ofs; >> + QWORD pt_lba; >> + >> + if (move_window(fs, 1) != FR_OK) return 4; /* Load GPT >> header sector (next to MBR) */ >> + if (!test_gpt_header(fs->win)) return 3; /* Check if GPT >> header is valid */ >> + n_ent = ld_dword(fs->win + GPTH_PtNum); /* Number of >> entries */ >> + pt_lba = ld_qword(fs->win + GPTH_PtOfs); /* Table location */ >> + for (v_ent = i = 0; i < n_ent; i++) { /* Find FAT >> partition */ >> + if (move_window(fs, pt_lba + i * SZ_GPTE / SS(fs)) != >> FR_OK) return 4; /* PT sector */ >> + ofs = i * SZ_GPTE % >> SS(fs); /* Offset in >> the sector */ >> + if (!mem_cmp(fs->win + ofs + GPTE_PtGuid, GUID_MS_Basic, >> 16)) { /* MS basic data partition? */ >> + v_ent++; >> + fmt = check_fs(fs, ld_qword(fs->win + ofs + >> GPTE_FstLba)); /* Load VBR and check status */ >> + if (part == 0 && fmt <= 1) return fmt; /* >> Auto search (valid FAT volume found first) */ >> + if (part != 0 && v_ent == part) return fmt; /* >> Forced partition order (regardless of it is valid or not) */ >> + } >> + } >> + return 3; /* Not found */ >> + } >> +#endif >> + if (FF_MULTI_PARTITION && part > 4) return 3; /* MBR has 4 >> partitions max */ >> + for (i = 0; i < 4; i++) { /* Load partition offset in the >> MBR */ >> + mbr_pt[i] = ld_dword(fs->win + MBR_Table + i * SZ_PTE + >> PTE_StLba); >> + } >> + i = part ? part - 1 : 0; /* Table index to find first */ >> + do { /* Find an FAT volume */ >> + fmt = mbr_pt[i] ? check_fs(fs, mbr_pt[i]) : 3; /* Check if >> the partition is FAT */ >> + } while (part == 0 && fmt >= 2 && ++i < 4); >> + return fmt; >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Determine logical drive number and mount the volume if >> needed */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an >> error occurred */ >> + const TCHAR** path, /* Pointer to pointer to the path >> name (drive number) */ >> + FATFS** rfs, /* Pointer to pointer to the found >> filesystem object */ >> + BYTE mode /* !=0: Check write protection for >> write access */ >> +) >> +{ >> + int vol; >> + DSTATUS stat; >> + LBA_t bsect; >> + DWORD tsect, sysect, fasize, nclst, szbfat; >> + WORD nrsv; >> + FATFS *fs; >> + UINT fmt; >> + >> + >> + /* Get logical drive number */ >> + *rfs = 0; >> + vol = get_ldnumber(path); >> + if (vol < 0) return FR_INVALID_DRIVE; >> + >> + /* Check if the filesystem object is valid or not */ >> + fs = FatFs[vol]; /* Get pointer to the >> filesystem object */ >> + if (!fs) return FR_NOT_ENABLED; /* Is the filesystem >> object available? */ >> +#if FF_FS_REENTRANT >> + if (!lock_fs(fs)) return FR_TIMEOUT; /* Lock the volume */ >> +#endif >> + *rfs = fs; /* Return pointer to the >> filesystem object */ >> + >> + mode &= (BYTE)~FA_READ; /* Desired access mode, >> write access or not */ >> + if (fs->fs_type != 0) { /* If the volume has been >> mounted */ >> + stat = disk_status(fs->pdrv); >> + if (!(stat & STA_NOINIT)) { /* and the physical drive >> is kept initialized */ >> + if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { >> /* Check write protection if needed */ >> + return FR_WRITE_PROTECTED; >> + } >> + return FR_OK; /* The filesystem object is >> already valid */ >> + } >> + } >> + >> + /* The filesystem object is not valid. */ >> + /* Following code attempts to mount the volume. (find a FAT >> volume, analyze the BPB and initialize the filesystem object) */ >> + >> + fs->fs_type = 0; /* Clear the filesystem >> object */ >> + fs->pdrv = LD2PD(vol); /* Volume hosting physical >> drive */ >> + stat = disk_initialize(fs->pdrv); /* Initialize the physical >> drive */ >> + if (stat & STA_NOINIT) { /* Check if the >> initialization succeeded */ >> + return FR_NOT_READY; /* Failed to initialize due >> to no medium or hard error */ >> + } >> + if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check >> disk write protection if needed */ >> + return FR_WRITE_PROTECTED; >> + } >> +#if FF_MAX_SS != FF_MIN_SS /* Get sector size >> (multiple sector size cfg only) */ >> + if (disk_ioctl(fs->pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) >> return FR_DISK_ERR; >> + if (SS(fs) > FF_MAX_SS || SS(fs) < FF_MIN_SS || (SS(fs) & (SS(fs) >> - 1))) return FR_DISK_ERR; >> +#endif >> + >> + /* Find an FAT volume on the drive */ >> + fmt = find_volume(fs, LD2PT(vol)); >> + if (fmt == 4) return FR_DISK_ERR; /* An error occured in >> the disk I/O layer */ >> + if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is >> found */ >> + bsect = fs->winsect; /* Volume location */ >> + >> + /* An FAT volume is found (bsect). Following code initializes the >> filesystem object */ >> + >> +#if FF_FS_EXFAT >> + if (fmt == 1) { >> + QWORD maxlba; >> + DWORD so, cv, bcl, i; >> + >> + for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == >> 0; i++) ; /* Check zero filler */ >> + if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM; >> + >> + if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return >> FR_NO_FILESYSTEM; /* Check exFAT version (must be version 1.0) */ >> + >> + if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) { /* >> (BPB_BytsPerSecEx must be equal to the physical sector size) */ >> + return FR_NO_FILESYSTEM; >> + } >> + >> + maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last >> LBA + 1 of the volume */ >> + if (!FF_LBA64 && maxlba >= 0x100000000) return >> FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */ >> + >> + fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of >> sectors per FAT */ >> + >> + fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of >> FATs */ >> + if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* (Supports >> only 1 FAT) */ >> + >> + fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster >> size */ >> + if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must >> be 1..32768) */ >> + >> + nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number >> of clusters */ >> + if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too >> many clusters) */ >> + fs->n_fatent = nclst + 2; >> + >> + /* Boundaries and Limits */ >> + fs->volbase = bsect; >> + fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx); >> + fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx); >> + if (maxlba < (QWORD)fs->database + nclst * fs->csize) return >> FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size >> requiered) */ >> + fs->dirbase = ld_dword(fs->win + BPB_RootClusEx); >> + >> + /* Get bitmap location and check if it is contiguous >> (implementation assumption) */ >> + so = i = 0; >> + for (;;) { /* Find the bitmap entry in the root directory >> (in only first cluster) */ >> + if (i == 0) { >> + if (so >= fs->csize) return FR_NO_FILESYSTEM; /* >> Not found? */ >> + if (move_window(fs, clst2sect(fs, (DWORD)fs->dirbase) >> + so) != FR_OK) return FR_DISK_ERR; >> + so++; >> + } >> + if (fs->win[i] == ET_BITMAP) break; /* Is >> it a bitmap entry? */ >> + i = (i + SZDIRE) % SS(fs); /* Next entry */ >> + } >> + bcl = ld_dword(fs->win + i + 20); /* >> Bitmap cluster */ >> + if (bcl < 2 || bcl >= fs->n_fatent) return FR_NO_FILESYSTEM; >> + fs->bitbase = fs->database + fs->csize * (bcl - 2); /* >> Bitmap sector */ >> + for (;;) { /* Check if bitmap is contiguous */ >> + if (move_window(fs, fs->fatbase + bcl / (SS(fs) / 4)) != >> FR_OK) return FR_DISK_ERR; >> + cv = ld_dword(fs->win + bcl % (SS(fs) / 4) * 4); >> + if (cv == 0xFFFFFFFF) break; /* Last link? */ >> + if (cv != ++bcl) return FR_NO_FILESYSTEM; /* >> Fragmented? */ >> + } >> + >> +#if !FF_FS_READONLY >> + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* >> Initialize cluster allocation information */ >> +#endif >> + fmt = FS_EXFAT; /* FAT sub-type */ >> + } else >> +#endif /* FF_FS_EXFAT */ >> + { >> + if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return >> FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical >> sector size) */ >> + >> + fasize = ld_word(fs->win + BPB_FATSz16); /* Number of >> sectors per FAT */ >> + if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32); >> + fs->fsize = fasize; >> + >> + fs->n_fats = fs->win[BPB_NumFATs]; /* Number >> of FATs */ >> + if (fs->n_fats != 1 && fs->n_fats != 2) return >> FR_NO_FILESYSTEM; /* (Must be 1 or 2) */ >> + fasize *= fs->n_fats; /* Number of >> sectors for FAT area */ >> + >> + fs->csize = fs->win[BPB_SecPerClus]; /* Cluster >> size */ >> + if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return >> FR_NO_FILESYSTEM; /* (Must be power of 2) */ >> + >> + fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* >> Number of root directory entries */ >> + if (fs->n_rootdir % (SS(fs) / SZDIRE)) return >> FR_NO_FILESYSTEM; /* (Must be sector aligned) */ >> + >> + tsect = ld_word(fs->win + BPB_TotSec16); /* Number of >> sectors on the volume */ >> + if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32); >> + >> + nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of >> reserved sectors */ >> + if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must >> not be 0) */ >> + >> + /* Determine the FAT sub type */ >> + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / >> SZDIRE); /* RSV + FAT + DIR */ >> + if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid >> volume size) */ >> + nclst = (tsect - sysect) / fs->csize; /* Number of >> clusters */ >> + if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid >> volume size) */ >> + fmt = 0; >> + if (nclst <= MAX_FAT32) fmt = FS_FAT32; >> + if (nclst <= MAX_FAT16) fmt = FS_FAT16; >> + if (nclst <= MAX_FAT12) fmt = FS_FAT12; >> + if (fmt == 0) return FR_NO_FILESYSTEM; >> + >> + /* Boundaries and Limits */ >> + fs->n_fatent = nclst + 2; /* Number of >> FAT entries */ >> + fs->volbase = bsect; /* Volume >> start sector */ >> + fs->fatbase = bsect + nrsv; /* FAT start >> sector */ >> + fs->database = bsect + sysect; /* Data >> start sector */ >> + if (fmt == FS_FAT32) { >> + if (ld_word(fs->win + BPB_FSVer32) != 0) return >> FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */ >> + if (fs->n_rootdir != 0) return FR_NO_FILESYSTEM; /* >> (BPB_RootEntCnt must be 0) */ >> + fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* >> Root directory start cluster */ >> + szbfat = fs->n_fatent * 4; /* (Needed >> FAT size) */ >> + } else { >> + if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM; /* >> (BPB_RootEntCnt must not be 0) */ >> + fs->dirbase = fs->fatbase + fasize; /* Root >> directory start sector */ >> + szbfat = (fmt == FS_FAT16) ? /* (Needed >> FAT size) */ >> + fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + >> (fs->n_fatent & 1); >> + } >> + if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return >> FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size >> needed) */ >> + >> +#if !FF_FS_READONLY >> + /* Get FSInfo if available */ >> + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* >> Initialize cluster allocation information */ >> + fs->fsi_flag = 0x80; >> +#if (FF_FS_NOFSINFO & 3) != 3 >> + if (fmt == FS_FAT32 /* Allow to update FSInfo >> only if BPB_FSInfo32 == 1 */ >> + && ld_word(fs->win + BPB_FSInfo32) == 1 >> + && move_window(fs, bsect + 1) == FR_OK) >> + { >> + fs->fsi_flag = 0; >> + if (ld_word(fs->win + BS_55AA) == 0xAA55 /* Load >> FSInfo data if available */ >> + && ld_dword(fs->win + FSI_LeadSig) == 0x41615252 >> + && ld_dword(fs->win + FSI_StrucSig) == 0x61417272) >> + { >> +#if (FF_FS_NOFSINFO & 1) == 0 >> + fs->free_clst = ld_dword(fs->win + FSI_Free_Count); >> +#endif >> +#if (FF_FS_NOFSINFO & 2) == 0 >> + fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free); >> +#endif >> + } >> + } >> +#endif /* (FF_FS_NOFSINFO & 3) != 3 */ >> +#endif /* !FF_FS_READONLY */ >> + } >> + >> + fs->fs_type = (BYTE)fmt;/* FAT sub-type */ >> + fs->id = ++Fsid; /* Volume mount ID */ >> +#if FF_USE_LFN == 1 >> + fs->lfnbuf = LfnBuf; /* Static LFN working buffer */ >> +#if FF_FS_EXFAT >> + fs->dirbuf = DirBuf; /* Static directory block scratchpad >> buuffer */ >> +#endif >> +#endif >> +#if FF_FS_RPATH != 0 >> + fs->cdir = 0; /* Initialize current directory */ >> +#endif >> +#if FF_FS_LOCK != 0 /* Clear file lock semaphores */ >> + clear_lock(fs); >> +#endif >> + return FR_OK; >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Check if the file/directory object is valid or >> not */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ >> + FFOBJID* obj, /* Pointer to the FFOBJID, the 1st >> member in the FIL/DIR object, to check validity */ >> + FATFS** rfs /* Pointer to pointer to the owner >> filesystem object to return */ >> +) >> +{ >> + FRESULT res = FR_INVALID_OBJECT; >> + >> + >> + if (obj && obj->fs && obj->fs->fs_type && obj->id == obj->fs->id) >> { /* Test if the object is valid */ >> +#if FF_FS_REENTRANT >> + if (lock_fs(obj->fs)) { /* Obtain the filesystem object */ >> + if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test >> if the phsical drive is kept initialized */ >> + res = FR_OK; >> + } else { >> + unlock_fs(obj->fs, FR_OK); >> + } >> + } else { >> + res = FR_TIMEOUT; >> + } >> +#else >> + if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if >> the phsical drive is kept initialized */ >> + res = FR_OK; >> + } >> +#endif >> + } >> + *rfs = (res == FR_OK) ? obj->fs : 0; /* Corresponding >> filesystem object */ >> + return res; >> +} >> + >> + >> + >> + >> +/*--------------------------------------------------------------------------- >> >> + >> + Public Functions (FatFs API) >> + >> +----------------------------------------------------------------------------*/ >> >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Mount/Unmount a Logical >> Drive */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_mount ( >> + FATFS* fs, /* Pointer to the filesystem object >> (NULL:unmount)*/ >> + const TCHAR* path, /* Logical drive number to be >> mounted/unmounted */ >> + BYTE opt /* Mode option 0:Do not mount (delayed >> mount), 1:Mount immediately */ >> +) >> +{ >> + FATFS *cfs; >> + int vol; >> + FRESULT res; >> + const TCHAR *rp = path; >> + >> + >> + /* Get logical drive number */ >> + vol = get_ldnumber(&rp); >> + if (vol < 0) return FR_INVALID_DRIVE; >> + cfs = FatFs[vol]; /* Pointer to fs object */ >> + >> + if (cfs) { >> +#if FF_FS_LOCK != 0 >> + clear_lock(cfs); >> +#endif >> +#if FF_FS_REENTRANT /* Discard sync object of >> the current volume */ >> + if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR; >> +#endif >> + cfs->fs_type = 0; /* Clear old fs object */ >> + } >> + >> + if (fs) { >> + fs->fs_type = 0; /* Clear new fs object */ >> +#if FF_FS_REENTRANT /* Create sync object for >> the new volume */ >> + if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR; >> +#endif >> + } >> + FatFs[vol] = fs; /* Register new fs object */ >> + >> + if (opt == 0) return FR_OK; /* Do not mount now, it >> will be mounted later */ >> + >> + res = mount_volume(&path, &fs, 0); /* Force mounted the volume */ >> + LEAVE_FF(fs, res); >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Open or Create a >> File */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_open ( >> + FIL* fp, /* Pointer to the blank file object */ >> + const TCHAR* path, /* Pointer to the file name */ >> + BYTE mode /* Access mode and file open mode flags */ >> +) >> +{ >> + FRESULT res; >> + DIR dj; >> + FATFS *fs; >> +#if !FF_FS_READONLY >> + DWORD cl, bcs, clst, tm; >> + LBA_t sc; >> + FSIZE_t ofs; >> +#endif >> + DEF_NAMBUF >> + >> + >> + if (!fp) return FR_INVALID_OBJECT; >> + >> + /* Get logical drive number */ >> + mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | >> FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND; >> + res = mount_volume(&path, &fs, mode); >> + if (res == FR_OK) { >> + dj.obj.fs = fs; >> + INIT_NAMBUF(fs); >> + res = follow_path(&dj, path); /* Follow the file path */ >> +#if !FF_FS_READONLY /* Read/Write configuration */ >> + if (res == FR_OK) { >> + if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory >> itself? */ >> + res = FR_INVALID_NAME; >> + } >> +#if FF_FS_LOCK != 0 >> + else { >> + res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : >> 0); /* Check if the file can be used */ >> + } >> +#endif >> + } >> + /* Create or Open a file */ >> + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | >> FA_CREATE_NEW)) { >> + if (res != FR_OK) { /* No file, create >> new */ >> + if (res == FR_NO_FILE) { /* There is no file >> to open, create a new entry */ >> +#if FF_FS_LOCK != 0 >> + res = enq_lock() ? dir_register(&dj) : >> FR_TOO_MANY_OPEN_FILES; >> +#else >> + res = dir_register(&dj); >> +#endif >> + } >> + mode |= FA_CREATE_ALWAYS; /* File is created */ >> + } >> + else { /* Any object with >> the same name is already existing */ >> + if (dj.obj.attr & (AM_RDO | AM_DIR)) { /* Cannot >> overwrite it (R/O or DIR) */ >> + res = FR_DENIED; >> + } else { >> + if (mode & FA_CREATE_NEW) res = FR_EXIST; /* >> Cannot create as new file */ >> + } >> + } >> + if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* >> Truncate the file if overwrite mode */ >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { >> + /* Get current allocation info */ >> + fp->obj.fs = fs; >> + init_alloc_info(fs, &fp->obj); >> + /* Set directory entry block initial state */ >> + mem_set(fs->dirbuf + 2, 0, 30); /* Clear >> 85 entry except for NumSec */ >> + mem_set(fs->dirbuf + 38, 0, 26); /* Clear C0 >> entry except for NumName and NameHash */ >> + fs->dirbuf[XDIR_Attr] = AM_ARC; >> + st_dword(fs->dirbuf + XDIR_CrtTime, GET_FATTIME()); >> + fs->dirbuf[XDIR_GenFlags] = 1; >> + res = store_xdir(&dj); >> + if (res == FR_OK && fp->obj.sclust != 0) { /* >> Remove the cluster chain if exist */ >> + res = remove_chain(&fp->obj, fp->obj.sclust, 0); >> + fs->last_clst = fp->obj.sclust - 1; /* >> Reuse the cluster hole */ >> + } >> + } else >> +#endif >> + { >> + /* Set directory entry initial state */ >> + tm = GET_FATTIME(); /* Set >> created time */ >> + st_dword(dj.dir + DIR_CrtTime, tm); >> + st_dword(dj.dir + DIR_ModTime, tm); >> + cl = ld_clust(fs, dj.dir); /* Get >> current cluster chain */ >> + dj.dir[DIR_Attr] = AM_ARC; /* Reset >> attribute */ >> + st_clust(fs, dj.dir, 0); /* Reset file >> allocation info */ >> + st_dword(dj.dir + DIR_FileSize, 0); >> + fs->wflag = 1; >> + if (cl != 0) { /* Remove >> the cluster chain if exist */ >> + sc = fs->winsect; >> + res = remove_chain(&dj.obj, cl, 0); >> + if (res == FR_OK) { >> + res = move_window(fs, sc); >> + fs->last_clst = cl - 1; /* Reuse >> the cluster hole */ >> + } >> + } >> + } >> + } >> + } >> + else { /* Open an existing file */ >> + if (res == FR_OK) { /* Is the object >> exsiting? */ >> + if (dj.obj.attr & AM_DIR) { /* File open >> against a directory */ >> + res = FR_NO_FILE; >> + } else { >> + if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) >> { /* Write mode open against R/O file */ >> + res = FR_DENIED; >> + } >> + } >> + } >> + } >> + if (res == FR_OK) { >> + if (mode & FA_CREATE_ALWAYS) mode |= FA_MODIFIED; /* >> Set file change flag if created or overwritten */ >> + fp->dir_sect = fs->winsect; /* Pointer to the >> directory entry */ >> + fp->dir_ptr = dj.dir; >> +#if FF_FS_LOCK != 0 >> + fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : >> 0); /* Lock the file for this session */ >> + if (fp->obj.lockid == 0) res = FR_INT_ERR; >> +#endif >> + } >> +#else /* R/O configuration */ >> + if (res == FR_OK) { >> + if (dj.fn[NSFLAG] & NS_NONAME) { /* Is it origin >> directory itself? */ >> + res = FR_INVALID_NAME; >> + } else { >> + if (dj.obj.attr & AM_DIR) { /* Is it a >> directory? */ >> + res = FR_NO_FILE; >> + } >> + } >> + } >> +#endif >> + >> + if (res == FR_OK) { >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { >> + fp->obj.c_scl = >> dj.obj.sclust; /* Get containing directory >> info */ >> + fp->obj.c_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) >> | dj.obj.stat; >> + fp->obj.c_ofs = dj.blk_ofs; >> + init_alloc_info(fs, &fp->obj); >> + } else >> +#endif >> + { >> + fp->obj.sclust = ld_clust(fs, >> dj.dir); /* Get object allocation info */ >> + fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize); >> + } >> +#if FF_USE_FASTSEEK >> + fp->cltbl = 0; /* Disable fast seek mode */ >> +#endif >> + fp->obj.fs = fs; /* Validate the file object */ >> + fp->obj.id = fs->id; >> + fp->flag = mode; /* Set file access mode */ >> + fp->err = 0; /* Clear error flag */ >> + fp->sect = 0; /* Invalidate current data >> sector */ >> + fp->fptr = 0; /* Set file pointer top of the >> file */ >> +#if !FF_FS_READONLY >> +#if !FF_FS_TINY >> + mem_set(fp->buf, 0, sizeof fp->buf); /* Clear sector >> buffer */ >> +#endif >> + if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* >> Seek to end of file if FA_OPEN_APPEND is specified */ >> + fp->fptr = fp->obj.objsize; /* Offset to >> seek */ >> + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size >> in byte */ >> + clst = fp->obj.sclust; /* Follow the >> cluster chain */ >> + for (ofs = fp->obj.objsize; res == FR_OK && ofs > >> bcs; ofs -= bcs) { >> + clst = get_fat(&fp->obj, clst); >> + if (clst <= 1) res = FR_INT_ERR; >> + if (clst == 0xFFFFFFFF) res = FR_DISK_ERR; >> + } >> + fp->clust = clst; >> + if (res == FR_OK && ofs % SS(fs)) { /* Fill sector >> buffer if not on the sector boundary */ >> + sc = clst2sect(fs, clst); >> + if (sc == 0) { >> + res = FR_INT_ERR; >> + } else { >> + fp->sect = sc + (DWORD)(ofs / SS(fs)); >> +#if !FF_FS_TINY >> + if (disk_read(fs->pdrv, fp->buf, fp->sect, 1) >> != RES_OK) res = FR_DISK_ERR; >> +#endif >> + } >> + } >> + } >> +#endif >> + } >> + >> + FREE_NAMBUF(); >> + } >> + >> + if (res != FR_OK) fp->obj.fs = 0; /* Invalidate file object on >> error */ >> + >> + LEAVE_FF(fs, res); >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Read >> File */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_read ( >> + FIL* fp, /* Pointer to the file object */ >> + void* buff, /* Pointer to data buffer */ >> + UINT btr, /* Number of bytes to read */ >> + UINT* br /* Pointer to number of bytes read */ >> +) >> +{ >> + FRESULT res; >> + FATFS *fs; >> + DWORD clst; >> + LBA_t sect; >> + FSIZE_t remain; >> + UINT rcnt, cc, csect; >> + BYTE *rbuff = (BYTE*)buff; >> + >> + >> + *br = 0; /* Clear read byte counter */ >> + res = validate(&fp->obj, &fs); /* Check validity >> of the file object */ >> + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) >> LEAVE_FF(fs, res); /* Check validity */ >> + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check >> access mode */ >> + remain = fp->obj.objsize - fp->fptr; >> + if (btr > remain) btr = (UINT)remain; /* Truncate btr by >> remaining bytes */ >> + >> + for ( ; btr; /* Repeat until btr >> bytes read */ >> + btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) { >> + if (fp->fptr % SS(fs) == 0) { /* On the sector >> boundary? */ >> + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); >> /* Sector offset in the cluster */ >> + if (csect == 0) { /* On the cluster >> boundary? */ >> + if (fp->fptr == 0) { /* On the top of the >> file? */ >> + clst = fp->obj.sclust; /* Follow cluster >> chain from the origin */ >> + } else { /* Middle or end of >> the file */ >> +#if FF_USE_FASTSEEK >> + if (fp->cltbl) { >> + clst = clmt_clust(fp, fp->fptr); /* Get >> cluster# from the CLMT */ >> + } else >> +#endif >> + { >> + clst = get_fat(&fp->obj, fp->clust); /* >> Follow cluster chain on the FAT */ >> + } >> + } >> + if (clst < 2) ABORT(fs, FR_INT_ERR); >> + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); >> + fp->clust = clst; /* Update current >> cluster */ >> + } >> + sect = clst2sect(fs, fp->clust); /* Get current sector */ >> + if (sect == 0) ABORT(fs, FR_INT_ERR); >> + sect += csect; >> + cc = btr / SS(fs); /* When remaining >> bytes >= sector size, */ >> + if (cc > 0) { /* Read maximum >> contiguous sectors directly */ >> + if (csect + cc > fs->csize) { /* Clip at cluster >> boundary */ >> + cc = fs->csize - csect; >> + } >> + if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) >> ABORT(fs, FR_DISK_ERR); >> +#if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of >> the read sectors with cached data if it contains a dirty sector */ >> +#if FF_FS_TINY >> + if (fs->wflag && fs->winsect - sect < cc) { >> + mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), >> fs->win, SS(fs)); >> + } >> +#else >> + if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) { >> + mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), >> fp->buf, SS(fs)); >> + } >> +#endif >> +#endif >> + rcnt = SS(fs) * cc; /* Number of bytes >> transferred */ >> + continue; >> + } >> +#if !FF_FS_TINY >> + if (fp->sect != sect) { /* Load data sector if >> not in cache */ >> +#if !FF_FS_READONLY >> + if (fp->flag & FA_DIRTY) { /* Write-back dirty >> sector cache */ >> + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != >> RES_OK) ABORT(fs, FR_DISK_ERR); >> + fp->flag &= (BYTE)~FA_DIRTY; >> + } >> +#endif >> + if (disk_read(fs->pdrv, fp->buf, sect, 1) != >> RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ >> + } >> +#endif >> + fp->sect = sect; >> + } >> + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of >> bytes remains in the sector */ >> + if (rcnt > btr) rcnt = btr; /* Clip it by >> btr if needed */ >> +#if FF_FS_TINY >> + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, >> FR_DISK_ERR); /* Move sector window */ >> + mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* >> Extract partial sector */ >> +#else >> + mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* >> Extract partial sector */ >> +#endif >> + } >> + >> + LEAVE_FF(fs, FR_OK); >> +} >> + >> + >> + >> + >> +#if !FF_FS_READONLY >> +/*-----------------------------------------------------------------------*/ >> >> +/* Write >> File */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_write ( >> + FIL* fp, /* Pointer to the file object */ >> + const void* buff, /* Pointer to the data to be written */ >> + UINT btw, /* Number of bytes to write */ >> + UINT* bw /* Pointer to number of bytes written */ >> +) >> +{ >> + FRESULT res; >> + FATFS *fs; >> + DWORD clst; >> + LBA_t sect; >> + UINT wcnt, cc, csect; >> + const BYTE *wbuff = (const BYTE*)buff; >> + >> + >> + *bw = 0; /* Clear write byte counter */ >> + res = validate(&fp->obj, &fs); /* Check validity of >> the file object */ >> + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) >> LEAVE_FF(fs, res); /* Check validity */ >> + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check >> access mode */ >> + >> + /* Check fptr wrap-around (file size cannot reach 4 GiB at FAT >> volume) */ >> + if ((!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr >> + btw) < (DWORD)fp->fptr) { >> + btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr); >> + } >> + >> + for ( ; btw; /* Repeat until all data >> written */ >> + btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, >> fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : >> fp->obj.objsize) { >> + if (fp->fptr % SS(fs) == 0) { /* On the sector >> boundary? */ >> + csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); >> /* Sector offset in the cluster */ >> + if (csect == 0) { /* On the cluster >> boundary? */ >> + if (fp->fptr == 0) { /* On the top of the >> file? */ >> + clst = fp->obj.sclust; /* Follow from the >> origin */ >> + if (clst == 0) { /* If no cluster is >> allocated, */ >> + clst = create_chain(&fp->obj, 0); /* >> create a new cluster chain */ >> + } >> + } else { /* On the middle or end >> of the file */ >> +#if FF_USE_FASTSEEK >> + if (fp->cltbl) { >> + clst = clmt_clust(fp, fp->fptr); /* Get >> cluster# from the CLMT */ >> + } else >> +#endif >> + { >> + clst = create_chain(&fp->obj, fp->clust); >> /* Follow or stretch cluster chain on the FAT */ >> + } >> + } >> + if (clst == 0) break; /* Could not allocate a >> new cluster (disk full) */ >> + if (clst == 1) ABORT(fs, FR_INT_ERR); >> + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); >> + fp->clust = clst; /* Update current >> cluster */ >> + if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* >> Set start cluster if the first write */ >> + } >> +#if FF_FS_TINY >> + if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) >> ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */ >> +#else >> + if (fp->flag & FA_DIRTY) { /* Write-back sector >> cache */ >> + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != >> RES_OK) ABORT(fs, FR_DISK_ERR); >> + fp->flag &= (BYTE)~FA_DIRTY; >> + } >> +#endif >> + sect = clst2sect(fs, fp->clust); /* Get current sector */ >> + if (sect == 0) ABORT(fs, FR_INT_ERR); >> + sect += csect; >> + cc = btw / SS(fs); /* When remaining bytes >> >= sector size, */ >> + if (cc > 0) { /* Write maximum >> contiguous sectors directly */ >> + if (csect + cc > fs->csize) { /* Clip at cluster >> boundary */ >> + cc = fs->csize - csect; >> + } >> + if (disk_write(fs->pdrv, wbuff, sect, cc) != RES_OK) >> ABORT(fs, FR_DISK_ERR); >> +#if FF_FS_MINIMIZE <= 2 >> +#if FF_FS_TINY >> + if (fs->winsect - sect < cc) { /* Refill sector >> cache if it gets invalidated by the direct write */ >> + mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * >> SS(fs)), SS(fs)); >> + fs->wflag = 0; >> + } >> +#else >> + if (fp->sect - sect < cc) { /* Refill sector cache if >> it gets invalidated by the direct write */ >> + mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * >> SS(fs)), SS(fs)); >> + fp->flag &= (BYTE)~FA_DIRTY; >> + } >> +#endif >> +#endif >> + wcnt = SS(fs) * cc; /* Number of bytes >> transferred */ >> + continue; >> + } >> +#if FF_FS_TINY >> + if (fp->fptr >= fp->obj.objsize) { /* Avoid silly >> cache filling on the growing edge */ >> + if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); >> + fs->winsect = sect; >> + } >> +#else >> + if (fp->sect != sect && /* Fill sector cache with >> file data */ >> + fp->fptr < fp->obj.objsize && >> + disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) { >> + ABORT(fs, FR_DISK_ERR); >> + } >> +#endif >> + fp->sect = sect; >> + } >> + wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of >> bytes remains in the sector */ >> + if (wcnt > btw) wcnt = btw; /* Clip it by >> btw if needed */ >> +#if FF_FS_TINY >> + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, >> FR_DISK_ERR); /* Move sector window */ >> + mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit >> data to the sector */ >> + fs->wflag = 1; >> +#else >> + mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit >> data to the sector */ >> + fp->flag |= FA_DIRTY; >> +#endif >> + } >> + >> + fp->flag |= FA_MODIFIED; /* Set file change flag */ >> + >> + LEAVE_FF(fs, FR_OK); >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Synchronize the >> File */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_sync ( >> + FIL* fp /* Pointer to the file object */ >> +) >> +{ >> + FRESULT res; >> + FATFS *fs; >> + DWORD tm; >> + BYTE *dir; >> + >> + >> + res = validate(&fp->obj, &fs); /* Check validity of the file >> object */ >> + if (res == FR_OK) { >> + if (fp->flag & FA_MODIFIED) { /* Is there any change to >> the file? */ >> +#if !FF_FS_TINY >> + if (fp->flag & FA_DIRTY) { /* Write-back cached data >> if needed */ >> + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != >> RES_OK) LEAVE_FF(fs, FR_DISK_ERR); >> + fp->flag &= (BYTE)~FA_DIRTY; >> + } >> +#endif >> + /* Update the directory entry */ >> + tm = GET_FATTIME(); /* Modified time */ >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { >> + res = fill_first_frag(&fp->obj); /* Fill first >> fragment on the FAT if needed */ >> + if (res == FR_OK) { >> + res = fill_last_frag(&fp->obj, fp->clust, >> 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */ >> + } >> + if (res == FR_OK) { >> + DIR dj; >> + DEF_NAMBUF >> + >> + INIT_NAMBUF(fs); >> + res = load_obj_xdir(&dj, &fp->obj); /* Load >> directory entry block */ >> + if (res == FR_OK) { >> + fs->dirbuf[XDIR_Attr] |= >> AM_ARC; /* Set archive attribute to indicate that the >> file has been changed */ >> + fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | >> 1; /* Update file allocation information */ >> + st_dword(fs->dirbuf + XDIR_FstClus, >> fp->obj.sclust); /* Update start cluster */ >> + st_qword(fs->dirbuf + XDIR_FileSize, >> fp->obj.objsize); /* Update file size */ >> + st_qword(fs->dirbuf + XDIR_ValidFileSize, >> fp->obj.objsize); /* (FatFs does not support Valid File Size >> feature) */ >> + st_dword(fs->dirbuf + XDIR_ModTime, >> tm); /* Update modified time */ >> + fs->dirbuf[XDIR_ModTime10] = 0; >> + st_dword(fs->dirbuf + XDIR_AccTime, 0); >> + res = store_xdir(&dj); /* Restore it to >> the directory */ >> + if (res == FR_OK) { >> + res = sync_fs(fs); >> + fp->flag &= (BYTE)~FA_MODIFIED; >> + } >> + } >> + FREE_NAMBUF(); >> + } >> + } else >> +#endif >> + { >> + res = move_window(fs, fp->dir_sect); >> + if (res == FR_OK) { >> + dir = fp->dir_ptr; >> + dir[DIR_Attr] |= AM_ARC; >> /* Set archive attribute to indicate that the file has been changed */ >> + st_clust(fp->obj.fs, dir, fp->obj.sclust); >> /* Update file allocation information */ >> + st_dword(dir + DIR_FileSize, >> (DWORD)fp->obj.objsize); /* Update file size */ >> + st_dword(dir + DIR_ModTime, tm); >> /* Update modified time */ >> + st_word(dir + DIR_LstAccDate, 0); >> + fs->wflag = 1; >> + res = sync_fs(fs); /* Restore >> it to the directory */ >> + fp->flag &= (BYTE)~FA_MODIFIED; >> + } >> + } >> + } >> + } >> + >> + LEAVE_FF(fs, res); >> +} >> + >> +#endif /* !FF_FS_READONLY */ >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Close >> File */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_close ( >> + FIL* fp /* Pointer to the file object to be closed */ >> +) >> +{ >> + FRESULT res; >> + FATFS *fs; >> + >> +#if !FF_FS_READONLY >> + res = f_sync(fp); /* Flush cached data */ >> + if (res == FR_OK) >> +#endif >> + { >> + res = validate(&fp->obj, &fs); /* Lock volume */ >> + if (res == FR_OK) { >> +#if FF_FS_LOCK != 0 >> + res = dec_lock(fp->obj.lockid); /* Decrement file >> open counter */ >> + if (res == FR_OK) fp->obj.fs = 0; /* Invalidate file >> object */ >> +#else >> + fp->obj.fs = 0; /* Invalidate file object */ >> +#endif >> +#if FF_FS_REENTRANT >> + unlock_fs(fs, FR_OK); /* Unlock volume */ >> +#endif >> + } >> + } >> + return res; >> +} >> + >> + >> + >> + >> +#if FF_FS_RPATH >= 1 >> +/*-----------------------------------------------------------------------*/ >> >> +/* Change Current Directory or Current Drive, Get Current >> Directory */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_chdrive ( >> + const TCHAR* path /* Drive number to set */ >> +) >> +{ >> + int vol; >> + >> + >> + /* Get logical drive number */ >> + vol = get_ldnumber(&path); >> + if (vol < 0) return FR_INVALID_DRIVE; >> + CurrVol = (BYTE)vol; /* Set it as current volume */ >> + >> + return FR_OK; >> +} >> + >> + >> + >> +FRESULT f_chdir ( >> + const TCHAR* path /* Pointer to the directory path */ >> +) >> +{ >> +#if FF_STR_VOLUME_ID == 2 >> + UINT i; >> +#endif >> + FRESULT res; >> + DIR dj; >> + FATFS *fs; >> + DEF_NAMBUF >> + >> + >> + /* Get logical drive */ >> + res = mount_volume(&path, &fs, 0); >> + if (res == FR_OK) { >> + dj.obj.fs = fs; >> + INIT_NAMBUF(fs); >> + res = follow_path(&dj, path); /* Follow the path */ >> + if (res == FR_OK) { /* Follow completed */ >> + if (dj.fn[NSFLAG] & NS_NONAME) { /* Is it the start >> directory itself? */ >> + fs->cdir = dj.obj.sclust; >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { >> + fs->cdc_scl = dj.obj.c_scl; >> + fs->cdc_size = dj.obj.c_size; >> + fs->cdc_ofs = dj.obj.c_ofs; >> + } >> +#endif >> + } else { >> + if (dj.obj.attr & AM_DIR) { /* It is a >> sub-directory */ >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { >> + fs->cdir = ld_dword(fs->dirbuf + >> XDIR_FstClus); /* Sub-directory cluster */ >> + fs->cdc_scl = >> dj.obj.sclust; /* Save containing directory >> information */ >> + fs->cdc_size = ((DWORD)dj.obj.objsize & >> 0xFFFFFF00) | dj.obj.stat; >> + fs->cdc_ofs = dj.blk_ofs; >> + } else >> +#endif >> + { >> + fs->cdir = ld_clust(fs, >> dj.dir); /* Sub-directory cluster */ >> + } >> + } else { >> + res = FR_NO_PATH; /* Reached but a file */ >> + } >> + } >> + } >> + FREE_NAMBUF(); >> + if (res == FR_NO_FILE) res = FR_NO_PATH; >> +#if FF_STR_VOLUME_ID == 2 /* Also current drive is changed at Unix >> style volume ID */ >> + if (res == FR_OK) { >> + for (i = FF_VOLUMES - 1; i && fs != FatFs[i]; i--) ; >> /* Set current drive */ >> + CurrVol = (BYTE)i; >> + } >> +#endif >> + } >> + >> + LEAVE_FF(fs, res); >> +} >> + >> + >> +#if FF_FS_RPATH >= 2 >> +FRESULT f_getcwd ( >> + TCHAR* buff, /* Pointer to the directory path */ >> + UINT len /* Size of buff in unit of TCHAR */ >> +) >> +{ >> + FRESULT res; >> + DIR dj; >> + FATFS *fs; >> + UINT i, n; >> + DWORD ccl; >> + TCHAR *tp = buff; >> +#if FF_VOLUMES >= 2 >> + UINT vl; >> +#if FF_STR_VOLUME_ID >> + const char *vp; >> +#endif >> +#endif >> + FILINFO fno; >> + DEF_NAMBUF >> + >> + >> + /* Get logical drive */ >> + buff[0] = 0; /* Set null string to get current volume */ >> + res = mount_volume((const TCHAR**)&buff, &fs, 0); /* Get >> current volume */ >> + if (res == FR_OK) { >> + dj.obj.fs = fs; >> + INIT_NAMBUF(fs); >> + >> + /* Follow parent directories and create the path */ >> + i = len; /* Bottom of buffer (directory stack >> base) */ >> + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* (Cannot >> do getcwd on exFAT and returns root path) */ >> + dj.obj.sclust = fs->cdir; /* Start to >> follow upper directory from current directory */ >> + while ((ccl = dj.obj.sclust) != 0) { /* Repeat while >> current directory is a sub-directory */ >> + res = dir_sdi(&dj, 1 * SZDIRE); /* Get parent >> directory */ >> + if (res != FR_OK) break; >> + res = move_window(fs, dj.sect); >> + if (res != FR_OK) break; >> + dj.obj.sclust = ld_clust(fs, dj.dir); /* Goto >> parent directory */ >> + res = dir_sdi(&dj, 0); >> + if (res != FR_OK) break; >> + do { /* Find the entry >> links to the child directory */ >> + res = DIR_READ_FILE(&dj); >> + if (res != FR_OK) break; >> + if (ccl == ld_clust(fs, dj.dir)) break; /* >> Found the entry */ >> + res = dir_next(&dj, 0); >> + } while (res == FR_OK); >> + if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot >> be 'not found'. */ >> + if (res != FR_OK) break; >> + get_fileinfo(&dj, &fno); /* Get the directory >> name and push it to the buffer */ >> + for (n = 0; fno.fname[n]; n++) ; /* Name length */ >> + if (i < n + 1) { /* Insufficient space to store >> the path name? */ >> + res = FR_NOT_ENOUGH_CORE; break; >> + } >> + while (n) buff[--i] = fno.fname[--n]; /* Stack the >> name */ >> + buff[--i] = '/'; >> + } >> + } >> + if (res == FR_OK) { >> + if (i == len) buff[--i] = '/'; /* Is it the >> root-directory? */ >> +#if FF_VOLUMES >= 2 /* Put drive prefix */ >> + vl = 0; >> +#if FF_STR_VOLUME_ID >= 1 /* String volume ID */ >> + for (n = 0, vp = (const char*)VolumeStr[CurrVol]; vp[n]; >> n++) ; >> + if (i >= n + 2) { >> + if (FF_STR_VOLUME_ID == 2) *tp++ = (TCHAR)'/'; >> + for (vl = 0; vl < n; *tp++ = (TCHAR)vp[vl], vl++) ; >> + if (FF_STR_VOLUME_ID == 1) *tp++ = (TCHAR)':'; >> + vl++; >> + } >> +#else /* Numeric volume ID */ >> + if (i >= 3) { >> + *tp++ = (TCHAR)'0' + CurrVol; >> + *tp++ = (TCHAR)':'; >> + vl = 2; >> + } >> +#endif >> + if (vl == 0) res = FR_NOT_ENOUGH_CORE; >> +#endif >> + /* Add current directory path */ >> + if (res == FR_OK) { >> + do *tp++ = buff[i++]; while (i < len); /* Copy >> stacked path string */ >> + } >> + } >> + FREE_NAMBUF(); >> + } >> + >> + *tp = 0; >> + LEAVE_FF(fs, res); >> +} >> + >> +#endif /* FF_FS_RPATH >= 2 */ >> +#endif /* FF_FS_RPATH >= 1 */ >> + >> + >> + >> +#if FF_FS_MINIMIZE <= 2 >> +/*-----------------------------------------------------------------------*/ >> >> +/* Seek File Read/Write >> Pointer */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_lseek ( >> + FIL* fp, /* Pointer to the file object */ >> + FSIZE_t ofs /* File pointer from top of file */ >> +) >> +{ >> + FRESULT res; >> + FATFS *fs; >> + DWORD clst, bcs; >> + LBA_t nsect; >> + FSIZE_t ifptr; >> +#if FF_USE_FASTSEEK >> + DWORD cl, pcl, ncl, tcl, tlen, ulen, *tbl; >> + LBA_t dsc; >> +#endif >> + >> + res = validate(&fp->obj, &fs); /* Check validity of the >> file object */ >> + if (res == FR_OK) res = (FRESULT)fp->err; >> +#if FF_FS_EXFAT && !FF_FS_READONLY >> + if (res == FR_OK && fs->fs_type == FS_EXFAT) { >> + res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* >> Fill last fragment on the FAT if needed */ >> + } >> +#endif >> + if (res != FR_OK) LEAVE_FF(fs, res); >> + >> +#if FF_USE_FASTSEEK >> + if (fp->cltbl) { /* Fast seek */ >> + if (ofs == CREATE_LINKMAP) { /* Create CLMT */ >> + tbl = fp->cltbl; >> + tlen = *tbl++; ulen = 2; /* Given table size and >> required table size */ >> + cl = fp->obj.sclust; /* Origin of the chain */ >> + if (cl != 0) { >> + do { >> + /* Get a fragment */ >> + tcl = cl; ncl = 0; ulen += 2; /* Top, length >> and used items */ >> + do { >> + pcl = cl; ncl++; >> + cl = get_fat(&fp->obj, cl); >> + if (cl <= 1) ABORT(fs, FR_INT_ERR); >> + if (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); >> + } while (cl == pcl + 1); >> + if (ulen <= tlen) { /* Store the length >> and top of the fragment */ >> + *tbl++ = ncl; *tbl++ = tcl; >> + } >> + } while (cl < fs->n_fatent); /* Repeat until end >> of chain */ >> + } >> + *fp->cltbl = ulen; /* Number of items used */ >> + if (ulen <= tlen) { >> + *tbl = 0; /* Terminate table */ >> + } else { >> + res = FR_NOT_ENOUGH_CORE; /* Given table size is >> smaller than required */ >> + } >> + } else { /* Fast seek */ >> + if (ofs > fp->obj.objsize) ofs = fp->obj.objsize; /* >> Clip offset at the file size */ >> + fp->fptr = ofs; /* Set file pointer */ >> + if (ofs > 0) { >> + fp->clust = clmt_clust(fp, ofs - 1); >> + dsc = clst2sect(fs, fp->clust); >> + if (dsc == 0) ABORT(fs, FR_INT_ERR); >> + dsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1); >> + if (fp->fptr % SS(fs) && dsc != fp->sect) { /* >> Refill sector cache if needed */ >> +#if !FF_FS_TINY >> +#if !FF_FS_READONLY >> + if (fp->flag & FA_DIRTY) { /* Write-back >> dirty sector cache */ >> + if (disk_write(fs->pdrv, fp->buf, fp->sect, >> 1) != RES_OK) ABORT(fs, FR_DISK_ERR); >> + fp->flag &= (BYTE)~FA_DIRTY; >> + } >> +#endif >> + if (disk_read(fs->pdrv, fp->buf, dsc, 1) != >> RES_OK) ABORT(fs, FR_DISK_ERR); /* Load current sector */ >> +#endif >> + fp->sect = dsc; >> + } >> + } >> + } >> + } else >> +#endif >> + >> + /* Normal Seek */ >> + { >> +#if FF_FS_EXFAT >> + if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = >> 0xFFFFFFFF; /* Clip at 4 GiB - 1 if at FATxx */ >> +#endif >> + if (ofs > fp->obj.objsize && (FF_FS_READONLY || !(fp->flag & >> FA_WRITE))) { /* In read-only mode, clip offset with the file size */ >> + ofs = fp->obj.objsize; >> + } >> + ifptr = fp->fptr; >> + fp->fptr = nsect = 0; >> + if (ofs > 0) { >> + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size >> (byte) */ >> + if (ifptr > 0 && >> + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When >> seek to same or following cluster, */ >> + fp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1); /* >> start from the current cluster */ >> + ofs -= fp->fptr; >> + clst = fp->clust; >> + } else { /* When seek >> to back cluster, */ >> + clst = fp->obj.sclust; /* start >> from the first cluster */ >> +#if !FF_FS_READONLY >> + if (clst == 0) { /* If no >> cluster chain, create a new chain */ >> + clst = create_chain(&fp->obj, 0); >> + if (clst == 1) ABORT(fs, FR_INT_ERR); >> + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); >> + fp->obj.sclust = clst; >> + } >> +#endif >> + fp->clust = clst; >> + } >> + if (clst != 0) { >> + while (ofs > bcs) { /* Cluster >> following loop */ >> + ofs -= bcs; fp->fptr += bcs; >> +#if !FF_FS_READONLY >> + if (fp->flag & FA_WRITE) { /* Check if >> in write mode or not */ >> + if (FF_FS_EXFAT && fp->fptr > >> fp->obj.objsize) { /* No FAT chain object needs correct objsize to >> generate FAT value */ >> + fp->obj.objsize = fp->fptr; >> + fp->flag |= FA_MODIFIED; >> + } >> + clst = create_chain(&fp->obj, clst); /* >> Follow chain with forceed stretch */ >> + if (clst == 0) { /* Clip file >> size in case of disk full */ >> + ofs = 0; break; >> + } >> + } else >> +#endif >> + { >> + clst = get_fat(&fp->obj, clst); /* Follow >> cluster chain if not in write mode */ >> + } >> + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); >> + if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, >> FR_INT_ERR); >> + fp->clust = clst; >> + } >> + fp->fptr += ofs; >> + if (ofs % SS(fs)) { >> + nsect = clst2sect(fs, clst); /* Current sector */ >> + if (nsect == 0) ABORT(fs, FR_INT_ERR); >> + nsect += (DWORD)(ofs / SS(fs)); >> + } >> + } >> + } >> + if (!FF_FS_READONLY && fp->fptr > fp->obj.objsize) { /* >> Set file change flag if the file size is extended */ >> + fp->obj.objsize = fp->fptr; >> + fp->flag |= FA_MODIFIED; >> + } >> + if (fp->fptr % SS(fs) && nsect != fp->sect) { /* Fill >> sector cache if needed */ >> +#if !FF_FS_TINY >> +#if !FF_FS_READONLY >> + if (fp->flag & FA_DIRTY) { /* Write-back dirty >> sector cache */ >> + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != >> RES_OK) ABORT(fs, FR_DISK_ERR); >> + fp->flag &= (BYTE)~FA_DIRTY; >> + } >> +#endif >> + if (disk_read(fs->pdrv, fp->buf, nsect, 1) != RES_OK) >> ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ >> +#endif >> + fp->sect = nsect; >> + } >> + } >> + >> + LEAVE_FF(fs, res); >> +} >> + >> + >> + >> +#if FF_FS_MINIMIZE <= 1 >> +/*-----------------------------------------------------------------------*/ >> >> +/* Create a Directory >> Object */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_opendir ( >> + DIR* dp, /* Pointer to directory object to create */ >> + const TCHAR* path /* Pointer to the directory path */ >> +) >> +{ >> + FRESULT res; >> + FATFS *fs; >> + DEF_NAMBUF >> + >> + >> + if (!dp) return FR_INVALID_OBJECT; >> + >> + /* Get logical drive */ >> + res = mount_volume(&path, &fs, 0); >> + if (res == FR_OK) { >> + dp->obj.fs = fs; >> + INIT_NAMBUF(fs); >> + res = follow_path(dp, path); /* Follow the path to >> the directory */ >> + if (res == FR_OK) { /* Follow >> completed */ >> + if (!(dp->fn[NSFLAG] & NS_NONAME)) { /* It is not the >> origin directory itself */ >> + if (dp->obj.attr & AM_DIR) { /* This object is >> a sub-directory */ >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { >> + dp->obj.c_scl = >> dp->obj.sclust; /* Get containing directory >> inforamation */ >> + dp->obj.c_size = ((DWORD)dp->obj.objsize & >> 0xFFFFFF00) | dp->obj.stat; >> + dp->obj.c_ofs = dp->blk_ofs; >> + init_alloc_info(fs, &dp->obj); /* Get >> object allocation info */ >> + } else >> +#endif >> + { >> + dp->obj.sclust = ld_clust(fs, dp->dir); /* >> Get object allocation info */ >> + } >> + } else { /* This object is a >> file */ >> + res = FR_NO_PATH; >> + } >> + } >> + if (res == FR_OK) { >> + dp->obj.id = fs->id; >> + res = dir_sdi(dp, 0); /* Rewind directory */ >> +#if FF_FS_LOCK != 0 >> + if (res == FR_OK) { >> + if (dp->obj.sclust != 0) { >> + dp->obj.lockid = inc_lock(dp, 0); /* Lock >> the sub directory */ >> + if (!dp->obj.lockid) res = >> FR_TOO_MANY_OPEN_FILES; >> + } else { >> + dp->obj.lockid = 0; /* Root directory need >> not to be locked */ >> + } >> + } >> +#endif >> + } >> + } >> + FREE_NAMBUF(); >> + if (res == FR_NO_FILE) res = FR_NO_PATH; >> + } >> + if (res != FR_OK) dp->obj.fs = 0; /* Invalidate the >> directory object if function faild */ >> + >> + LEAVE_FF(fs, res); >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Close >> Directory */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_closedir ( >> + DIR *dp /* Pointer to the directory object to be closed */ >> +) >> +{ >> + FRESULT res; >> + FATFS *fs; >> + >> + >> + res = validate(&dp->obj, &fs); /* Check validity of the file >> object */ >> + if (res == FR_OK) { >> +#if FF_FS_LOCK != 0 >> + if (dp->obj.lockid) res = dec_lock(dp->obj.lockid); /* >> Decrement sub-directory open counter */ >> + if (res == FR_OK) dp->obj.fs = 0; /* Invalidate directory >> object */ >> +#else >> + dp->obj.fs = 0; /* Invalidate directory object */ >> +#endif >> +#if FF_FS_REENTRANT >> + unlock_fs(fs, FR_OK); /* Unlock volume */ >> +#endif >> + } >> + return res; >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Read Directory Entries in >> Sequence */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_readdir ( >> + DIR* dp, /* Pointer to the open directory object */ >> + FILINFO* fno /* Pointer to file information to return */ >> +) >> +{ >> + FRESULT res; >> + FATFS *fs; >> + DEF_NAMBUF >> + >> + >> + res = validate(&dp->obj, &fs); /* Check validity of the >> directory object */ >> + if (res == FR_OK) { >> + if (!fno) { >> + res = dir_sdi(dp, 0); /* Rewind the directory >> object */ >> + } else { >> + INIT_NAMBUF(fs); >> + res = DIR_READ_FILE(dp); /* Read an item */ >> + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of >> directory */ >> + if (res == FR_OK) { /* A valid entry is >> found */ >> + get_fileinfo(dp, fno); /* Get the object >> information */ >> + res = dir_next(dp, 0); /* Increment index for >> next */ >> + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end >> of directory now */ >> + } >> + FREE_NAMBUF(); >> + } >> + } >> + LEAVE_FF(fs, res); >> +} >> + >> + >> + >> +#if FF_USE_FIND >> +/*-----------------------------------------------------------------------*/ >> >> +/* Find Next >> File */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_findnext ( >> + DIR* dp, /* Pointer to the open directory object */ >> + FILINFO* fno /* Pointer to the file information structure */ >> +) >> +{ >> + FRESULT res; >> + >> + >> + for (;;) { >> + res = f_readdir(dp, fno); /* Get a directory item */ >> + if (res != FR_OK || !fno || !fno->fname[0]) break; /* >> Terminate if any error or end of directory */ >> + if (pattern_match(dp->pat, fno->fname, 0, FIND_RECURS)) >> break; /* Test for the file name */ >> +#if FF_USE_LFN && FF_USE_FIND == 2 >> + if (pattern_match(dp->pat, fno->altname, 0, FIND_RECURS)) >> break; /* Test for alternative name if exist */ >> +#endif >> + } >> + return res; >> +} >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Find First >> File */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_findfirst ( >> + DIR* dp, /* Pointer to the blank directory object */ >> + FILINFO* fno, /* Pointer to the file information >> structure */ >> + const TCHAR* path, /* Pointer to the directory to open */ >> + const TCHAR* pattern /* Pointer to the matching pattern */ >> +) >> +{ >> + FRESULT res; >> + >> + >> + dp->pat = pattern; /* Save pointer to pattern string */ >> + res = f_opendir(dp, path); /* Open the target directory */ >> + if (res == FR_OK) { >> + res = f_findnext(dp, fno); /* Find the first item */ >> + } >> + return res; >> +} >> + >> +#endif /* FF_USE_FIND */ >> + >> + >> + >> +#if FF_FS_MINIMIZE == 0 >> +/*-----------------------------------------------------------------------*/ >> >> +/* Get File >> Status */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_stat ( >> + const TCHAR* path, /* Pointer to the file path */ >> + FILINFO* fno /* Pointer to file information to return */ >> +) >> +{ >> + FRESULT res; >> + DIR dj; >> + DEF_NAMBUF >> + >> + >> + /* Get logical drive */ >> + res = mount_volume(&path, &dj.obj.fs, 0); >> + if (res == FR_OK) { >> + INIT_NAMBUF(dj.obj.fs); >> + res = follow_path(&dj, path); /* Follow the file path */ >> + if (res == FR_OK) { /* Follow completed */ >> + if (dj.fn[NSFLAG] & NS_NONAME) { /* It is origin >> directory */ >> + res = FR_INVALID_NAME; >> + } else { /* Found an object */ >> + if (fno) get_fileinfo(&dj, fno); >> + } >> + } >> + FREE_NAMBUF(); >> + } >> + >> + LEAVE_FF(dj.obj.fs, res); >> +} >> + >> + >> + >> +#if !FF_FS_READONLY >> +/*-----------------------------------------------------------------------*/ >> >> +/* Get Number of Free >> Clusters */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_getfree ( >> + const TCHAR* path, /* Logical drive number */ >> + DWORD* nclst, /* Pointer to a variable to return number of >> free clusters */ >> + FATFS** fatfs /* Pointer to return pointer to >> corresponding filesystem object */ >> +) >> +{ >> + FRESULT res; >> + FATFS *fs; >> + DWORD nfree, clst, stat; >> + LBA_t sect; >> + UINT i; >> + FFOBJID obj; >> + >> + >> + /* Get logical drive */ >> + res = mount_volume(&path, &fs, 0); >> + if (res == FR_OK) { >> + *fatfs = fs; /* Return ptr to the fs object */ >> + /* If free_clst is valid, return it without full FAT scan */ >> + if (fs->free_clst <= fs->n_fatent - 2) { >> + *nclst = fs->free_clst; >> + } else { >> + /* Scan FAT to obtain number of free clusters */ >> + nfree = 0; >> + if (fs->fs_type == FS_FAT12) { /* FAT12: Scan bit >> field FAT entries */ >> + clst = 2; obj.fs = fs; >> + do { >> + stat = get_fat(&obj, clst); >> + if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; >> break; } >> + if (stat == 1) { res = FR_INT_ERR; break; } >> + if (stat == 0) nfree++; >> + } while (++clst < fs->n_fatent); >> + } else { >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { /* exFAT: Scan >> allocation bitmap */ >> + BYTE bm; >> + UINT b; >> + >> + clst = fs->n_fatent - 2; /* Number of clusters */ >> + sect = fs->bitbase; /* Bitmap sector */ >> + i = 0; /* Offset in the >> sector */ >> + do { /* Counts numbuer of bits with zero in >> the bitmap */ >> + if (i == 0) { >> + res = move_window(fs, sect++); >> + if (res != FR_OK) break; >> + } >> + for (b = 8, bm = fs->win[i]; b && clst; b--, >> clst--) { >> + if (!(bm & 1)) nfree++; >> + bm >>= 1; >> + } >> + i = (i + 1) % SS(fs); >> + } while (clst); >> + } else >> +#endif >> + { /* FAT16/32: Scan WORD/DWORD FAT entries */ >> + clst = fs->n_fatent; /* Number of entries */ >> + sect = fs->fatbase; /* Top of the FAT */ >> + i = 0; /* Offset in the sector */ >> + do { /* Counts numbuer of entries with zero in >> the FAT */ >> + if (i == 0) { >> + res = move_window(fs, sect++); >> + if (res != FR_OK) break; >> + } >> + if (fs->fs_type == FS_FAT16) { >> + if (ld_word(fs->win + i) == 0) nfree++; >> + i += 2; >> + } else { >> + if ((ld_dword(fs->win + i) & 0x0FFFFFFF) >> == 0) nfree++; >> + i += 4; >> + } >> + i %= SS(fs); >> + } while (--clst); >> + } >> + } >> + *nclst = nfree; /* Return the free clusters */ >> + fs->free_clst = nfree; /* Now free_clst is valid */ >> + fs->fsi_flag |= 1; /* FAT32: FSInfo is to be >> updated */ >> + } >> + } >> + >> + LEAVE_FF(fs, res); >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Truncate >> File */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_truncate ( >> + FIL* fp /* Pointer to the file object */ >> +) >> +{ >> + FRESULT res; >> + FATFS *fs; >> + DWORD ncl; >> + >> + >> + res = validate(&fp->obj, &fs); /* Check validity of the file >> object */ >> + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) >> LEAVE_FF(fs, res); >> + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check >> access mode */ >> + >> + if (fp->fptr < fp->obj.objsize) { /* Process when fptr is not >> on the eof */ >> + if (fp->fptr == 0) { /* When set file size to zero, remove >> entire cluster chain */ >> + res = remove_chain(&fp->obj, fp->obj.sclust, 0); >> + fp->obj.sclust = 0; >> + } else { /* When truncate a part of the file, >> remove remaining clusters */ >> + ncl = get_fat(&fp->obj, fp->clust); >> + res = FR_OK; >> + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; >> + if (ncl == 1) res = FR_INT_ERR; >> + if (res == FR_OK && ncl < fs->n_fatent) { >> + res = remove_chain(&fp->obj, ncl, fp->clust); >> + } >> + } >> + fp->obj.objsize = fp->fptr; /* Set file size to current >> read/write point */ >> + fp->flag |= FA_MODIFIED; >> +#if !FF_FS_TINY >> + if (res == FR_OK && (fp->flag & FA_DIRTY)) { >> + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) { >> + res = FR_DISK_ERR; >> + } else { >> + fp->flag &= (BYTE)~FA_DIRTY; >> + } >> + } >> +#endif >> + if (res != FR_OK) ABORT(fs, res); >> + } >> + >> + LEAVE_FF(fs, res); >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Delete a >> File/Directory */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_unlink ( >> + const TCHAR* path /* Pointer to the file or directory path */ >> +) >> +{ >> + FRESULT res; >> + DIR dj, sdj; >> + DWORD dclst = 0; >> + FATFS *fs; >> +#if FF_FS_EXFAT >> + FFOBJID obj; >> +#endif >> + DEF_NAMBUF >> + >> + >> + /* Get logical drive */ >> + res = mount_volume(&path, &fs, FA_WRITE); >> + if (res == FR_OK) { >> + dj.obj.fs = fs; >> + INIT_NAMBUF(fs); >> + res = follow_path(&dj, path); /* Follow the file path */ >> + if (FF_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) { >> + res = FR_INVALID_NAME; /* Cannot remove dot >> entry */ >> + } >> +#if FF_FS_LOCK != 0 >> + if (res == FR_OK) res = chk_lock(&dj, 2); /* Check if it >> is an open object */ >> +#endif >> + if (res == FR_OK) { /* The object is >> accessible */ >> + if (dj.fn[NSFLAG] & NS_NONAME) { >> + res = FR_INVALID_NAME; /* Cannot remove the >> origin directory */ >> + } else { >> + if (dj.obj.attr & AM_RDO) { >> + res = FR_DENIED; /* Cannot remove R/O >> object */ >> + } >> + } >> + if (res == FR_OK) { >> +#if FF_FS_EXFAT >> + obj.fs = fs; >> + if (fs->fs_type == FS_EXFAT) { >> + init_alloc_info(fs, &obj); >> + dclst = obj.sclust; >> + } else >> +#endif >> + { >> + dclst = ld_clust(fs, dj.dir); >> + } >> + if (dj.obj.attr & AM_DIR) { /* Is it a >> sub-directory? */ >> +#if FF_FS_RPATH != 0 >> + if (dclst == fs->cdir) { /* Is it the >> current directory? */ >> + res = FR_DENIED; >> + } else >> +#endif >> + { >> + sdj.obj.fs = fs; /* Open the >> sub-directory */ >> + sdj.obj.sclust = dclst; >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { >> + sdj.obj.objsize = obj.objsize; >> + sdj.obj.stat = obj.stat; >> + } >> +#endif >> + res = dir_sdi(&sdj, 0); >> + if (res == FR_OK) { >> + res = DIR_READ_FILE(&sdj); /* >> Test if the directory is empty */ >> + if (res == FR_OK) res = FR_DENIED; /* >> Not empty? */ >> + if (res == FR_NO_FILE) res = FR_OK; /* >> Empty? */ >> + } >> + } >> + } >> + } >> + if (res == FR_OK) { >> + res = dir_remove(&dj); /* Remove the >> directory entry */ >> + if (res == FR_OK && dclst != 0) { /* Remove the >> cluster chain if exist */ >> +#if FF_FS_EXFAT >> + res = remove_chain(&obj, dclst, 0); >> +#else >> + res = remove_chain(&dj.obj, dclst, 0); >> +#endif >> + } >> + if (res == FR_OK) res = sync_fs(fs); >> + } >> + } >> + FREE_NAMBUF(); >> + } >> + >> + LEAVE_FF(fs, res); >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Create a >> Directory */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_mkdir ( >> + const TCHAR* path /* Pointer to the directory path */ >> +) >> +{ >> + FRESULT res; >> + DIR dj; >> + FFOBJID sobj; >> + FATFS *fs; >> + DWORD dcl, pcl, tm; >> + DEF_NAMBUF >> + >> + >> + res = mount_volume(&path, &fs, FA_WRITE); /* Get logical drive */ >> + if (res == FR_OK) { >> + dj.obj.fs = fs; >> + INIT_NAMBUF(fs); >> + res = follow_path(&dj, path); /* Follow the file >> path */ >> + if (res == FR_OK) res = FR_EXIST; /* Name collision? */ >> + if (FF_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & >> NS_DOT)) { /* Invalid name? */ >> + res = FR_INVALID_NAME; >> + } >> + if (res == FR_NO_FILE) { /* It is clear to >> create a new directory */ >> + sobj.fs = fs; /* New object id to >> create a new chain */ >> + dcl = create_chain(&sobj, 0); /* Allocate a >> cluster for the new directory */ >> + res = FR_OK; >> + if (dcl == 0) res = FR_DENIED; /* No space to >> allocate a new cluster? */ >> + if (dcl == 1) res = FR_INT_ERR; /* Any insanity? */ >> + if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; /* Disk >> error? */ >> + tm = GET_FATTIME(); >> + if (res == FR_OK) { >> + res = dir_clear(fs, dcl); /* Clean up the new >> table */ >> + if (res == FR_OK) { >> + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { >> /* Create dot entries (FAT only) */ >> + mem_set(fs->win + DIR_Name, ' ', 11); /* >> Create "." entry */ >> + fs->win[DIR_Name] = '.'; >> + fs->win[DIR_Attr] = AM_DIR; >> + st_dword(fs->win + DIR_ModTime, tm); >> + st_clust(fs, fs->win, dcl); >> + mem_cpy(fs->win + SZDIRE, fs->win, SZDIRE); >> /* Create ".." entry */ >> + fs->win[SZDIRE + 1] = '.'; pcl = dj.obj.sclust; >> + st_clust(fs, fs->win + SZDIRE, pcl); >> + fs->wflag = 1; >> + } >> + res = dir_register(&dj); /* Register the >> object to the parent directoy */ >> + } >> + } >> + if (res == FR_OK) { >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { /* Initialize >> directory entry block */ >> + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* >> Created time */ >> + st_dword(fs->dirbuf + XDIR_FstClus, dcl); /* >> Table start cluster */ >> + st_dword(fs->dirbuf + XDIR_FileSize, >> (DWORD)fs->csize * SS(fs)); /* Directory size needs to be valid */ >> + st_dword(fs->dirbuf + XDIR_ValidFileSize, >> (DWORD)fs->csize * SS(fs)); >> + fs->dirbuf[XDIR_GenFlags] = 3; /* >> Initialize the object flag */ >> + fs->dirbuf[XDIR_Attr] = AM_DIR; /* >> Attribute */ >> + res = store_xdir(&dj); >> + } else >> +#endif >> + { >> + st_dword(dj.dir + DIR_ModTime, tm); /* Created >> time */ >> + st_clust(fs, dj.dir, dcl); /* Table >> start cluster */ >> + dj.dir[DIR_Attr] = AM_DIR; /* >> Attribute */ >> + fs->wflag = 1; >> + } >> + if (res == FR_OK) { >> + res = sync_fs(fs); >> + } >> + } else { >> + remove_chain(&sobj, dcl, 0); /* Could not >> register, remove the allocated cluster */ >> + } >> + } >> + FREE_NAMBUF(); >> + } >> + >> + LEAVE_FF(fs, res); >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Rename a >> File/Directory */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_rename ( >> + const TCHAR* path_old, /* Pointer to the object name to be >> renamed */ >> + const TCHAR* path_new /* Pointer to the new name */ >> +) >> +{ >> + FRESULT res; >> + DIR djo, djn; >> + FATFS *fs; >> + BYTE buf[FF_FS_EXFAT ? SZDIRE * 2 : SZDIRE], *dir; >> + LBA_t sect; >> + DEF_NAMBUF >> + >> + >> + get_ldnumber(&path_new); /* Snip the drive >> number of new name off */ >> + res = mount_volume(&path_old, &fs, FA_WRITE); /* Get logical >> drive of the old object */ >> + if (res == FR_OK) { >> + djo.obj.fs = fs; >> + INIT_NAMBUF(fs); >> + res = follow_path(&djo, path_old); /* Check old object */ >> + if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) >> res = FR_INVALID_NAME; /* Check validity of name */ >> +#if FF_FS_LOCK != 0 >> + if (res == FR_OK) { >> + res = chk_lock(&djo, 2); >> + } >> +#endif >> + if (res == FR_OK) { /* Object to be >> renamed is found */ >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { /* At exFAT volume */ >> + BYTE nf, nn; >> + WORD nh; >> + >> + mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save >> 85+C0 entry of old object */ >> + mem_cpy(&djn, &djo, sizeof djo); >> + res = follow_path(&djn, path_new); /* Make >> sure if new object name is not in use */ >> + if (res == FR_OK) { /* Is new >> name already in use by any other object? */ >> + res = (djn.obj.sclust == djo.obj.sclust && >> djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; >> + } >> + if (res == FR_NO_FILE) { /* It is a >> valid path and no name collision */ >> + res = dir_register(&djn); /* Register >> the new entry */ >> + if (res == FR_OK) { >> + nf = fs->dirbuf[XDIR_NumSec]; nn = >> fs->dirbuf[XDIR_NumName]; >> + nh = ld_word(fs->dirbuf + XDIR_NameHash); >> + mem_cpy(fs->dirbuf, buf, SZDIRE * 2); /* >> Restore 85+C0 entry */ >> + fs->dirbuf[XDIR_NumSec] = nf; >> fs->dirbuf[XDIR_NumName] = nn; >> + st_word(fs->dirbuf + XDIR_NameHash, nh); >> + if (!(fs->dirbuf[XDIR_Attr] & AM_DIR)) >> fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute if it is >> a file */ >> +/* Start of critical section where an interruption can cause a >> cross-link */ >> + res = store_xdir(&djn); >> + } >> + } >> + } else >> +#endif >> + { /* At FAT/FAT32 volume */ >> + mem_cpy(buf, djo.dir, SZDIRE); /* Save >> directory entry of the object */ >> + mem_cpy(&djn, &djo, sizeof (DIR)); /* >> Duplicate the directory object */ >> + res = follow_path(&djn, path_new); /* Make >> sure if new object name is not in use */ >> + if (res == FR_OK) { /* Is new >> name already in use by any other object? */ >> + res = (djn.obj.sclust == djo.obj.sclust && >> djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; >> + } >> + if (res == FR_NO_FILE) { /* It is a >> valid path and no name collision */ >> + res = dir_register(&djn); /* Register >> the new entry */ >> + if (res == FR_OK) { >> + dir = djn.dir; /* Copy >> directory entry of the object except name */ >> + mem_cpy(dir + 13, buf + 13, SZDIRE - 13); >> + dir[DIR_Attr] = buf[DIR_Attr]; >> + if (!(dir[DIR_Attr] & AM_DIR)) dir[DIR_Attr] >> |= AM_ARC; /* Set archive attribute if it is a file */ >> + fs->wflag = 1; >> + if ((dir[DIR_Attr] & AM_DIR) && >> djo.obj.sclust != djn.obj.sclust) { /* Update .. entry in the >> sub-directory if needed */ >> + sect = clst2sect(fs, ld_clust(fs, dir)); >> + if (sect == 0) { >> + res = FR_INT_ERR; >> + } else { >> +/* Start of critical section where an interruption can cause a >> cross-link */ >> + res = move_window(fs, sect); >> + dir = fs->win + SZDIRE * 1; /* Ptr >> to .. entry */ >> + if (res == FR_OK && dir[1] == '.') { >> + st_clust(fs, dir, djn.obj.sclust); >> + fs->wflag = 1; >> + } >> + } >> + } >> + } >> + } >> + } >> + if (res == FR_OK) { >> + res = dir_remove(&djo); /* Remove old entry */ >> + if (res == FR_OK) { >> + res = sync_fs(fs); >> + } >> + } >> +/* End of the critical section */ >> + } >> + FREE_NAMBUF(); >> + } >> + >> + LEAVE_FF(fs, res); >> +} >> + >> +#endif /* !FF_FS_READONLY */ >> +#endif /* FF_FS_MINIMIZE == 0 */ >> +#endif /* FF_FS_MINIMIZE <= 1 */ >> +#endif /* FF_FS_MINIMIZE <= 2 */ >> + >> + >> + >> +#if FF_USE_CHMOD && !FF_FS_READONLY >> +/*-----------------------------------------------------------------------*/ >> >> +/* Change >> Attribute */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_chmod ( >> + const TCHAR* path, /* Pointer to the file path */ >> + BYTE attr, /* Attribute bits */ >> + BYTE mask /* Attribute mask to change */ >> +) >> +{ >> + FRESULT res; >> + DIR dj; >> + FATFS *fs; >> + DEF_NAMBUF >> + >> + >> + res = mount_volume(&path, &fs, FA_WRITE); /* Get logical drive */ >> + if (res == FR_OK) { >> + dj.obj.fs = fs; >> + INIT_NAMBUF(fs); >> + res = follow_path(&dj, path); /* Follow the file path */ >> + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) >> res = FR_INVALID_NAME; /* Check object validity */ >> + if (res == FR_OK) { >> + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid >> attribute mask */ >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { >> + fs->dirbuf[XDIR_Attr] = (attr & mask) | >> (fs->dirbuf[XDIR_Attr] & (BYTE)~mask); /* Apply attribute change */ >> + res = store_xdir(&dj); >> + } else >> +#endif >> + { >> + dj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] >> & (BYTE)~mask); /* Apply attribute change */ >> + fs->wflag = 1; >> + } >> + if (res == FR_OK) { >> + res = sync_fs(fs); >> + } >> + } >> + FREE_NAMBUF(); >> + } >> + >> + LEAVE_FF(fs, res); >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Change >> Timestamp */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_utime ( >> + const TCHAR* path, /* Pointer to the file/directory name */ >> + const FILINFO* fno /* Pointer to the timestamp to be set */ >> +) >> +{ >> + FRESULT res; >> + DIR dj; >> + FATFS *fs; >> + DEF_NAMBUF >> + >> + >> + res = mount_volume(&path, &fs, FA_WRITE); /* Get logical drive */ >> + if (res == FR_OK) { >> + dj.obj.fs = fs; >> + INIT_NAMBUF(fs); >> + res = follow_path(&dj, path); /* Follow the file path */ >> + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) >> res = FR_INVALID_NAME; /* Check object validity */ >> + if (res == FR_OK) { >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { >> + st_dword(fs->dirbuf + XDIR_ModTime, (DWORD)fno->fdate >> << 16 | fno->ftime); >> + res = store_xdir(&dj); >> + } else >> +#endif >> + { >> + st_dword(dj.dir + DIR_ModTime, (DWORD)fno->fdate << >> 16 | fno->ftime); >> + fs->wflag = 1; >> + } >> + if (res == FR_OK) { >> + res = sync_fs(fs); >> + } >> + } >> + FREE_NAMBUF(); >> + } >> + >> + LEAVE_FF(fs, res); >> +} >> + >> +#endif /* FF_USE_CHMOD && !FF_FS_READONLY */ >> + >> + >> + >> +#if FF_USE_LABEL >> +/*-----------------------------------------------------------------------*/ >> >> +/* Get Volume >> Label */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_getlabel ( >> + const TCHAR* path, /* Logical drive number */ >> + TCHAR* label, /* Buffer to store the volume label */ >> + DWORD* vsn /* Variable to store the volume serial >> number */ >> +) >> +{ >> + FRESULT res; >> + DIR dj; >> + FATFS *fs; >> + UINT si, di; >> + WCHAR wc; >> + >> + /* Get logical drive */ >> + res = mount_volume(&path, &fs, 0); >> + >> + /* Get volume label */ >> + if (res == FR_OK && label) { >> + dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */ >> + res = dir_sdi(&dj, 0); >> + if (res == FR_OK) { >> + res = DIR_READ_LABEL(&dj); /* Find a volume label >> entry */ >> + if (res == FR_OK) { >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { >> + WCHAR hs; >> + >> + for (si = di = hs = 0; si < >> dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 >> entry */ >> + wc = ld_word(dj.dir + XDIR_Label + si * 2); >> + if (hs == 0 && IsSurrogate(wc)) { /* Is >> the code a surrogate? */ >> + hs = wc; continue; >> + } >> + wc = put_utf((DWORD)hs << 16 | wc, >> &label[di], 4); >> + if (wc == 0) { di = 0; break; } >> + di += wc; >> + hs = 0; >> + } >> + if (hs != 0) di = 0; /* Broken surrogate pair? */ >> + label[di] = 0; >> + } else >> +#endif >> + { >> + si = di = 0; /* Extract volume label from >> AM_VOL entry */ >> + while (si < 11) { >> + wc = dj.dir[si++]; >> +#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode output */ >> + if (dbc_1st((BYTE)wc) && si < 11) wc = wc << >> 8 | dj.dir[si++]; /* Is it a DBC? */ >> + wc = ff_oem2uni(wc, >> CODEPAGE); /* Convert it into Unicode */ >> + if (wc != 0) wc = put_utf(wc, &label[di], >> 4); /* Put it in Unicode */ >> + if (wc == 0) { di = 0; break; } >> + di += wc; >> +#else /* ANSI/OEM output */ >> + label[di++] = (TCHAR)wc; >> +#endif >> + } >> + do { /* Truncate trailing spaces */ >> + label[di] = 0; >> + if (di == 0) break; >> + } while (label[--di] == ' '); >> + } >> + } >> + } >> + if (res == FR_NO_FILE) { /* No label entry and return nul >> string */ >> + label[0] = 0; >> + res = FR_OK; >> + } >> + } >> + >> + /* Get volume serial number */ >> + if (res == FR_OK && vsn) { >> + res = move_window(fs, fs->volbase); >> + if (res == FR_OK) { >> + switch (fs->fs_type) { >> + case FS_EXFAT: >> + di = BPB_VolIDEx; >> + break; >> + >> + case FS_FAT32: >> + di = BS_VolID32; >> + break; >> + >> + default: >> + di = BS_VolID; >> + } >> + *vsn = ld_dword(fs->win + di); >> + } >> + } >> + >> + LEAVE_FF(fs, res); >> +} >> + >> + >> + >> +#if !FF_FS_READONLY >> +/*-----------------------------------------------------------------------*/ >> >> +/* Set Volume >> Label */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_setlabel ( >> + const TCHAR* label /* Volume label to set with heading logical >> drive number */ >> +) >> +{ >> + FRESULT res; >> + DIR dj; >> + FATFS *fs; >> + BYTE dirvn[22]; >> + UINT di; >> + WCHAR wc; >> + static const char badchr[] = "+.,;=[]/\\\"*:<>\?|\x7F"; /* >> [0..] for FAT, [7..] for exFAT */ >> +#if FF_USE_LFN >> + DWORD dc; >> +#endif >> + >> + /* Get logical drive */ >> + res = mount_volume(&label, &fs, FA_WRITE); >> + if (res != FR_OK) LEAVE_FF(fs, res); >> + >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ >> + mem_set(dirvn, 0, 22); >> + di = 0; >> + while ((UINT)*label >= ' ') { /* Create volume label */ >> + dc = tchar2uni(&label); /* Get a Unicode character */ >> + if (dc >= 0x10000) { >> + if (dc == 0xFFFFFFFF || di >= 10) { /* Wrong >> surrogate or buffer overflow */ >> + dc = 0; >> + } else { >> + st_word(dirvn + di * 2, (WCHAR)(dc >> 16)); di++; >> + } >> + } >> + if (dc == 0 || chk_chr(badchr + 7, (int)dc) || di >= 11) >> { /* Check validity of the volume label */ >> + LEAVE_FF(fs, FR_INVALID_NAME); >> + } >> + st_word(dirvn + di * 2, (WCHAR)dc); di++; >> + } >> + } else >> +#endif >> + { /* On the FAT/FAT32 volume */ >> + mem_set(dirvn, ' ', 11); >> + di = 0; >> + while ((UINT)*label >= ' ') { /* Create volume label */ >> +#if FF_USE_LFN >> + dc = tchar2uni(&label); >> + wc = (dc < 0x10000) ? ff_uni2oem(ff_wtoupper(dc), >> CODEPAGE) : 0; >> +#else /* ANSI/OEM input */ >> + wc = (BYTE)*label++; >> + if (dbc_1st((BYTE)wc)) wc = dbc_2nd((BYTE)*label) ? wc << >> 8 | (BYTE)*label++ : 0; >> + if (IsLower(wc)) wc -= 0x20; /* To upper ASCII >> characters */ >> +#if FF_CODE_PAGE == 0 >> + if (ExCvt && wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To >> upper extended characters (SBCS cfg) */ >> +#elif FF_CODE_PAGE < 900 >> + if (wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper >> extended characters (SBCS cfg) */ >> +#endif >> +#endif >> + if (wc == 0 || chk_chr(badchr + 0, (int)wc) || di >= >> (UINT)((wc >= 0x100) ? 10 : 11)) { /* Reject invalid characters for >> volume label */ >> + LEAVE_FF(fs, FR_INVALID_NAME); >> + } >> + if (wc >= 0x100) dirvn[di++] = (BYTE)(wc >> 8); >> + dirvn[di++] = (BYTE)wc; >> + } >> + if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* >> Reject illegal name (heading DDEM) */ >> + while (di && dirvn[di - 1] == ' ') di--; /* >> Snip trailing spaces */ >> + } >> + >> + /* Set volume label */ >> + dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */ >> + res = dir_sdi(&dj, 0); >> + if (res == FR_OK) { >> + res = DIR_READ_LABEL(&dj); /* Get volume label entry */ >> + if (res == FR_OK) { >> + if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { >> + dj.dir[XDIR_NumLabel] = (BYTE)di; /* Change the >> volume label */ >> + mem_cpy(dj.dir + XDIR_Label, dirvn, 22); >> + } else { >> + if (di != 0) { >> + mem_cpy(dj.dir, dirvn, 11); /* Change the >> volume label */ >> + } else { >> + dj.dir[DIR_Name] = DDEM; /* Remove the volume >> label */ >> + } >> + } >> + fs->wflag = 1; >> + res = sync_fs(fs); >> + } else { /* No volume label entry or an error */ >> + if (res == FR_NO_FILE) { >> + res = FR_OK; >> + if (di != 0) { /* Create a volume label entry */ >> + res = dir_alloc(&dj, 1); /* Allocate an entry */ >> + if (res == FR_OK) { >> + mem_set(dj.dir, 0, SZDIRE); /* Clean the >> entry */ >> + if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { >> + dj.dir[XDIR_Type] = ET_VLABEL; /* >> Create volume label entry */ >> + dj.dir[XDIR_NumLabel] = (BYTE)di; >> + mem_cpy(dj.dir + XDIR_Label, dirvn, 22); >> + } else { >> + dj.dir[DIR_Attr] = AM_VOL; /* >> Create volume label entry */ >> + mem_cpy(dj.dir, dirvn, 11); >> + } >> + fs->wflag = 1; >> + res = sync_fs(fs); >> + } >> + } >> + } >> + } >> + } >> + >> + LEAVE_FF(fs, res); >> +} >> + >> +#endif /* !FF_FS_READONLY */ >> +#endif /* FF_USE_LABEL */ >> + >> + >> + >> +#if FF_USE_EXPAND && !FF_FS_READONLY >> +/*-----------------------------------------------------------------------*/ >> >> +/* Allocate a Contiguous Blocks to the >> File */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_expand ( >> + FIL* fp, /* Pointer to the file object */ >> + FSIZE_t fsz, /* File size to be expanded to */ >> + BYTE opt /* Operation mode 0:Find and prepare or 1:Find >> and allocate */ >> +) >> +{ >> + FRESULT res; >> + FATFS *fs; >> + DWORD n, clst, stcl, scl, ncl, tcl, lclst; >> + >> + >> + res = validate(&fp->obj, &fs); /* Check validity of the >> file object */ >> + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) >> LEAVE_FF(fs, res); >> + if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) >> LEAVE_FF(fs, FR_DENIED); >> +#if FF_FS_EXFAT >> + if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, >> FR_DENIED); /* Check if in size limit */ >> +#endif >> + n = (DWORD)fs->csize * SS(fs); /* Cluster size */ >> + tcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0); /* Number >> of clusters required */ >> + stcl = fs->last_clst; lclst = 0; >> + if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2; >> + >> +#if FF_FS_EXFAT >> + if (fs->fs_type == FS_EXFAT) { >> + scl = find_bitmap(fs, stcl, tcl); /* Find a >> contiguous cluster block */ >> + if (scl == 0) res = FR_DENIED; /* No >> contiguous cluster block was found */ >> + if (scl == 0xFFFFFFFF) res = FR_DISK_ERR; >> + if (res == FR_OK) { /* A contiguous free area is found */ >> + if (opt) { /* Allocate it now */ >> + res = change_bitmap(fs, scl, tcl, 1); /* Mark the >> cluster block 'in use' */ >> + lclst = scl + tcl - 1; >> + } else { /* Set it as suggested point for next >> allocation */ >> + lclst = scl - 1; >> + } >> + } >> + } else >> +#endif >> + { >> + scl = clst = stcl; ncl = 0; >> + for (;;) { /* Find a contiguous cluster block */ >> + n = get_fat(&fp->obj, clst); >> + if (++clst >= fs->n_fatent) clst = 2; >> + if (n == 1) { res = FR_INT_ERR; break; } >> + if (n == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } >> + if (n == 0) { /* Is it a free cluster? */ >> + if (++ncl == tcl) break; /* Break if a contiguous >> cluster block is found */ >> + } else { >> + scl = clst; ncl = 0; /* Not a free cluster */ >> + } >> + if (clst == stcl) { res = FR_DENIED; break; } /* No >> contiguous cluster? */ >> + } >> + if (res == FR_OK) { /* A contiguous free area is found */ >> + if (opt) { /* Allocate it now */ >> + for (clst = scl, n = tcl; n; clst++, n--) { /* >> Create a cluster chain on the FAT */ >> + res = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : >> clst + 1); >> + if (res != FR_OK) break; >> + lclst = clst; >> + } >> + } else { /* Set it as suggested point for next >> allocation */ >> + lclst = scl - 1; >> + } >> + } >> + } >> + >> + if (res == FR_OK) { >> + fs->last_clst = lclst; /* Set suggested start cluster >> to start next */ >> + if (opt) { /* Is it allocated now? */ >> + fp->obj.sclust = scl; /* Update object allocation >> information */ >> + fp->obj.objsize = fsz; >> + if (FF_FS_EXFAT) fp->obj.stat = 2; /* Set status >> 'contiguous chain' */ >> + fp->flag |= FA_MODIFIED; >> + if (fs->free_clst <= fs->n_fatent - 2) { /* Update >> FSINFO */ >> + fs->free_clst -= tcl; >> + fs->fsi_flag |= 1; >> + } >> + } >> + } >> + >> + LEAVE_FF(fs, res); >> +} >> + >> +#endif /* FF_USE_EXPAND && !FF_FS_READONLY */ >> + >> + >> + >> +#if FF_USE_FORWARD >> +/*-----------------------------------------------------------------------*/ >> >> +/* Forward Data to the Stream >> Directly */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_forward ( >> + FIL* fp, /* Pointer to the file object */ >> + UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming >> function */ >> + UINT btf, /* Number of bytes to forward */ >> + UINT* bf /* Pointer to number of bytes >> forwarded */ >> +) >> +{ >> + FRESULT res; >> + FATFS *fs; >> + DWORD clst; >> + LBA_t sect; >> + FSIZE_t remain; >> + UINT rcnt, csect; >> + BYTE *dbuf; >> + >> + >> + *bf = 0; /* Clear transfer byte counter */ >> + res = validate(&fp->obj, &fs); /* Check validity of the >> file object */ >> + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) >> LEAVE_FF(fs, res); >> + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check >> access mode */ >> + >> + remain = fp->obj.objsize - fp->fptr; >> + if (btf > remain) btf = (UINT)remain; /* Truncate btf >> by remaining bytes */ >> + >> + for ( ; btf && (*func)(0, 0); /* Repeat until >> all data transferred or stream goes busy */ >> + fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { >> + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* >> Sector offset in the cluster */ >> + if (fp->fptr % SS(fs) == 0) { /* On the sector >> boundary? */ >> + if (csect == 0) { /* On the >> cluster boundary? */ >> + clst = (fp->fptr == 0) ? /* On the top of >> the file? */ >> + fp->obj.sclust : get_fat(&fp->obj, fp->clust); >> + if (clst <= 1) ABORT(fs, FR_INT_ERR); >> + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); >> + fp->clust = clst; /* Update >> current cluster */ >> + } >> + } >> + sect = clst2sect(fs, fp->clust); /* Get current >> data sector */ >> + if (sect == 0) ABORT(fs, FR_INT_ERR); >> + sect += csect; >> +#if FF_FS_TINY >> + if (move_window(fs, sect) != FR_OK) ABORT(fs, >> FR_DISK_ERR); /* Move sector window to the file data */ >> + dbuf = fs->win; >> +#else >> + if (fp->sect != sect) { /* Fill sector cache with file >> data */ >> +#if !FF_FS_READONLY >> + if (fp->flag & FA_DIRTY) { /* Write-back dirty >> sector cache */ >> + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != >> RES_OK) ABORT(fs, FR_DISK_ERR); >> + fp->flag &= (BYTE)~FA_DIRTY; >> + } >> +#endif >> + if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) >> ABORT(fs, FR_DISK_ERR); >> + } >> + dbuf = fp->buf; >> +#endif >> + fp->sect = sect; >> + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of >> bytes remains in the sector */ >> + if (rcnt > btf) rcnt = btf; /* Clip it by >> btr if needed */ >> + rcnt = (*func)(dbuf + ((UINT)fp->fptr % SS(fs)), rcnt); /* >> Forward the file data */ >> + if (rcnt == 0) ABORT(fs, FR_INT_ERR); >> + } >> + >> + LEAVE_FF(fs, FR_OK); >> +} >> +#endif /* FF_USE_FORWARD */ >> + >> + >> + >> +#if !FF_FS_READONLY && FF_USE_MKFS >> +/*-----------------------------------------------------------------------*/ >> >> +/* Create FAT/exFAT >> volume */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +#define N_SEC_TRACK 63 /* Sectors per track for >> determination of drive CHS */ >> +#define GPT_ALIGN 0x100000 /* Alignment of partitions in GPT >> [byte] (>=128KB) */ >> +#define GPT_ITEMS 128 /* Number of GPT table size >> (>=128, sector aligned) */ >> + >> + >> +/* Create partitions on the physical drive in format of MBR or GPT */ >> + >> +static FRESULT create_partition ( >> + BYTE drv, /* Physical drive number */ >> + const LBA_t plst[], /* Partition list */ >> + BYTE sys, /* System ID (for only MBR, temp setting) */ >> + BYTE* buf /* Working buffer for a sector */ >> +) >> +{ >> + UINT i, cy; >> + LBA_t sz_drv; >> + DWORD sz_drv32, s_lba32, n_lba32; >> + BYTE *pte, hd, n_hd, sc, n_sc; >> + >> + /* Get drive size */ >> + if (disk_ioctl(drv, GET_SECTOR_COUNT, &sz_drv) != RES_OK) return >> FR_DISK_ERR; >> + >> +#if FF_LBA64 >> + if (sz_drv >= FF_MIN_GPT) { /* Create partitions in GPT */ >> + WORD ss; >> + UINT sz_pt, pi, si, ofs; >> + DWORD bcc, rnd, align; >> + QWORD s_lba64, n_lba64, sz_pool, s_bpt; >> + static const BYTE gpt_mbr[16] = {0x00, 0x00, 0x02, 0x00, >> 0xEE, 0xFE, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; >> + >> +#if FF_MAX_SS != FF_MIN_SS >> + if (disk_ioctl(drv, GET_SECTOR_SIZE, &ss) != RES_OK) return >> FR_DISK_ERR; /* Get sector size */ >> + if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) >> return FR_DISK_ERR; >> +#else >> + ss = FF_MAX_SS; >> +#endif >> + rnd = GET_FATTIME(); /* Random seed */ >> + align = GPT_ALIGN / ss; /* Partition alignment >> [sector] */ >> + sz_pt = GPT_ITEMS * SZ_GPTE / ss; /* Size of PT [sector] */ >> + s_bpt = sz_drv - sz_pt - 1; /* Backup PT start sector */ >> + s_lba64 = 2 + sz_pt; /* First allocatable sector */ >> + sz_pool = s_bpt - s_lba64; /* Size of allocatable area */ >> + bcc = 0xFFFFFFFF; n_lba64 = 1; >> + pi = si = 0; /* partition table index, size table index */ >> + do { >> + if (pi * SZ_GPTE % ss == 0) mem_set(buf, 0, ss); /* >> Clean the buffer if needed */ >> + if (n_lba64 != 0) { /* Is the size table not >> termintated? */ >> + s_lba64 = (s_lba64 + align - 1) & ((QWORD)0 - >> align); /* Align partition start */ >> + n_lba64 = plst[si++]; /* Get a partition size */ >> + if (n_lba64 <= 100) { /* Is the size in >> percentage? */ >> + n_lba64 = sz_pool * n_lba64 / 100; >> + n_lba64 = (n_lba64 + align - 1) & ((QWORD)0 - >> align); /* Align partition end (only if in percentage) */ >> + } >> + if (s_lba64 + n_lba64 > s_bpt) { /* Clip at end of >> the pool */ >> + n_lba64 = (s_lba64 < s_bpt) ? s_bpt - s_lba64 : 0; >> + } >> + } >> + if (n_lba64 != 0) { /* Add a partition? */ >> + ofs = pi * SZ_GPTE % ss; >> + mem_cpy(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, >> 16); /* Partition GUID (Microsoft Basic Data) */ >> + rnd = make_rand(rnd, buf + ofs + GPTE_UpGuid, >> 16); /* Unique partition GUID */ >> + st_qword(buf + ofs + GPTE_FstLba, >> s_lba64); /* Partition start LBA */ >> + st_qword(buf + ofs + GPTE_LstLba, s_lba64 + n_lba64 - >> 1); /* Partition end LBA */ >> + s_lba64 += n_lba64; /* Next partition LBA */ >> + } >> + if ((pi + 1) * SZ_GPTE % ss == 0) { /* Write the >> buffer if it is filled up */ >> + for (i = 0; i < ss; bcc = crc32(bcc, buf[i++])) ; >> /* Calculate table check sum */ >> + if (disk_write(drv, buf, 2 + pi * SZ_GPTE / ss, 1) != >> RES_OK) return FR_DISK_ERR; /* Primary table */ >> + if (disk_write(drv, buf, s_bpt + pi * SZ_GPTE / ss, >> 1) != RES_OK) return FR_DISK_ERR; /* Secondary table */ >> + } >> + } while (++pi < GPT_ITEMS); >> + >> + /* Create primary GPT header */ >> + mem_set(buf, 0, ss); >> + mem_cpy(buf + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", >> 16); /* Signature, version (1.0) and size (92) */ >> + st_dword(buf + GPTH_PtBcc, ~bcc); /* Table >> check sum */ >> + st_qword(buf + GPTH_CurLba, 1); /* LBA of >> this header */ >> + st_qword(buf + GPTH_BakLba, sz_drv - 1); /* LBA of >> another header */ >> + st_qword(buf + GPTH_FstLba, 2 + sz_pt); /* LBA of >> first allocatable sector */ >> + st_qword(buf + GPTH_LstLba, s_bpt - 1); /* LBA of >> last allocatable sector */ >> + st_dword(buf + GPTH_PteSize, SZ_GPTE); /* Size of >> a table entry */ >> + st_dword(buf + GPTH_PtNum, GPT_ITEMS); /* Number >> of table entries */ >> + st_dword(buf + GPTH_PtOfs, 2); /* LBA of >> this table */ >> + rnd = make_rand(rnd, buf + GPTH_DskGuid, 16); /* Disk GUID */ >> + for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, >> buf[i++])) ; /* Calculate header check sum */ >> + st_dword(buf + GPTH_Bcc, ~bcc); /* Header >> check sum */ >> + if (disk_write(drv, buf, 1, 1) != RES_OK) return FR_DISK_ERR; >> + >> + /* Create secondary GPT header */ >> + st_qword(buf + GPTH_CurLba, sz_drv - 1); /* LBA of >> this header */ >> + st_qword(buf + GPTH_BakLba, 1); /* LBA of >> another header */ >> + st_qword(buf + GPTH_PtOfs, s_bpt); /* LBA of >> this table */ >> + st_dword(buf + GPTH_Bcc, 0); >> + for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, >> buf[i++])) ; /* Calculate header check sum */ >> + st_dword(buf + GPTH_Bcc, ~bcc); /* Header >> check sum */ >> + if (disk_write(drv, buf, sz_drv - 1, 1) != RES_OK) return >> FR_DISK_ERR; >> + >> + /* Create protective MBR */ >> + mem_set(buf, 0, ss); >> + mem_cpy(buf + MBR_Table, gpt_mbr, 16); /* Create a >> GPT partition */ >> + st_word(buf + BS_55AA, 0xAA55); >> + if (disk_write(drv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; >> + >> + } else >> +#endif >> + { /* Create partitions in MBR */ >> + sz_drv32 = (DWORD)sz_drv; >> + n_sc = N_SEC_TRACK; /* Determine drive CHS without any >> consideration of the drive geometry */ >> + for (n_hd = 8; n_hd != 0 && sz_drv32 / n_hd / n_sc > 1024; >> n_hd *= 2) ; >> + if (n_hd == 0) n_hd = 255; /* Number of heads needs to be >> <256 */ >> + >> + mem_set(buf, 0, FF_MAX_SS); /* Clear MBR */ >> + pte = buf + MBR_Table; /* Partition table in the MBR */ >> + for (i = 0, s_lba32 = n_sc; i < 4 && s_lba32 != 0 && s_lba32 >> < sz_drv32; i++, s_lba32 += n_lba32) { >> + n_lba32 = (DWORD)plst[i]; /* Get partition size */ >> + if (n_lba32 <= 100) n_lba32 = (n_lba32 == 100) ? sz_drv32 >> : sz_drv32 / 100 * n_lba32; /* Size in percentage? */ >> + if (s_lba32 + n_lba32 > sz_drv32 || s_lba32 + n_lba32 < >> s_lba32) n_lba32 = sz_drv32 - s_lba32; /* Clip at drive size */ >> + if (n_lba32 == 0) break; /* End of table or no sector >> to allocate? */ >> + >> + st_dword(pte + PTE_StLba, s_lba32); /* Start LBA */ >> + st_dword(pte + PTE_SizLba, n_lba32); /* Number of >> sectors */ >> + pte[PTE_System] = sys; /* System type */ >> + >> + cy = (UINT)(s_lba32 / n_sc / n_hd); /* Start >> cylinder */ >> + hd = (BYTE)(s_lba32 / n_sc % n_hd); /* Start head */ >> + sc = (BYTE)(s_lba32 % n_sc + 1); /* Start sector */ >> + pte[PTE_StHead] = hd; >> + pte[PTE_StSec] = (BYTE)((cy >> 2 & 0xC0) | sc); >> + pte[PTE_StCyl] = (BYTE)cy; >> + >> + cy = (UINT)((s_lba32 + n_lba32 - 1) / n_sc / n_hd); /* >> End cylinder */ >> + hd = (BYTE)((s_lba32 + n_lba32 - 1) / n_sc % n_hd); /* >> End head */ >> + sc = (BYTE)((s_lba32 + n_lba32 - 1) % n_sc + 1); /* >> End sector */ >> + pte[PTE_EdHead] = hd; >> + pte[PTE_EdSec] = (BYTE)((cy >> 2 & 0xC0) | sc); >> + pte[PTE_EdCyl] = (BYTE)cy; >> + >> + pte += SZ_PTE; /* Next entry */ >> + } >> + >> + st_word(buf + BS_55AA, 0xAA55); /* MBR signature */ >> + if (disk_write(drv, buf, 0, 1) != RES_OK) return >> FR_DISK_ERR; /* Write it to the MBR */ >> + } >> + >> + return FR_OK; >> +} >> + >> + >> + >> +FRESULT f_mkfs ( >> + const TCHAR* path, /* Logical drive number */ >> + const MKFS_PARM* opt, /* Format options */ >> + void* work, /* Pointer to working buffer (null: >> use heap memory) */ >> + UINT len /* Size of working buffer [byte] */ >> +) >> +{ >> + static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* >> Cluster size boundary for FAT volume (4Ks unit) */ >> + static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* >> Cluster size boundary for FAT32 volume (128Ks unit) */ >> + static const MKFS_PARM defopt = {FM_ANY, 0, 0, 0, 0}; /* >> Default parameter */ >> + BYTE fsopt, fsty, sys, *buf, *pte, pdrv, ipart; >> + WORD ss; /* Sector size */ >> + DWORD sz_buf, sz_blk, n_clst, pau, nsect, n; >> + LBA_t sz_vol, b_vol, b_fat, b_data; /* Size of volume, >> Base LBA of volume, fat, data */ >> + LBA_t sect, lba[2]; >> + DWORD sz_rsv, sz_fat, sz_dir, sz_au; /* Size of reserved, fat, >> dir, data, cluster */ >> + UINT n_fat, n_root, i; /* Index, Number of >> FATs and Number of roor dir entries */ >> + int vol; >> + DSTATUS ds; >> + FRESULT fr; >> + >> + >> + /* Check mounted drive and clear work area */ >> + vol = get_ldnumber(&path); /* Get target >> logical drive */ >> + if (vol < 0) return FR_INVALID_DRIVE; >> + if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear the fs >> object if mounted */ >> + pdrv = LD2PD(vol); /* Physical drive */ >> + ipart = LD2PT(vol); /* Partition (0:create as new, >> 1..:get from partition table) */ >> + if (!opt) opt = &defopt; /* Use default parameter if it is not >> given */ >> + >> + /* Get physical drive status (sz_drv, sz_blk, ss) */ >> + ds = disk_initialize(pdrv); >> + if (ds & STA_NOINIT) return FR_NOT_READY; >> + if (ds & STA_PROTECT) return FR_WRITE_PROTECTED; >> + sz_blk = opt->align; >> + if (sz_blk == 0 && disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != >> RES_OK) sz_blk = 1; >> + if (sz_blk == 0 || sz_blk > 0x8000 || (sz_blk & (sz_blk - 1))) >> sz_blk = 1; >> +#if FF_MAX_SS != FF_MIN_SS >> + if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return >> FR_DISK_ERR; >> + if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) return >> FR_DISK_ERR; >> +#else >> + ss = FF_MAX_SS; >> +#endif >> + /* Options for FAT sub-type and FAT parameters */ >> + fsopt = opt->fmt & (FM_ANY | FM_SFD); >> + n_fat = (opt->n_fat >= 1 && opt->n_fat <= 2) ? opt->n_fat : 1; >> + n_root = (opt->n_root >= 1 && opt->n_root <= 32768 && >> (opt->n_root % (ss / SZDIRE)) == 0) ? opt->n_root : 512; >> + sz_au = (opt->au_size <= 0x1000000 && (opt->au_size & >> (opt->au_size - 1)) == 0) ? opt->au_size : 0; >> + sz_au /= ss; /* Byte --> Sector */ >> + >> + /* Get working buffer */ >> + sz_buf = len / ss; /* Size of working buffer [sector] */ >> + if (sz_buf == 0) return FR_NOT_ENOUGH_CORE; >> + buf = (BYTE*)work; /* Working buffer */ >> +#if FF_USE_LFN == 3 >> + if (!buf) buf = ff_memalloc(sz_buf * ss); /* Use heap memory >> for working buffer */ >> +#endif >> + if (!buf) return FR_NOT_ENOUGH_CORE; >> + >> + /* Determine where the volume to be located (b_vol, sz_vol) */ >> + b_vol = sz_vol = 0; >> + if (FF_MULTI_PARTITION && ipart != 0) { /* Is the volume >> associated with any specific partition? */ >> + /* Get partition location from the existing partition table */ >> + if (disk_read(pdrv, buf, 0, 1) != RES_OK) >> LEAVE_MKFS(FR_DISK_ERR); /* Load MBR */ >> + if (ld_word(buf + BS_55AA) != 0xAA55) >> LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if MBR is valid */ >> +#if FF_LBA64 >> + if (buf[MBR_Table + PTE_System] == 0xEE) { /* GPT >> protective MBR? */ >> + DWORD n_ent, ofs; >> + QWORD pt_lba; >> + >> + /* Get the partition location from GPT */ >> + if (disk_read(pdrv, buf, 1, 1) != RES_OK) >> LEAVE_MKFS(FR_DISK_ERR); /* Load GPT header sector (next to MBR) */ >> + if (!test_gpt_header(buf)) >> LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if GPT header is valid */ >> + n_ent = ld_dword(buf + GPTH_PtNum); /* Number of >> entries */ >> + pt_lba = ld_qword(buf + GPTH_PtOfs); /* Table start >> sector */ >> + ofs = i = 0; >> + while (n_ent) { /* Find MS Basic partition with >> order of ipart */ >> + if (ofs == 0 && disk_read(pdrv, buf, pt_lba++, 1) != >> RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Get PT sector */ >> + if (!mem_cmp(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, >> 16) && ++i == ipart) { /* MS basic data partition? */ >> + b_vol = ld_qword(buf + ofs + GPTE_FstLba); >> + sz_vol = ld_qword(buf + ofs + GPTE_LstLba) - >> b_vol + 1; >> + break; >> + } >> + n_ent--; ofs = (ofs + SZ_GPTE) % ss; /* Next entry */ >> + } >> + if (n_ent == 0) LEAVE_MKFS(FR_MKFS_ABORTED); /* >> Partition not found */ >> + fsopt |= 0x80; /* Partitioning is in GPT */ >> + } else >> +#endif >> + { /* Get the partition location from MBR partition table */ >> + pte = buf + (MBR_Table + (ipart - 1) * SZ_PTE); >> + if (ipart > 4 || pte[PTE_System] == 0) >> LEAVE_MKFS(FR_MKFS_ABORTED); /* No partition? */ >> + b_vol = ld_dword(pte + PTE_StLba); /* Get volume >> start sector */ >> + sz_vol = ld_dword(pte + PTE_SizLba); /* Get volume >> size */ >> + } >> + } else { /* The volume is associated with a physical drive */ >> + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) >> LEAVE_MKFS(FR_DISK_ERR); >> + if (!(fsopt & FM_SFD)) { /* To be partitioned? */ >> + /* Create a single-partition on the drive in this >> function */ >> +#if FF_LBA64 >> + if (sz_vol >= FF_MIN_GPT) { /* Which partition type to >> create, MBR or GPT? */ >> + fsopt |= 0x80; /* Partitioning is in GPT */ >> + b_vol = GPT_ALIGN / ss; sz_vol -= b_vol + GPT_ITEMS * >> SZ_GPTE / ss + 1; /* Estimated partition offset and size */ >> + } else >> +#endif >> + { /* Partitioning is in MBR */ >> + if (sz_vol > N_SEC_TRACK) { >> + b_vol = N_SEC_TRACK; sz_vol -= b_vol; /* >> Estimated partition offset and size */ >> + } >> + } >> + } >> + } >> + if (sz_vol < 128) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if >> volume size is >=128s */ >> + >> + /* Now start to create a FAT volume at b_vol and sz_vol */ >> + >> + do { /* Pre-determine the FAT type */ >> + if (FF_FS_EXFAT && (fsopt & FM_EXFAT)) { /* exFAT >> possible? */ >> + if ((fsopt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 >> || sz_au > 128) { /* exFAT only, vol >= 64MS or sz_au > 128S ? */ >> + fsty = FS_EXFAT; break; >> + } >> + } >> +#if FF_LBA64 >> + if (sz_vol >= 0x100000000) LEAVE_MKFS(FR_MKFS_ABORTED); /* >> Too large volume for FAT/FAT32 */ >> +#endif >> + if (sz_au > 128) sz_au = 128; /* Invalid AU for FAT/FAT32? */ >> + if (fsopt & FM_FAT32) { /* FAT32 possible? */ >> + if (!(fsopt & FM_FAT)) { /* no-FAT? */ >> + fsty = FS_FAT32; break; >> + } >> + } >> + if (!(fsopt & FM_FAT)) LEAVE_MKFS(FR_INVALID_PARAMETER); >> /* no-FAT? */ >> + fsty = FS_FAT16; >> + } while (0); >> + >> +#if FF_FS_EXFAT >> + if (fsty == FS_EXFAT) { /* Create an exFAT volume */ >> + DWORD szb_bit, szb_case, sum, nbit, clu, clen[3]; >> + WCHAR ch, si; >> + UINT j, st; >> + >> + if (sz_vol < 0x1000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too >> small volume for exFAT? */ >> +#if FF_USE_TRIM >> + lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform >> storage device that the volume area may be erased */ >> + disk_ioctl(pdrv, CTRL_TRIM, lba); >> +#endif >> + /* Determine FAT location, data location and number of >> clusters */ >> + if (sz_au == 0) { /* AU auto-selection */ >> + sz_au = 8; >> + if (sz_vol >= 0x80000) sz_au = 64; /* >= 512Ks */ >> + if (sz_vol >= 0x4000000) sz_au = 256; /* >= 64Ms */ >> + } >> + b_fat = b_vol + 32; /* >> FAT start at offset 32 */ >> + sz_fat = (DWORD)((sz_vol / sz_au + 2) * 4 + ss - 1) / ss; >> /* Number of FAT sectors */ >> + b_data = (b_fat + sz_fat + sz_blk - 1) & ~((LBA_t)sz_blk - >> 1); /* Align data area to the erase block boundary */ >> + if (b_data - b_vol >= sz_vol / 2) >> LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */ >> + n_clst = (DWORD)(sz_vol - (b_data - b_vol)) / sz_au; /* >> Number of clusters */ >> + if (n_clst <16) LEAVE_MKFS(FR_MKFS_ABORTED); /* >> Too few clusters? */ >> + if (n_clst > MAX_EXFAT) LEAVE_MKFS(FR_MKFS_ABORTED); /* >> Too many clusters? */ >> + >> + szb_bit = (n_clst + 7) / 8; /* >> Size of allocation bitmap */ >> + clen[0] = (szb_bit + sz_au * ss - 1) / (sz_au * ss); /* >> Number of allocation bitmap clusters */ >> + >> + /* Create a compressed up-case table */ >> + sect = b_data + sz_au * clen[0]; /* Table start sector */ >> + sum = 0; /* Table checksum to be >> stored in the 82 entry */ >> + st = 0; si = 0; i = 0; j = 0; szb_case = 0; >> + do { >> + switch (st) { >> + case 0: >> + ch = (WCHAR)ff_wtoupper(si); /* Get an up-case >> char */ >> + if (ch != si) { >> + si++; break; /* Store the up-case char if >> exist */ >> + } >> + for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == >> ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case >> block */ >> + if (j >= 128) { >> + ch = 0xFFFF; st = 2; break; /* Compress the >> no-case block if run is >= 128 chars */ >> + } >> + st = 1; /* Do not compress short run */ >> + /* FALLTHROUGH */ >> + case 1: >> + ch = si++; /* Fill the short run */ >> + if (--j == 0) st = 0; >> + break; >> + >> + default: >> + ch = (WCHAR)j; si += (WCHAR)j; /* Number of chars >> to skip */ >> + st = 0; >> + } >> + sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put >> it into the write buffer */ >> + sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum); >> + i += 2; szb_case += 2; >> + if (si == 0 || i == sz_buf * ss) { /* Write >> buffered data when buffer full or end of process */ >> + n = (i + ss - 1) / ss; >> + if (disk_write(pdrv, buf, sect, n) != RES_OK) >> LEAVE_MKFS(FR_DISK_ERR); >> + sect += n; i = 0; >> + } >> + } while (si); >> + clen[1] = (szb_case + sz_au * ss - 1) / (sz_au * ss); /* >> Number of up-case table clusters */ >> + clen[2] = 1; /* Number of root dir clusters */ >> + >> + /* Initialize the allocation bitmap */ >> + sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start >> of bitmap and number of bitmap sectors */ >> + nbit = clen[0] + clen[1] + clen[2]; /* Number >> of clusters in-use by system (bitmap, up-case and root-dir) */ >> + do { >> + mem_set(buf, 0, sz_buf * ss); /* >> Initialize bitmap buffer */ >> + for (i = 0; nbit != 0 && i / 8 < sz_buf * ss; buf[i / 8] >> |= 1 << (i % 8), i++, nbit--) ; /* Mark used clusters */ >> + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write >> the buffered data */ >> + if (disk_write(pdrv, buf, sect, n) != RES_OK) >> LEAVE_MKFS(FR_DISK_ERR); >> + sect += n; nsect -= n; >> + } while (nsect); >> + >> + /* Initialize the FAT */ >> + sect = b_fat; nsect = sz_fat; /* Start of FAT and number >> of FAT sectors */ >> + j = nbit = clu = 0; >> + do { >> + mem_set(buf, 0, sz_buf * ss); i = 0; /* Clear work >> area and reset write offset */ >> + if (clu == 0) { /* Initialize FAT [0] and FAT[1] */ >> + st_dword(buf + i, 0xFFFFFFF8); i += 4; clu++; >> + st_dword(buf + i, 0xFFFFFFFF); i += 4; clu++; >> + } >> + do { /* Create chains of bitmap, up-case and >> root dir */ >> + while (nbit != 0 && i < sz_buf * ss) { /* Create a >> chain */ >> + st_dword(buf + i, (nbit > 1) ? clu + 1 : >> 0xFFFFFFFF); >> + i += 4; clu++; nbit--; >> + } >> + if (nbit == 0 && j < 3) nbit = clen[j++]; /* Get >> next chain length */ >> + } while (nbit != 0 && i < sz_buf * ss); >> + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the >> buffered data */ >> + if (disk_write(pdrv, buf, sect, n) != RES_OK) >> LEAVE_MKFS(FR_DISK_ERR); >> + sect += n; nsect -= n; >> + } while (nsect); >> + >> + /* Initialize the root directory */ >> + mem_set(buf, 0, sz_buf * ss); >> + buf[SZDIRE * 0 + 0] = ET_VLABEL; /* Volume >> label entry (no label) */ >> + buf[SZDIRE * 1 + 0] = ET_BITMAP; /* Bitmap >> entry */ >> + st_dword(buf + SZDIRE * 1 + 20, 2); /* >> cluster */ >> + st_dword(buf + SZDIRE * 1 + 24, szb_bit); /* size */ >> + buf[SZDIRE * 2 + 0] = ET_UPCASE; /* >> Up-case table entry */ >> + st_dword(buf + SZDIRE * 2 + 4, sum); /* sum */ >> + st_dword(buf + SZDIRE * 2 + 20, 2 + clen[0]); /* >> cluster */ >> + st_dword(buf + SZDIRE * 2 + 24, szb_case); /* >> size */ >> + sect = b_data + sz_au * (clen[0] + clen[1]); nsect = >> sz_au; /* Start of the root directory and number of sectors */ >> + do { /* Fill root directory sectors */ >> + n = (nsect > sz_buf) ? sz_buf : nsect; >> + if (disk_write(pdrv, buf, sect, n) != RES_OK) >> LEAVE_MKFS(FR_DISK_ERR); >> + mem_set(buf, 0, ss); /* Rest of entries are filled >> with zero */ >> + sect += n; nsect -= n; >> + } while (nsect); >> + >> + /* Create two set of the exFAT VBR blocks */ >> + sect = b_vol; >> + for (n = 0; n < 2; n++) { >> + /* Main record (+0) */ >> + mem_set(buf, 0, ss); >> + mem_cpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", >> 11); /* Boot jump code (x86), OEM name */ >> + st_qword(buf + BPB_VolOfsEx, b_vol); >> /* Volume offset in the physical drive [sector] */ >> + st_qword(buf + BPB_TotSecEx, sz_vol); >> /* Volume size [sector] */ >> + st_dword(buf + BPB_FatOfsEx, (DWORD)(b_fat - b_vol)); >> /* FAT offset [sector] */ >> + st_dword(buf + BPB_FatSzEx, sz_fat); >> /* FAT size [sector] */ >> + st_dword(buf + BPB_DataOfsEx, (DWORD)(b_data - >> b_vol)); /* Data offset [sector] */ >> + st_dword(buf + BPB_NumClusEx, n_clst); >> /* Number of clusters */ >> + st_dword(buf + BPB_RootClusEx, 2 + clen[0] + clen[1]); >> /* Root dir cluster # */ >> + st_dword(buf + BPB_VolIDEx, >> GET_FATTIME()); /* VSN */ >> + st_word(buf + BPB_FSVerEx, 0x100); >> /* Filesystem version (1.00) */ >> + for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; >> buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */ >> + for (buf[BPB_SecPerClusEx] = 0, i = sz_au; i >>= 1; >> buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */ >> + buf[BPB_NumFATsEx] = 1; /* Number of >> FATs */ >> + buf[BPB_DrvNumEx] = 0x80; /* Drive number >> (for int13) */ >> + st_word(buf + BS_BootCodeEx, 0xFEEB); /* Boot code >> (x86) */ >> + st_word(buf + BS_55AA, 0xAA55); /* Signature >> (placed here regardless of sector size) */ >> + for (i = sum = 0; i < ss; i++) { /* VBR checksum */ >> + if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i >> != BPB_PercInUseEx) sum = xsum32(buf[i], sum); >> + } >> + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) >> LEAVE_MKFS(FR_DISK_ERR); >> + /* Extended bootstrap record (+1..+8) */ >> + mem_set(buf, 0, ss); >> + st_word(buf + ss - 2, 0xAA55); /* Signature (placed at >> end of sector) */ >> + for (j = 1; j < 9; j++) { >> + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; >> /* VBR checksum */ >> + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) >> LEAVE_MKFS(FR_DISK_ERR); >> + } >> + /* OEM/Reserved record (+9..+10) */ >> + mem_set(buf, 0, ss); >> + for ( ; j < 11; j++) { >> + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; >> /* VBR checksum */ >> + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) >> LEAVE_MKFS(FR_DISK_ERR); >> + } >> + /* Sum record (+11) */ >> + for (i = 0; i < ss; i += 4) st_dword(buf + i, >> sum); /* Fill with checksum value */ >> + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) >> LEAVE_MKFS(FR_DISK_ERR); >> + } >> + >> + } else >> +#endif /* FF_FS_EXFAT */ >> + { /* Create an FAT/FAT32 volume */ >> + do { >> + pau = sz_au; >> + /* Pre-determine number of clusters and FAT sub-type */ >> + if (fsty == FS_FAT32) { /* FAT32 volume */ >> + if (pau == 0) { /* AU auto-selection */ >> + n = (DWORD)sz_vol / 0x20000; /* Volume size in >> unit of 128KS */ >> + for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; >> i++, pau <<= 1) ; /* Get from table */ >> + } >> + n_clst = (DWORD)sz_vol / pau; /* Number of >> clusters */ >> + sz_fat = (n_clst * 4 + 8 + ss - 1) / ss; /* FAT >> size [sector] */ >> + sz_rsv = 32; /* Number of reserved sectors */ >> + sz_dir = 0; /* No static directory */ >> + if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) >> LEAVE_MKFS(FR_MKFS_ABORTED); >> + } else { /* FAT volume */ >> + if (pau == 0) { /* au auto-selection */ >> + n = (DWORD)sz_vol / 0x1000; /* Volume size in >> unit of 4KS */ >> + for (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, >> pau <<= 1) ; /* Get from table */ >> + } >> + n_clst = (DWORD)sz_vol / pau; >> + if (n_clst > MAX_FAT12) { >> + n = n_clst * 2 + 4; /* FAT size [byte] */ >> + } else { >> + fsty = FS_FAT12; >> + n = (n_clst * 3 + 1) / 2 + 3; /* FAT size >> [byte] */ >> + } >> + sz_fat = (n + ss - 1) / ss; /* FAT size >> [sector] */ >> + sz_rsv = 1; /* Number of >> reserved sectors */ >> + sz_dir = (DWORD)n_root * SZDIRE / ss; /* Root dir >> size [sector] */ >> + } >> + b_fat = b_vol + sz_rsv; /* FAT >> base */ >> + b_data = b_fat + sz_fat * n_fat + sz_dir; /* Data base */ >> + >> + /* Align data area to erase block boundary (for flash >> memory media) */ >> + n = (DWORD)(((b_data + sz_blk - 1) & ~(sz_blk - 1)) - >> b_data); /* Sectors to next nearest from current data base */ >> + if (fsty == FS_FAT32) { /* FAT32: Move FAT */ >> + sz_rsv += n; b_fat += n; >> + } else { /* FAT: Expand FAT */ >> + if (n % n_fat) { /* Adjust fractional error if >> needed */ >> + n--; sz_rsv++; b_fat++; >> + } >> + sz_fat += n / n_fat; >> + } >> + >> + /* Determine number of clusters and final check of >> validity of the FAT sub-type */ >> + if (sz_vol < b_data + pau * 16 - b_vol) >> LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */ >> + n_clst = ((DWORD)sz_vol - sz_rsv - sz_fat * n_fat - >> sz_dir) / pau; >> + if (fsty == FS_FAT32) { >> + if (n_clst <= MAX_FAT16) { /* Too few clusters for >> FAT32? */ >> + if (sz_au == 0 && (sz_au = pau / 2) != 0) >> continue; /* Adjust cluster size and retry */ >> + LEAVE_MKFS(FR_MKFS_ABORTED); >> + } >> + } >> + if (fsty == FS_FAT16) { >> + if (n_clst > MAX_FAT16) { /* Too many clusters for >> FAT16 */ >> + if (sz_au == 0 && (pau * 2) <= 64) { >> + sz_au = pau * 2; continue; /* Adjust >> cluster size and retry */ >> + } >> + if ((fsopt & FM_FAT32)) { >> + fsty = FS_FAT32; continue; /* Switch type >> to FAT32 and retry */ >> + } >> + if (sz_au == 0 && (sz_au = pau * 2) <= 128) >> continue; /* Adjust cluster size and retry */ >> + LEAVE_MKFS(FR_MKFS_ABORTED); >> + } >> + if (n_clst <= MAX_FAT12) { /* Too few clusters >> for FAT16 */ >> + if (sz_au == 0 && (sz_au = pau * 2) <= 128) >> continue; /* Adjust cluster size and retry */ >> + LEAVE_MKFS(FR_MKFS_ABORTED); >> + } >> + } >> + if (fsty == FS_FAT12 && n_clst > MAX_FAT12) >> LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters for FAT12 */ >> + >> + /* Ok, it is the valid cluster configuration */ >> + break; >> + } while (1); >> + >> +#if FF_USE_TRIM >> + lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform >> storage device that the volume area may be erased */ >> + disk_ioctl(pdrv, CTRL_TRIM, lba); >> +#endif >> + /* Create FAT VBR */ >> + mem_set(buf, 0, ss); >> + mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* >> Boot jump code (x86), OEM name */ >> + st_word(buf + BPB_BytsPerSec, ss); /* Sector >> size [byte] */ >> + buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster >> size [sector] */ >> + st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of >> reserved area */ >> + buf[BPB_NumFATs] = (BYTE)n_fat; /* Number >> of FATs */ >> + st_word(buf + BPB_RootEntCnt, (WORD)((fsty == FS_FAT32) ? 0 : >> n_root)); /* Number of root directory entries */ >> + if (sz_vol < 0x10000) { >> + st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume >> size in 16-bit LBA */ >> + } else { >> + st_dword(buf + BPB_TotSec32, (DWORD)sz_vol); /* Volume >> size in 32-bit LBA */ >> + } >> + buf[BPB_Media] = 0xF8; /* Media >> descriptor byte */ >> + st_word(buf + BPB_SecPerTrk, 63); /* Number of >> sectors per track (for int13) */ >> + st_word(buf + BPB_NumHeads, 255); /* Number of >> heads (for int13) */ >> + st_dword(buf + BPB_HiddSec, (DWORD)b_vol); /* Volume >> offset in the physical drive [sector] */ >> + if (fsty == FS_FAT32) { >> + st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */ >> + st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size >> [sector] */ >> + st_dword(buf + BPB_RootClus32, 2); /* Root >> directory cluster # (2) */ >> + st_word(buf + BPB_FSInfo32, 1); /* Offset >> of FSINFO sector (VBR + 1) */ >> + st_word(buf + BPB_BkBootSec32, 6); /* Offset >> of backup VBR (VBR + 6) */ >> + buf[BS_DrvNum32] = 0x80; /* Drive >> number (for int13) */ >> + buf[BS_BootSig32] = 0x29; /* Extended >> boot signature */ >> + mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", >> 19); /* Volume label, FAT signature */ >> + } else { >> + st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */ >> + st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size >> [sector] */ >> + buf[BS_DrvNum] = 0x80; /* Drive >> number (for int13) */ >> + buf[BS_BootSig] = 0x29; /* >> Extended boot signature */ >> + mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", >> 19); /* Volume label, FAT signature */ >> + } >> + st_word(buf + BS_55AA, 0xAA55); /* >> Signature (offset is fixed here regardless of sector size) */ >> + if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) >> LEAVE_MKFS(FR_DISK_ERR); /* Write it to the VBR sector */ >> + >> + /* Create FSINFO record if needed */ >> + if (fsty == FS_FAT32) { >> + disk_write(pdrv, buf, b_vol + 6, 1); /* Write >> backup VBR (VBR + 6) */ >> + mem_set(buf, 0, ss); >> + st_dword(buf + FSI_LeadSig, 0x41615252); >> + st_dword(buf + FSI_StrucSig, 0x61417272); >> + st_dword(buf + FSI_Free_Count, n_clst - 1); /* Number >> of free clusters */ >> + st_dword(buf + FSI_Nxt_Free, 2); /* Last >> allocated cluster# */ >> + st_word(buf + BS_55AA, 0xAA55); >> + disk_write(pdrv, buf, b_vol + 7, 1); /* Write >> backup FSINFO (VBR + 7) */ >> + disk_write(pdrv, buf, b_vol + 1, 1); /* Write >> original FSINFO (VBR + 1) */ >> + } >> + >> + /* Initialize FAT area */ >> + mem_set(buf, 0, sz_buf * ss); >> + sect = b_fat; /* FAT start sector */ >> + for (i = 0; i < n_fat; i++) { /* Initialize FATs >> each */ >> + if (fsty == FS_FAT32) { >> + st_dword(buf + 0, 0xFFFFFFF8); /* FAT[0] */ >> + st_dword(buf + 4, 0xFFFFFFFF); /* FAT[1] */ >> + st_dword(buf + 8, 0x0FFFFFFF); /* FAT[2] (root >> directory) */ >> + } else { >> + st_dword(buf + 0, (fsty == FS_FAT12) ? 0xFFFFF8 : >> 0xFFFFFFF8); /* FAT[0] and FAT[1] */ >> + } >> + nsect = sz_fat; /* Number of FAT sectors */ >> + do { /* Fill FAT sectors */ >> + n = (nsect > sz_buf) ? sz_buf : nsect; >> + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) >> LEAVE_MKFS(FR_DISK_ERR); >> + mem_set(buf, 0, ss); /* Rest of FAT all are >> cleared */ >> + sect += n; nsect -= n; >> + } while (nsect); >> + } >> + >> + /* Initialize root directory (fill with zero) */ >> + nsect = (fsty == FS_FAT32) ? pau : sz_dir; /* Number of >> root directory sectors */ >> + do { >> + n = (nsect > sz_buf) ? sz_buf : nsect; >> + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) >> LEAVE_MKFS(FR_DISK_ERR); >> + sect += n; nsect -= n; >> + } while (nsect); >> + } >> + >> + /* A FAT volume has been created here */ >> + >> + /* Determine system ID in the MBR partition table */ >> + if (FF_FS_EXFAT && fsty == FS_EXFAT) { >> + sys = 0x07; /* exFAT */ >> + } else { >> + if (fsty == FS_FAT32) { >> + sys = 0x0C; /* FAT32X */ >> + } else { >> + if (sz_vol >= 0x10000) { >> + sys = 0x06; /* FAT12/16 (large) */ >> + } else { >> + sys = (fsty == FS_FAT16) ? 0x04 : 0x01; /* FAT16 : >> FAT12 */ >> + } >> + } >> + } >> + >> + /* Update partition information */ >> + if (FF_MULTI_PARTITION && ipart != 0) { /* Volume is in the >> existing partition */ >> + if (!FF_LBA64 || !(fsopt & 0x80)) { >> + /* Update system ID in the partition table */ >> + if (disk_read(pdrv, buf, 0, 1) != RES_OK) >> LEAVE_MKFS(FR_DISK_ERR); /* Read the MBR */ >> + buf[MBR_Table + (ipart - 1) * SZ_PTE + PTE_System] = >> sys; /* Set system ID */ >> + if (disk_write(pdrv, buf, 0, 1) != RES_OK) >> LEAVE_MKFS(FR_DISK_ERR); /* Write it back to the MBR */ >> + } >> + } else { /* Volume as a new single >> partition */ >> + if (!(fsopt & FM_SFD)) { /* Create partition table if not >> in SFD */ >> + lba[0] = sz_vol, lba[1] = 0; >> + fr = create_partition(pdrv, lba, sys, buf); >> + if (fr != FR_OK) LEAVE_MKFS(fr); >> + } >> + } >> + >> + if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) >> LEAVE_MKFS(FR_DISK_ERR); >> + >> + LEAVE_MKFS(FR_OK); >> +} >> + >> + >> + >> + >> +#if FF_MULTI_PARTITION >> +/*-----------------------------------------------------------------------*/ >> >> +/* Create Partition Table on the Physical >> Drive */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_fdisk ( >> + BYTE pdrv, /* Physical drive number */ >> + const LBA_t ptbl[], /* Pointer to the size table for each >> partitions */ >> + void* work /* Pointer to the working buffer (null: use >> heap memory) */ >> +) >> +{ >> + BYTE *buf = (BYTE*)work; >> + DSTATUS stat; >> + >> + >> + stat = disk_initialize(pdrv); >> + if (stat & STA_NOINIT) return FR_NOT_READY; >> + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; >> +#if FF_USE_LFN == 3 >> + if (!buf) buf = ff_memalloc(FF_MAX_SS); /* Use heap memory for >> working buffer */ >> +#endif >> + if (!buf) return FR_NOT_ENOUGH_CORE; >> + >> + LEAVE_MKFS(create_partition(pdrv, ptbl, 0x07, buf)); >> +} >> + >> +#endif /* FF_MULTI_PARTITION */ >> +#endif /* !FF_FS_READONLY && FF_USE_MKFS */ >> + >> + >> + >> + >> +#if FF_USE_STRFUNC >> +#if FF_USE_LFN && FF_LFN_UNICODE && (FF_STRF_ENCODE < 0 || >> FF_STRF_ENCODE > 3) >> +#error Wrong FF_STRF_ENCODE setting >> +#endif >> +/*-----------------------------------------------------------------------*/ >> >> +/* Get a String from the >> File */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +TCHAR* f_gets ( >> + TCHAR* buff, /* Pointer to the buffer to store read string */ >> + int len, /* Size of string buffer (items) */ >> + FIL* fp /* Pointer to the file object */ >> +) >> +{ >> + int nc = 0; >> + TCHAR *p = buff; >> + BYTE s[4]; >> + UINT rc; >> + DWORD dc; >> +#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE <= 2 >> + WCHAR wc; >> +#endif >> +#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE == 3 >> + UINT ct; >> +#endif >> + >> +#if FF_USE_LFN && FF_LFN_UNICODE /* With code conversion >> (Unicode API) */ >> + /* Make a room for the character and terminator */ >> + if (FF_LFN_UNICODE == 1) len -= (FF_STRF_ENCODE == 0) ? 1 : 2; >> + if (FF_LFN_UNICODE == 2) len -= (FF_STRF_ENCODE == 0) ? 3 : 4; >> + if (FF_LFN_UNICODE == 3) len -= 1; >> + while (nc < len) { >> +#if FF_STRF_ENCODE == 0 /* Read a character in >> ANSI/OEM */ >> + f_read(fp, s, 1, &rc); /* Get a code unit */ >> + if (rc != 1) break; /* EOF? */ >> + wc = s[0]; >> + if (dbc_1st((BYTE)wc)) { /* DBC 1st byte? */ >> + f_read(fp, s, 1, &rc); /* Get DBC 2nd byte */ >> + if (rc != 1 || !dbc_2nd(s[0])) continue; /* Wrong >> code? */ >> + wc = wc << 8 | s[0]; >> + } >> + dc = ff_oem2uni(wc, CODEPAGE); /* OEM --> */ >> + if (dc == 0) continue; >> +#elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2 /* Read a >> character in UTF-16LE/BE */ >> + f_read(fp, s, 2, &rc); /* Get a code unit */ >> + if (rc != 2) break; /* EOF? */ >> + dc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1]; >> + if (IsSurrogateL(dc)) continue; /* Broken surrogate pair? */ >> + if (IsSurrogateH(dc)) { /* High surrogate? */ >> + f_read(fp, s, 2, &rc); /* Get low surrogate */ >> + if (rc != 2) break; /* EOF? */ >> + wc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1]; >> + if (!IsSurrogateL(wc)) continue; /* Broken surrogate >> pair? */ >> + dc = ((dc & 0x3FF) + 0x40) << 10 | (wc & 0x3FF); /* >> Merge surrogate pair */ >> + } >> +#else /* Read a character in UTF-8 */ >> + f_read(fp, s, 1, &rc); /* Get a code unit */ >> + if (rc != 1) break; /* EOF? */ >> + dc = s[0]; >> + if (dc >= 0x80) { /* Multi-byte sequence? */ >> + ct = 0; >> + if ((dc & 0xE0) == 0xC0) { dc &= 0x1F; ct = 1; } /* >> 2-byte sequence? */ >> + if ((dc & 0xF0) == 0xE0) { dc &= 0x0F; ct = 2; } /* >> 3-byte sequence? */ >> + if ((dc & 0xF8) == 0xF0) { dc &= 0x07; ct = 3; } /* >> 4-byte sequence? */ >> + if (ct == 0) continue; >> + f_read(fp, s, ct, &rc); /* Get trailing bytes */ >> + if (rc != ct) break; >> + rc = 0; >> + do { /* Merge the byte sequence */ >> + if ((s[rc] & 0xC0) != 0x80) break; >> + dc = dc << 6 | (s[rc] & 0x3F); >> + } while (++rc < ct); >> + if (rc != ct || dc < 0x80 || IsSurrogate(dc) || dc >= >> 0x110000) continue; /* Wrong encoding? */ >> + } >> +#endif >> + /* A code point is avaialble in dc to be output */ >> + >> + if (FF_USE_STRFUNC == 2 && dc == '\r') continue; /* Strip >> \r off if needed */ >> +#if FF_LFN_UNICODE == 1 || FF_LFN_UNICODE == 3 /* Output it in >> UTF-16/32 encoding */ >> + if (FF_LFN_UNICODE == 1 && dc >= 0x10000) { /* Out of BMP >> at UTF-16? */ >> + *p++ = (TCHAR)(0xD800 | ((dc >> 10) - 0x40)); nc++; /* >> Make and output high surrogate */ >> + dc = 0xDC00 | (dc & 0x3FF); /* Make low surrogate */ >> + } >> + *p++ = (TCHAR)dc; nc++; >> + if (dc == '\n') break; /* End of line? */ >> +#elif FF_LFN_UNICODE == 2 /* Output it in UTF-8 encoding */ >> + if (dc < 0x80) { /* Single byte? */ >> + *p++ = (TCHAR)dc; >> + nc++; >> + if (dc == '\n') break; /* End of line? */ >> + } else { >> + if (dc < 0x800) { /* 2-byte sequence? */ >> + *p++ = (TCHAR)(0xC0 | (dc >> 6 & 0x1F)); >> + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); >> + nc += 2; >> + } else { >> + if (dc < 0x10000) { /* 3-byte sequence? */ >> + *p++ = (TCHAR)(0xE0 | (dc >> 12 & 0x0F)); >> + *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F)); >> + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); >> + nc += 3; >> + } else { /* 4-byte sequence? */ >> + *p++ = (TCHAR)(0xF0 | (dc >> 18 & 0x07)); >> + *p++ = (TCHAR)(0x80 | (dc >> 12 & 0x3F)); >> + *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F)); >> + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); >> + nc += 4; >> + } >> + } >> + } >> +#endif >> + } >> + >> +#else /* Byte-by-byte read without any conversion >> (ANSI/OEM API) */ >> + len -= 1; /* Make a room for the terminator */ >> + while (nc < len) { >> + f_read(fp, s, 1, &rc); /* Get a byte */ >> + if (rc != 1) break; /* EOF? */ >> + dc = s[0]; >> + if (FF_USE_STRFUNC == 2 && dc == '\r') continue; >> + *p++ = (TCHAR)dc; nc++; >> + if (dc == '\n') break; >> + } >> +#endif >> + >> + *p = 0; /* Terminate the string */ >> + return nc ? buff : 0; /* When no data read due to EOF or >> error, return with error. */ >> +} >> + >> + >> + >> + >> +#if !FF_FS_READONLY >> +#include <stdarg.h> >> +/*-----------------------------------------------------------------------*/ >> >> +/* Put a Character to the File >> (sub-functions) */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +/* Putchar output buffer and work area */ >> + >> +typedef struct { >> + FIL *fp; /* Ptr to the writing file */ >> + int idx, nchr; /* Write index of buf[] (-1:error), number of >> encoding units written */ >> +#if FF_USE_LFN && FF_LFN_UNICODE == 1 >> + WCHAR hs; >> +#elif FF_USE_LFN && FF_LFN_UNICODE == 2 >> + BYTE bs[4]; >> + UINT wi, ct; >> +#endif >> + BYTE buf[64]; /* Write buffer */ >> +} putbuff; >> + >> + >> +/* Buffered write with code conversion */ >> + >> +static void putc_bfd (putbuff* pb, TCHAR c) >> +{ >> + UINT n; >> + int i, nc; >> +#if FF_USE_LFN && FF_LFN_UNICODE >> + WCHAR hs, wc; >> +#if FF_LFN_UNICODE == 2 >> + DWORD dc; >> + const TCHAR *tp; >> +#endif >> +#endif >> + >> + if (FF_USE_STRFUNC == 2 && c == '\n') { /* LF -> CRLF >> conversion */ >> + putc_bfd(pb, '\r'); >> + } >> + >> + i = pb->idx; /* Write index of pb->buf[] */ >> + if (i < 0) return; >> + nc = pb->nchr; /* Write unit counter */ >> + >> +#if FF_USE_LFN && FF_LFN_UNICODE >> +#if FF_LFN_UNICODE == 1 /* UTF-16 input */ >> + if (IsSurrogateH(c)) { /* High surrogate? */ >> + pb->hs = c; return; /* Save it for next */ >> + } >> + hs = pb->hs; pb->hs = 0; >> + if (hs != 0) { /* There is a leading high surrogate */ >> + if (!IsSurrogateL(c)) hs = 0; /* Discard high surrogate if >> not a surrogate pair */ >> + } else { >> + if (IsSurrogateL(c)) return; /* Discard stray low >> surrogate */ >> + } >> + wc = c; >> +#elif FF_LFN_UNICODE == 2 /* UTF-8 input */ >> + for (;;) { >> + if (pb->ct == 0) { /* Out of multi-byte sequence? */ >> + pb->bs[pb->wi = 0] = (BYTE)c; /* Save 1st byte */ >> + if ((BYTE)c < 0x80) break; /* Single >> byte? */ >> + if (((BYTE)c & 0xE0) == 0xC0) pb->ct = 1; /* 2-byte >> sequence? */ >> + if (((BYTE)c & 0xF0) == 0xE0) pb->ct = 2; /* 3-byte >> sequence? */ >> + if (((BYTE)c & 0xF1) == 0xF0) pb->ct = 3; /* 4-byte >> sequence? */ >> + return; >> + } else { /* In the multi-byte sequence */ >> + if (((BYTE)c & 0xC0) != 0x80) { /* Broken sequence? */ >> + pb->ct = 0; continue; >> + } >> + pb->bs[++pb->wi] = (BYTE)c; /* Save the trailing byte */ >> + if (--pb->ct == 0) break; /* End of multi-byte >> sequence? */ >> + return; >> + } >> + } >> + tp = (const TCHAR*)pb->bs; >> + dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */ >> + if (dc == 0xFFFFFFFF) return; /* Wrong code? */ >> + wc = (WCHAR)dc; >> + hs = (WCHAR)(dc >> 16); >> +#elif FF_LFN_UNICODE == 3 /* UTF-32 input */ >> + if (IsSurrogate(c) || c >= 0x110000) return; /* Discard >> invalid code */ >> + if (c >= 0x10000) { /* Out of BMP? */ >> + hs = (WCHAR)(0xD800 | ((c >> 10) - 0x40)); /* Make high >> surrogate */ >> + wc = 0xDC00 | (c & 0x3FF); /* Make low >> surrogate */ >> + } else { >> + hs = 0; >> + wc = (WCHAR)c; >> + } >> +#endif >> + /* A code point in UTF-16 is available in hs and wc */ >> + >> +#if FF_STRF_ENCODE == 1 /* Write a code point in UTF-16LE */ >> + if (hs != 0) { /* Surrogate pair? */ >> + st_word(&pb->buf[i], hs); >> + i += 2; >> + nc++; >> + } >> + st_word(&pb->buf[i], wc); >> + i += 2; >> +#elif FF_STRF_ENCODE == 2 /* Write a code point in UTF-16BE */ >> + if (hs != 0) { /* Surrogate pair? */ >> + pb->buf[i++] = (BYTE)(hs >> 8); >> + pb->buf[i++] = (BYTE)hs; >> + nc++; >> + } >> + pb->buf[i++] = (BYTE)(wc >> 8); >> + pb->buf[i++] = (BYTE)wc; >> +#elif FF_STRF_ENCODE == 3 /* Write a code point in UTF-8 */ >> + if (hs != 0) { /* 4-byte sequence? */ >> + nc += 3; >> + hs = (hs & 0x3FF) + 0x40; >> + pb->buf[i++] = (BYTE)(0xF0 | hs >> 8); >> + pb->buf[i++] = (BYTE)(0x80 | (hs >> 2 & 0x3F)); >> + pb->buf[i++] = (BYTE)(0x80 | (hs & 3) << 4 | (wc >> 6 & 0x0F)); >> + pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F)); >> + } else { >> + if (wc < 0x80) { /* Single byte? */ >> + pb->buf[i++] = (BYTE)wc; >> + } else { >> + if (wc < 0x800) { /* 2-byte sequence? */ >> + nc += 1; >> + pb->buf[i++] = (BYTE)(0xC0 | wc >> 6); >> + } else { /* 3-byte sequence */ >> + nc += 2; >> + pb->buf[i++] = (BYTE)(0xE0 | wc >> 12); >> + pb->buf[i++] = (BYTE)(0x80 | (wc >> 6 & 0x3F)); >> + } >> + pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F)); >> + } >> + } >> +#else /* Write a code point in ANSI/OEM */ >> + if (hs != 0) return; >> + wc = ff_uni2oem(wc, CODEPAGE); /* UTF-16 ==> ANSI/OEM */ >> + if (wc == 0) return; >> + if (wc >= 0x100) { >> + pb->buf[i++] = (BYTE)(wc >> 8); nc++; >> + } >> + pb->buf[i++] = (BYTE)wc; >> +#endif >> + >> +#else /* ANSI/OEM input (without >> re-encoding) */ >> + pb->buf[i++] = (BYTE)c; >> +#endif >> + >> + if (i >= (int)(sizeof pb->buf) - 4) { /* Write buffered >> characters to the file */ >> + f_write(pb->fp, pb->buf, (UINT)i, &n); >> + i = (n == (UINT)i) ? 0 : -1; >> + } >> + pb->idx = i; >> + pb->nchr = nc + 1; >> +} >> + >> + >> +/* Flush remaining characters in the buffer */ >> + >> +static int putc_flush (putbuff* pb) >> +{ >> + UINT nw; >> + >> + if ( pb->idx >= 0 /* Flush buffered characters to the file */ >> + && f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK >> + && (UINT)pb->idx == nw) return pb->nchr; >> + return -1; >> +} >> + >> + >> +/* Initialize write buffer */ >> + >> +static void putc_init (putbuff* pb, FIL* fp) >> +{ >> + mem_set(pb, 0, sizeof (putbuff)); >> + pb->fp = fp; >> +} >> + >> + >> + >> +int f_putc ( >> + TCHAR c, /* A character to be output */ >> + FIL* fp /* Pointer to the file object */ >> +) >> +{ >> + putbuff pb; >> + >> + >> + putc_init(&pb, fp); >> + putc_bfd(&pb, c); /* Put the character */ >> + return putc_flush(&pb); >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Put a String to the >> File */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +int f_puts ( >> + const TCHAR* str, /* Pointer to the string to be output */ >> + FIL* fp /* Pointer to the file object */ >> +) >> +{ >> + putbuff pb; >> + >> + >> + putc_init(&pb, fp); >> + while (*str) putc_bfd(&pb, *str++); /* Put the string */ >> + return putc_flush(&pb); >> +} >> + >> + >> + >> + >> +/*-----------------------------------------------------------------------*/ >> >> +/* Put a Formatted String to the >> File */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +int f_printf ( >> + FIL* fp, /* Pointer to the file object */ >> + const TCHAR* fmt, /* Pointer to the format string */ >> + ... /* Optional arguments... */ >> +) >> +{ >> + va_list arp; >> + putbuff pb; >> + BYTE f, r; >> + UINT i, j, w; >> + DWORD v; >> + TCHAR c, d, str[32], *p; >> + >> + >> + putc_init(&pb, fp); >> + >> + va_start(arp, fmt); >> + >> + for (;;) { >> + c = *fmt++; >> + if (c == 0) break; /* End of string */ >> + if (c != '%') { /* Non escape character */ >> + putc_bfd(&pb, c); >> + continue; >> + } >> + w = f = 0; >> + c = *fmt++; >> + if (c == '0') { /* Flag: '0' padding */ >> + f = 1; c = *fmt++; >> + } else { >> + if (c == '-') { /* Flag: left justified */ >> + f = 2; c = *fmt++; >> + } >> + } >> + if (c == '*') { /* Minimum width by argument */ >> + w = va_arg(arp, int); >> + c = *fmt++; >> + } else { >> + while (IsDigit(c)) { /* Minimum width */ >> + w = w * 10 + c - '0'; >> + c = *fmt++; >> + } >> + } >> + if (c == 'l' || c == 'L') { /* Type prefix: Size is long >> int */ >> + f |= 4; c = *fmt++; >> + } >> + if (c == 0) break; >> + d = c; >> + if (IsLower(d)) d -= 0x20; >> + switch (d) { /* Atgument type is... */ >> + case 'S': /* String */ >> + p = va_arg(arp, TCHAR*); >> + for (j = 0; p[j]; j++) ; >> + if (!(f & 2)) { /* Right padded */ >> + while (j++ < w) putc_bfd(&pb, ' ') ; >> + } >> + while (*p) putc_bfd(&pb, *p++) ; /* String body */ >> + while (j++ < w) putc_bfd(&pb, ' ') ; /* Left padded */ >> + continue; >> + >> + case 'C': /* Character */ >> + putc_bfd(&pb, (TCHAR)va_arg(arp, int)); >> + continue; >> + >> + case 'B': /* Unsigned binary */ >> + r = 2; >> + break; >> + >> + case 'O': /* Unsigned octal */ >> + r = 8; >> + break; >> + >> + case 'D': /* Signed decimal */ >> + case 'U': /* Unsigned decimal */ >> + r = 10; >> + break; >> + >> + case 'X': /* Unsigned hexdecimal */ >> + r = 16; >> + break; >> + >> + default: /* Unknown type (pass-through) */ >> + putc_bfd(&pb, c); continue; >> + } >> + >> + /* Get an argument and put it in numeral */ >> + v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? >> (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int)); >> + if (d == 'D' && (v & 0x80000000)) { >> + v = 0 - v; >> + f |= 8; >> + } >> + i = 0; >> + do { >> + d = (TCHAR)(v % r); v /= r; >> + if (d > 9) d += (c == 'x') ? 0x27 : 0x07; >> + str[i++] = d + '0'; >> + } while (v && i < sizeof str / sizeof *str); >> + if (f & 8) str[i++] = '-'; >> + j = i; d = (f & 1) ? '0' : ' '; >> + if (!(f & 2)) { >> + while (j++ < w) putc_bfd(&pb, d); /* Right pad */ >> + } >> + do { >> + putc_bfd(&pb, str[--i]); /* Number body */ >> + } while (i); >> + while (j++ < w) putc_bfd(&pb, d); /* Left pad */ >> + } >> + >> + va_end(arp); >> + >> + return putc_flush(&pb); >> +} >> + >> +#endif /* !FF_FS_READONLY */ >> +#endif /* FF_USE_STRFUNC */ >> + >> + >> + >> +#if FF_CODE_PAGE == 0 >> +/*-----------------------------------------------------------------------*/ >> >> +/* Set Active Codepage for the Path >> Name */ >> +/*-----------------------------------------------------------------------*/ >> >> + >> +FRESULT f_setcp ( >> + WORD cp /* Value to be set as active code page */ >> +) >> +{ >> + static const WORD validcp[] = { 437, 720, 737, >> 771, 775, 850, 852, 857, 860, 861, 862, 863, 864, >> 865, 866, 869, 932, 936, 949, 950, 0}; >> + static const BYTE* const tables[] = {Ct437, Ct720, Ct737, Ct771, >> Ct775, Ct850, Ct852, Ct857, Ct860, Ct861, Ct862, Ct863, Ct864, Ct865, >> Ct866, Ct869, Dc932, Dc936, Dc949, Dc950, 0}; >> + UINT i; >> + >> + >> + for (i = 0; validcp[i] != 0 && validcp[i] != cp; i++) ; /* >> Find the code page */ >> + if (validcp[i] != cp) return FR_INVALID_PARAMETER; /* Not >> found? */ >> + >> + CodePage = cp; >> + if (cp >= 900) { /* DBCS */ >> + ExCvt = 0; >> + DbcTbl = tables[i]; >> + } else { /* SBCS */ >> + ExCvt = tables[i]; >> + DbcTbl = 0; >> + } >> + return FR_OK; >> +} >> +#endif /* FF_CODE_PAGE == 0 */ >> diff --git a/fatfs/ff.h b/fatfs/ff.h >> new file mode 100644 >> index 0000000..ac41fa4 >> --- /dev/null >> +++ b/fatfs/ff.h >> @@ -0,0 +1,425 @@ >> +/*----------------------------------------------------------------------------/ >> >> +/ FatFs - Generic FAT Filesystem module >> R0.14a / >> +/-----------------------------------------------------------------------------/ >> >> +/ >> +/ Copyright (C) 2020, ChaN, all right reserved. >> +/ >> +/ FatFs module is an open source software. Redistribution and use of >> FatFs in >> +/ source and binary forms, with or without modification, are >> permitted provided >> +/ that the following condition is met: >> +/ >> +/ 1. Redistributions of source code must retain the above copyright >> notice, >> +/ this condition and the following disclaimer. >> +/ >> +/ This software is provided by the copyright holder and contributors >> "AS IS" >> +/ and any warranties related to this software are DISCLAIMED. >> +/ The copyright owner or contributors be NOT LIABLE for any damages >> caused >> +/ by use of this software. >> +/ >> +/ SPDX-License-Identifier: BSD-1-Clause >> +/ >> +/----------------------------------------------------------------------------*/ >> >> + >> + >> +#ifndef FF_DEFINED >> +#define FF_DEFINED 80196 /* Revision ID */ >> + >> +#ifdef __cplusplus >> +extern "C" { >> +#endif >> + >> +#include "ffconf.h" /* FatFs configuration options */ >> + >> +#if FF_DEFINED != FFCONF_DEF >> +#error Wrong configuration file (ffconf.h). >> +#endif >> + >> + >> +/* Integer types used for FatFs API */ >> + >> +#if defined(_WIN32) /* Main development platform */ >> +#define FF_INTDEF 2 >> +#include <windows.h> >> +typedef unsigned __int64 QWORD; >> +#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || >> defined(__cplusplus) /* C99 or later */ >> +#define FF_INTDEF 2 >> +#include <stdint.h> >> +typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ >> +typedef unsigned char BYTE; /* char must be 8-bit */ >> +typedef uint16_t WORD; /* 16-bit unsigned integer */ >> +typedef uint32_t DWORD; /* 32-bit unsigned integer */ >> +typedef uint64_t QWORD; /* 64-bit unsigned integer */ >> +typedef WORD WCHAR; /* UTF-16 character type */ >> +#else /* Earlier than C99 */ >> +#define FF_INTDEF 1 >> +typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ >> +typedef unsigned char BYTE; /* char must be 8-bit */ >> +typedef unsigned short WORD; /* 16-bit unsigned integer */ >> +typedef unsigned long DWORD; /* 32-bit unsigned integer */ >> +typedef WORD WCHAR; /* UTF-16 character type */ >> +#endif >> + >> + >> +/* Definitions of volume management */ >> + >> +#if FF_MULTI_PARTITION /* Multiple partition configuration */ >> +typedef struct { >> + BYTE pd; /* Physical drive number */ >> + BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ >> +} PARTITION; >> +extern PARTITION VolToPart[]; /* Volume - Partition mapping table */ >> +#endif >> + >> +#if FF_STR_VOLUME_ID >> +#ifndef FF_VOLUME_STRS >> +extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */ >> +#endif >> +#endif >> + >> + >> + >> +/* Type of path name strings on FatFs API */ >> + >> +#ifndef _INC_TCHAR >> +#define _INC_TCHAR >> + >> +#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 >> encoding */ >> +typedef WCHAR TCHAR; >> +#define _T(x) L ## x >> +#define _TEXT(x) L ## x >> +#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 >> encoding */ >> +typedef char TCHAR; >> +#define _T(x) u8 ## x >> +#define _TEXT(x) u8 ## x >> +#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 >> encoding */ >> +typedef DWORD TCHAR; >> +#define _T(x) U ## x >> +#define _TEXT(x) U ## x >> +#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3) >> +#error Wrong FF_LFN_UNICODE setting >> +#else /* ANSI/OEM code in >> SBCS/DBCS */ >> +typedef char TCHAR; >> +#define _T(x) x >> +#define _TEXT(x) x >> +#endif >> + >> +#endif >> + >> + >> + >> +/* Type of file size and LBA variables */ >> + >> +#if FF_FS_EXFAT >> +#if FF_INTDEF != 2 >> +#error exFAT feature wants C99 or later >> +#endif >> +typedef QWORD FSIZE_t; >> +#if FF_LBA64 >> +typedef QWORD LBA_t; >> +#else >> +typedef DWORD LBA_t; >> +#endif >> +#else >> +#if FF_LBA64 >> +#error exFAT needs to be enabled when enable 64-bit LBA >> +#endif >> +typedef DWORD FSIZE_t; >> +typedef DWORD LBA_t; >> +#endif >> + >> + >> + >> +/* Filesystem object structure (FATFS) */ >> + >> +typedef struct { >> + BYTE fs_type; /* Filesystem type (0:not mounted) */ >> + BYTE pdrv; /* Associated physical drive */ >> + BYTE n_fats; /* Number of FATs (1 or 2) */ >> + BYTE wflag; /* win[] flag (b0:dirty) */ >> + BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ >> + WORD id; /* Volume mount ID */ >> + WORD n_rootdir; /* Number of root directory entries >> (FAT12/16) */ >> + WORD csize; /* Cluster size [sectors] */ >> +#if FF_MAX_SS != FF_MIN_SS >> + WORD ssize; /* Sector size (512, 1024, 2048 or >> 4096) */ >> +#endif >> +#if FF_USE_LFN >> + WCHAR* lfnbuf; /* LFN working buffer */ >> +#endif >> +#if FF_FS_EXFAT >> + BYTE* dirbuf; /* Directory entry block scratchpad >> buffer for exFAT */ >> +#endif >> +#if FF_FS_REENTRANT >> + FF_SYNC_t sobj; /* Identifier of sync object */ >> +#endif >> +#if !FF_FS_READONLY >> + DWORD last_clst; /* Last allocated cluster */ >> + DWORD free_clst; /* Number of free clusters */ >> +#endif >> +#if FF_FS_RPATH >> + DWORD cdir; /* Current directory start cluster >> (0:root) */ >> +#if FF_FS_EXFAT >> + DWORD cdc_scl; /* Containing directory start cluster >> (invalid when cdir is 0) */ >> + DWORD cdc_size; /* b31-b8:Size of containing directory, >> b7-b0: Chain status */ >> + DWORD cdc_ofs; /* Offset in the containing directory >> (invalid when cdir is 0) */ >> +#endif >> +#endif >> + DWORD n_fatent; /* Number of FAT entries (number of >> clusters + 2) */ >> + DWORD fsize; /* Size of an FAT [sectors] */ >> + LBA_t volbase; /* Volume base sector */ >> + LBA_t fatbase; /* FAT base sector */ >> + LBA_t dirbase; /* Root directory base sector/cluster */ >> + LBA_t database; /* Data base sector */ >> +#if FF_FS_EXFAT >> + LBA_t bitbase; /* Allocation bitmap base sector */ >> +#endif >> + LBA_t winsect; /* Current sector appearing in the win[] */ >> + BYTE win[FF_MAX_SS]; /* Disk access window for Directory, >> FAT (and file data at tiny cfg) */ >> +} FATFS; >> + >> + >> + >> +/* Object ID and allocation information (FFOBJID) */ >> + >> +typedef struct { >> + FATFS* fs; /* Pointer to the hosting volume of >> this object */ >> + WORD id; /* Hosting volume mount ID */ >> + BYTE attr; /* Object attribute */ >> + BYTE stat; /* Object chain status (b1-0: =0:not >> contiguous, =2:contiguous, =3:fragmented in this session, >> b2:sub-directory stretched) */ >> + DWORD sclust; /* Object data start cluster (0:no >> cluster or root directory) */ >> + FSIZE_t objsize; /* Object size (valid when sclust != >> 0) */ >> +#if FF_FS_EXFAT >> + DWORD n_cont; /* Size of first fragment - 1 (valid >> when stat == 3) */ >> + DWORD n_frag; /* Size of last fragment needs to be >> written to FAT (valid when not zero) */ >> + DWORD c_scl; /* Containing directory start cluster >> (valid when sclust != 0) */ >> + DWORD c_size; /* b31-b8:Size of containing >> directory, b7-b0: Chain status (valid when c_scl != 0) */ >> + DWORD c_ofs; /* Offset in the containing directory >> (valid when file object and sclust != 0) */ >> +#endif >> +#if FF_FS_LOCK >> + UINT lockid; /* File lock ID origin from 1 (index >> of file semaphore table Files[]) */ >> +#endif >> +} FFOBJID; >> + >> + >> + >> +/* File object structure (FIL) */ >> + >> +typedef struct { >> + FFOBJID obj; /* Object identifier (must be the 1st >> member to detect invalid object pointer) */ >> + BYTE flag; /* File status flags */ >> + BYTE err; /* Abort flag (error code) */ >> + FSIZE_t fptr; /* File read/write pointer (Zeroed on >> file open) */ >> + DWORD clust; /* Current cluster of fpter (invalid >> when fptr is 0) */ >> + LBA_t sect; /* Sector number appearing in buf[] >> (0:invalid) */ >> +#if !FF_FS_READONLY >> + LBA_t dir_sect; /* Sector number containing the >> directory entry (not used at exFAT) */ >> + BYTE* dir_ptr; /* Pointer to the directory entry in the >> win[] (not used at exFAT) */ >> +#endif >> +#if FF_USE_FASTSEEK >> + DWORD* cltbl; /* Pointer to the cluster link map >> table (nulled on open, set by application) */ >> +#endif >> +#if !FF_FS_TINY >> + BYTE buf[FF_MAX_SS]; /* File private data read/write window */ >> +#endif >> +} FIL; >> + >> + >> + >> +/* Directory object structure (DIR) */ >> + >> +typedef struct { >> + FFOBJID obj; /* Object identifier */ >> + DWORD dptr; /* Current read/write offset */ >> + DWORD clust; /* Current cluster */ >> + LBA_t sect; /* Current sector (0:Read operation has >> terminated) */ >> + BYTE* dir; /* Pointer to the directory item in the >> win[] */ >> + BYTE fn[12]; /* SFN (in/out) >> {body[8],ext[3],status[1]} */ >> +#if FF_USE_LFN >> + DWORD blk_ofs; /* Offset of current entry block being >> processed (0xFFFFFFFF:Invalid) */ >> +#endif >> +#if FF_USE_FIND >> + const TCHAR* pat; /* Pointer to the name matching pattern */ >> +#endif >> +} DIR; >> + >> + >> + >> +/* File information structure (FILINFO) */ >> + >> +typedef struct { >> + FSIZE_t fsize; /* File size */ >> + WORD fdate; /* Modified date */ >> + WORD ftime; /* Modified time */ >> + BYTE fattrib; /* File attribute */ >> +#if FF_USE_LFN >> + TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */ >> + TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */ >> +#else >> + TCHAR fname[12 + 1]; /* File name */ >> +#endif >> +} FILINFO; >> + >> + >> + >> +/* Format parameter structure (MKFS_PARM) */ >> + >> +typedef struct { >> + BYTE fmt; /* Format option (FM_FAT, FM_FAT32, FM_EXFAT >> and FM_SFD) */ >> + BYTE n_fat; /* Number of FATs */ >> + UINT align; /* Data area alignment (sector) */ >> + UINT n_root; /* Number of root directory entries */ >> + DWORD au_size; /* Cluster size (byte) */ >> +} MKFS_PARM; >> + >> + >> + >> +/* File function return code (FRESULT) */ >> + >> +typedef enum { >> + FR_OK = 0, /* (0) Succeeded */ >> + FR_DISK_ERR, /* (1) A hard error occurred in the low >> level disk I/O layer */ >> + FR_INT_ERR, /* (2) Assertion failed */ >> + FR_NOT_READY, /* (3) The physical drive cannot work */ >> + FR_NO_FILE, /* (4) Could not find the file */ >> + FR_NO_PATH, /* (5) Could not find the path */ >> + FR_INVALID_NAME, /* (6) The path name format is invalid */ >> + FR_DENIED, /* (7) Access denied due to prohibited >> access or directory full */ >> + FR_EXIST, /* (8) Access denied due to prohibited >> access */ >> + FR_INVALID_OBJECT, /* (9) The file/directory object is >> invalid */ >> + FR_WRITE_PROTECTED, /* (10) The physical drive is write >> protected */ >> + FR_INVALID_DRIVE, /* (11) The logical drive number is >> invalid */ >> + FR_NOT_ENABLED, /* (12) The volume has no work area */ >> + FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ >> + FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any >> problem */ >> + FR_TIMEOUT, /* (15) Could not get a grant to >> access the volume within defined period */ >> + FR_LOCKED, /* (16) The operation is rejected >> according to the file sharing policy */ >> + FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not >> be allocated */ >> + FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > >> FF_FS_LOCK */ >> + FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ >> +} FRESULT; >> + >> + >> + >> +/*--------------------------------------------------------------*/ >> +/* FatFs module application interface */ >> + >> +FRESULT f_open (FIL* fp, const TCHAR* path, BYTE >> mode); /* Open or create a file */ >> +FRESULT f_close (FIL* fp); >> /* Close an open file object */ >> +FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); >> /* Read data from the file */ >> +FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); >> /* Write data to the file */ >> +FRESULT f_lseek (FIL* fp, FSIZE_t >> ofs); /* Move file pointer of the file >> object */ >> +FRESULT f_truncate (FIL* fp); >> /* Truncate the file */ >> +FRESULT f_sync (FIL* fp); >> /* Flush cached data of the writing file */ >> +FRESULT f_opendir (DIR* dp, const TCHAR* >> path); /* Open a directory */ >> +FRESULT f_closedir (DIR* dp); >> /* Close an open directory */ >> +FRESULT f_readdir (DIR* dp, FILINFO* fno); >> /* Read a directory item */ >> +FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const >> TCHAR* pattern); /* Find first file */ >> +FRESULT f_findnext (DIR* dp, FILINFO* >> fno); /* Find next file */ >> +FRESULT f_mkdir (const TCHAR* path); >> /* Create a sub directory */ >> +FRESULT f_unlink (const TCHAR* path); >> /* Delete an existing file or directory */ >> +FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); >> /* Rename/Move a file or directory */ >> +FRESULT f_stat (const TCHAR* path, FILINFO* fno); >> /* Get file status */ >> +FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); >> /* Change attribute of a file/dir */ >> +FRESULT f_utime (const TCHAR* path, const FILINFO* fno); >> /* Change timestamp of a file/dir */ >> +FRESULT f_chdir (const TCHAR* path); >> /* Change current directory */ >> +FRESULT f_chdrive (const TCHAR* path); >> /* Change current drive */ >> +FRESULT f_getcwd (TCHAR* buff, UINT len); >> /* Get current directory */ >> +FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** >> fatfs); /* Get number of free clusters on the drive */ >> +FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); >> /* Get volume label */ >> +FRESULT f_setlabel (const TCHAR* label); >> /* Set volume label */ >> +FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, >> UINT* bf); /* Forward data to the stream */ >> +FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); >> /* Allocate a contiguous block to the file */ >> +FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); >> /* Mount/Unmount a logical drive */ >> +FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, >> UINT len); /* Create a FAT volume */ >> +FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); >> /* Divide a physical drive into some partitions */ >> +FRESULT f_setcp (WORD cp); >> /* Set current code page */ >> +int f_putc (TCHAR c, FIL* fp); >> /* Put a character to the file */ >> +int f_puts (const TCHAR* str, FIL* >> cp); /* Put a string to the file */ >> +int f_printf (FIL* fp, const TCHAR* str, ...); >> /* Put a formatted string to the file */ >> +TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); >> /* Get a string from the file */ >> + >> +#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) >> +#define f_error(fp) ((fp)->err) >> +#define f_tell(fp) ((fp)->fptr) >> +#define f_size(fp) ((fp)->obj.objsize) >> +#define f_rewind(fp) f_lseek((fp), 0) >> +#define f_rewinddir(dp) f_readdir((dp), 0) >> +#define f_rmdir(path) f_unlink(path) >> +#define f_unmount(path) f_mount(0, path, 0) >> + >> + >> + >> + >> +/*--------------------------------------------------------------*/ >> +/* Additional user defined functions */ >> + >> +/* RTC function */ >> +#if !FF_FS_READONLY && !FF_FS_NORTC >> +DWORD get_fattime (void); >> +#endif >> + >> +/* LFN support functions */ >> +#if FF_USE_LFN >= 1 /* Code conversion >> (defined in unicode.c) */ >> +WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode >> conversion */ >> +WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code >> conversion */ >> +DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case >> conversion */ >> +#endif >> +#if FF_USE_LFN == 3 /* Dynamic memory >> allocation */ >> +void* ff_memalloc (UINT msize); /* Allocate memory block */ >> +void ff_memfree (void* mblock); /* Free memory block */ >> +#endif >> + >> +/* Sync functions */ >> +#if FF_FS_REENTRANT >> +int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync >> object */ >> +int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */ >> +void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */ >> +int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */ >> +#endif >> + >> +/* Function for assigning UNIX devices to FatFs library. */ >> +extern int fatfs_init(char *device); >> +extern void fatfs_release(void); >> + >> +/*--------------------------------------------------------------*/ >> +/* Flags and offset address */ >> + >> + >> +/* File access mode and open method flags (3rd argument of f_open) */ >> +#define FA_READ 0x01 >> +#define FA_WRITE 0x02 >> +#define FA_OPEN_EXISTING 0x00 >> +#define FA_CREATE_NEW 0x04 >> +#define FA_CREATE_ALWAYS 0x08 >> +#define FA_OPEN_ALWAYS 0x10 >> +#define FA_OPEN_APPEND 0x30 >> + >> +/* Fast seek controls (2nd argument of f_lseek) */ >> +#define CREATE_LINKMAP ((FSIZE_t)0 - 1) >> + >> +/* Format options (2nd argument of f_mkfs) */ >> +#define FM_FAT 0x01 >> +#define FM_FAT32 0x02 >> +#define FM_EXFAT 0x04 >> +#define FM_ANY 0x07 >> +#define FM_SFD 0x08 >> + >> +/* Filesystem type (FATFS.fs_type) */ >> +#define FS_FAT12 1 >> +#define FS_FAT16 2 >> +#define FS_FAT32 3 >> +#define FS_EXFAT 4 >> + >> +/* File attribute bits for directory entry (FILINFO.fattrib) */ >> +#define AM_RDO 0x01 /* Read only */ >> +#define AM_HID 0x02 /* Hidden */ >> +#define AM_SYS 0x04 /* System */ >> +#define AM_DIR 0x10 /* Directory */ >> +#define AM_ARC 0x20 /* Archive */ >> + >> + >> +#ifdef __cplusplus >> +} >> +#endif >> + >> +#endif /* FF_DEFINED */ >> diff --git a/fatfs/ffconf.h b/fatfs/ffconf.h >> new file mode 100644 >> index 0000000..b85bbb0 >> --- /dev/null >> +++ b/fatfs/ffconf.h >> @@ -0,0 +1,317 @@ >> +/*---------------------------------------------------------------------------/ >> >> +/ FatFs Functional Configurations >> +/----------------------------------------------------------------------------/ >> >> +/ >> +/ Copyright (C) 2019, ChaN, all right reserved. >> +/ >> +/ FatFs module is an open source software. Redistribution and use of >> FatFs in >> +/ source and binary forms, with or without modification, are >> permitted provided >> +/ that the following condition is met: >> +/ >> +/ 1. Redistributions of source code must retain the above copyright >> notice, >> +/ this condition and the following disclaimer. >> +/ >> +/ This software is provided by the copyright holder and contributors >> "AS IS" >> +/ and any warranties related to this software are DISCLAIMED. >> +/ The copyright owner or contributors be NOT LIABLE for any damages >> caused >> +/ by use of this software. >> +/ >> +/ SPDX-License-Identifier: BSD-1-Clause >> +/ >> +/---------------------------------------------------------------------------*/ >> >> + >> + >> +#define FFCONF_DEF 80196 /* Revision ID */ >> + >> +/*---------------------------------------------------------------------------/ >> >> +/ Function Configurations >> +/---------------------------------------------------------------------------*/ >> >> + >> +#define FF_FS_READONLY 0 >> +/* This option switches read-only configuration. (0:Read/Write or >> 1:Read-only) >> +/ Read-only configuration removes writing API functions, f_write(), >> f_sync(), >> +/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), >> f_getfree() >> +/ and optional writing functions as well. */ >> + >> + >> +#define FF_FS_MINIMIZE 3 >> +/* This option defines minimization level to remove some basic API >> functions. >> +/ >> +/ 0: Basic functions are fully enabled. >> +/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and >> f_rename() >> +/ are removed. >> +/ 2: f_opendir(), f_readdir() and f_closedir() are removed in >> addition to 1. >> +/ 3: f_lseek() function is removed in addition to 2. */ >> + >> + >> +#define FF_USE_STRFUNC 0 >> +/* This option switches string functions, f_gets(), f_putc(), >> f_puts() and f_printf(). >> +/ >> +/ 0: Disable string functions. >> +/ 1: Enable without LF-CRLF conversion. >> +/ 2: Enable with LF-CRLF conversion. */ >> + >> + >> +#define FF_USE_FIND 0 >> +/* This option switches filtered directory read functions, >> f_findfirst() and >> +/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching >> altname[] too) */ >> + >> + >> +#define FF_USE_MKFS 1 >> +/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ >> + >> + >> +#define FF_USE_FASTSEEK 0 >> +/* This option switches fast seek function. (0:Disable or 1:Enable) */ >> + >> + >> +#define FF_USE_EXPAND 0 >> +/* This option switches f_expand function. (0:Disable or 1:Enable) */ >> + >> + >> +#define FF_USE_CHMOD 0 >> +/* This option switches attribute manipulation functions, f_chmod() >> and f_utime(). >> +/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to >> enable this option. */ >> + >> + >> +#define FF_USE_LABEL 0 >> +/* This option switches volume label functions, f_getlabel() and >> f_setlabel(). >> +/ (0:Disable or 1:Enable) */ >> + >> + >> +#define FF_USE_FORWARD 0 >> +/* This option switches f_forward() function. (0:Disable or 1:Enable) */ >> + >> + >> +/*---------------------------------------------------------------------------/ >> >> +/ Locale and Namespace Configurations >> +/---------------------------------------------------------------------------*/ >> >> + >> +#define FF_CODE_PAGE 932 >> +/* This option specifies the OEM code page to be used on the target >> system. >> +/ Incorrect code page setting can cause a file open failure. >> +/ >> +/ 437 - U.S. >> +/ 720 - Arabic >> +/ 737 - Greek >> +/ 771 - KBL >> +/ 775 - Baltic >> +/ 850 - Latin 1 >> +/ 852 - Latin 2 >> +/ 855 - Cyrillic >> +/ 857 - Turkish >> +/ 860 - Portuguese >> +/ 861 - Icelandic >> +/ 862 - Hebrew >> +/ 863 - Canadian French >> +/ 864 - Arabic >> +/ 865 - Nordic >> +/ 866 - Russian >> +/ 869 - Greek 2 >> +/ 932 - Japanese (DBCS) >> +/ 936 - Simplified Chinese (DBCS) >> +/ 949 - Korean (DBCS) >> +/ 950 - Traditional Chinese (DBCS) >> +/ 0 - Include all code pages above and configured by f_setcp() >> +*/ >> + >> + >> +#define FF_USE_LFN 0 >> +#define FF_MAX_LFN 255 >> +/* The FF_USE_LFN switches the support for LFN (long file name). >> +/ >> +/ 0: Disable LFN. FF_MAX_LFN has no effect. >> +/ 1: Enable LFN with static working buffer on the BSS. Always NOT >> thread-safe. >> +/ 2: Enable LFN with dynamic working buffer on the STACK. >> +/ 3: Enable LFN with dynamic working buffer on the HEAP. >> +/ >> +/ To enable the LFN, ffunicode.c needs to be added to the project. >> The LFN function >> +/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) >> * 2 bytes and >> +/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled. >> +/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code >> unit and it can >> +/ be in range of 12 to 255. It is recommended to be set it 255 to >> fully support LFN >> +/ specification. >> +/ When use stack for the working buffer, take care on stack >> overflow. When use heap >> +/ memory for the working buffer, memory management functions, >> ff_memalloc() and >> +/ ff_memfree() exemplified in ffsystem.c, need to be added to the >> project. */ >> + >> + >> +#define FF_LFN_UNICODE 0 >> +/* This option switches the character encoding on the API when LFN is >> enabled. >> +/ >> +/ 0: ANSI/OEM in current CP (TCHAR = char) >> +/ 1: Unicode in UTF-16 (TCHAR = WCHAR) >> +/ 2: Unicode in UTF-8 (TCHAR = char) >> +/ 3: Unicode in UTF-32 (TCHAR = DWORD) >> +/ >> +/ Also behavior of string I/O functions will be affected by this >> option. >> +/ When LFN is not enabled, this option has no effect. */ >> + >> + >> +#define FF_LFN_BUF 255 >> +#define FF_SFN_BUF 12 >> +/* This set of options defines size of file name members in the >> FILINFO structure >> +/ which is used to read out directory items. These values should be >> suffcient for >> +/ the file names to read. The maximum possible length of the read >> file name depends >> +/ on character encoding. When LFN is not enabled, these options have >> no effect. */ >> + >> + >> +#define FF_STRF_ENCODE 3 >> +/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, >> f_gets(), >> +/ f_putc(), f_puts and f_printf() convert the character encoding in it. >> +/ This option selects assumption of character encoding ON THE FILE >> to be >> +/ read/written via those functions. >> +/ >> +/ 0: ANSI/OEM in current CP >> +/ 1: Unicode in UTF-16LE >> +/ 2: Unicode in UTF-16BE >> +/ 3: Unicode in UTF-8 >> +*/ >> + >> + >> +#define FF_FS_RPATH 0 >> +/* This option configures support for relative path. >> +/ >> +/ 0: Disable relative path and remove related functions. >> +/ 1: Enable relative path. f_chdir() and f_chdrive() are available. >> +/ 2: f_getcwd() function is available in addition to 1. >> +*/ >> + >> + >> +/*---------------------------------------------------------------------------/ >> >> +/ Drive/Volume Configurations >> +/---------------------------------------------------------------------------*/ >> >> + >> +#define FF_VOLUMES 1 >> +/* Number of volumes (logical drives) to be used. (1-10) */ >> + >> + >> +#define FF_STR_VOLUME_ID 0 >> +#define FF_VOLUME_STRS >> "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" >> +/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings. >> +/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be >> used as drive >> +/ number in the path name. FF_VOLUME_STRS defines the volume ID >> strings for each >> +/ logical drives. Number of items must not be less than FF_VOLUMES. >> Valid >> +/ characters for the volume ID strings are A-Z, a-z and 0-9, >> however, they are >> +/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and >> FF_VOLUME_STRS is >> +/ not defined, a user defined volume string table needs to be >> defined as: >> +/ >> +/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",... >> +*/ >> + >> + >> +#define FF_MULTI_PARTITION 0 >> +/* This option switches support for multiple volumes on the physical >> drive. >> +/ By default (0), each logical drive number is bound to the same >> physical drive >> +/ number and only an FAT volume found on the physical drive will be >> mounted. >> +/ When this function is enabled (1), each logical drive number can >> be bound to >> +/ arbitrary physical drive and partition listed in the VolToPart[]. >> Also f_fdisk() >> +/ funciton will be available. */ >> + >> + >> +#define FF_MIN_SS 512 >> +#define FF_MAX_SS 512 >> +/* This set of options configures the range of sector size to be >> supported. (512, >> +/ 1024, 2048 or 4096) Always set both 512 for most systems, generic >> memory card and >> +/ harddisk. But a larger value may be required for on-board flash >> memory and some >> +/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, >> FatFs is configured >> +/ for variable sector size mode and disk_ioctl() function needs to >> implement >> +/ GET_SECTOR_SIZE command. */ >> + >> + >> +#define FF_LBA64 0 >> +/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable) >> +/ To enable the 64-bit LBA, also exFAT needs to be enabled. >> (FF_FS_EXFAT == 1) */ >> + >> + >> +#define FF_MIN_GPT 0x10000000 >> +/* Minimum number of sectors to switch GPT as partitioning format in >> f_mkfs and >> +/ f_fdisk function. 0x100000000 max. This option has no effect when >> FF_LBA64 == 0. */ >> + >> + >> +#define FF_USE_TRIM 0 >> +/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable) >> +/ To enable Trim function, also CTRL_TRIM command should be >> implemented to the >> +/ disk_ioctl() function. */ >> + >> + >> + >> +/*---------------------------------------------------------------------------/ >> >> +/ System Configurations >> +/---------------------------------------------------------------------------*/ >> >> + >> +#define FF_FS_TINY 0 >> +/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) >> +/ At the tiny configuration, size of file object (FIL) is shrinked >> FF_MAX_SS bytes. >> +/ Instead of private sector buffer eliminated from the file object, >> common sector >> +/ buffer in the filesystem object (FATFS) is used for the file data >> transfer. */ >> + >> + >> +#define FF_FS_EXFAT 0 >> +/* This option switches support for exFAT filesystem. (0:Disable or >> 1:Enable) >> +/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1) >> +/ Note that enabling exFAT discards ANSI C (C89) compatibility. */ >> + >> + >> +#define FF_FS_NORTC 0 >> +#define FF_NORTC_MON 1 >> +#define FF_NORTC_MDAY 1 >> +#define FF_NORTC_YEAR 2020 >> +/* The option FF_FS_NORTC switches timestamp functiton. If the system >> does not have >> +/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC >> = 1 to disable >> +/ the timestamp function. Every object modified by FatFs will have a >> fixed timestamp >> +/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local >> time. >> +/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() >> function need to be >> +/ added to the project to read current time form real-time clock. >> FF_NORTC_MON, >> +/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect. >> +/ These options have no effect in read-only configuration >> (FF_FS_READONLY = 1). */ >> + >> + >> +#define FF_FS_NOFSINFO 0 >> +/* If you need to know correct free space on the FAT32 volume, set >> bit 0 of this >> +/ option, and f_getfree() function at first time after volume mount >> will force >> +/ a full FAT scan. Bit 1 controls the use of last allocated cluster >> number. >> +/ >> +/ bit0=0: Use free cluster count in the FSINFO if available. >> +/ bit0=1: Do not trust free cluster count in the FSINFO. >> +/ bit1=0: Use last allocated cluster number in the FSINFO if available. >> +/ bit1=1: Do not trust last allocated cluster number in the FSINFO. >> +*/ >> + >> + >> +#define FF_FS_LOCK 0 >> +/* The option FF_FS_LOCK switches file lock function to control >> duplicated file open >> +/ and illegal operation to open objects. This option must be 0 when >> FF_FS_READONLY >> +/ is 1. >> +/ >> +/ 0: Disable file lock function. To avoid volume corruption, >> application program >> +/ should avoid illegal open, remove and rename to the open objects. >> +/ >0: Enable file lock function. The value defines how many >> files/sub-directories >> +/ can be opened simultaneously under file lock control. Note >> that the file >> +/ lock control is independent of re-entrancy. */ >> + >> + >> +/* #include <somertos.h> // O/S definitions */ >> +#define FF_FS_REENTRANT 0 >> +#define FF_FS_TIMEOUT 1000 >> +#define FF_SYNC_t HANDLE >> +/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) >> of the FatFs >> +/ module itself. Note that regardless of this option, file access to >> different >> +/ volume is always re-entrant and volume control functions, >> f_mount(), f_mkfs() >> +/ and f_fdisk() function, are always not re-entrant. Only >> file/directory access >> +/ to the same volume is under control of this function. >> +/ >> +/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect. >> +/ 1: Enable re-entrancy. Also user provided synchronization handlers, >> +/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and >> ff_cre_syncobj() >> +/ function, must be added to the project. Samples are available in >> +/ option/syscall.c. >> +/ >> +/ The FF_FS_TIMEOUT defines timeout period in unit of time tick. >> +/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, >> ID, OS_EVENT*, >> +/ SemaphoreHandle_t and etc. A header file for O/S definitions needs >> to be >> +/ included somewhere in the scope of ff.h. */ >> + >> + >> + >> +/*--- End of configuration options ---*/ >> > >
Hello Stefano, when looking at the code I get the impression, that Coverity Scan might report a false positive, explanation below. On 26.03.21 21:53, Stefano Babic wrote: > Hi Roland, > > On 26.03.21 15:18, Stefano Babic wrote: >> Hi Roland, >> >> I have no specific comments to your series. I merged the series and I >> ran the coverity scan. I agree that this is imported code, and it does >> not matter if coding style differs from SWUpdate's. However, coverity >> discovers some topics in the code that I think they should fixes (with >> an additional patch). To be short: malloc() is called, no check if the >> pointer is Null. Can you check it ? >> > > I was too fast to report the coverity's report : most of them are > unrelated to this series. There is just: > >> Here the results: >> >> Hi, >> >> Please find the latest report on new defect(s) introduced to >> sbabic/swupdate found with Coverity Scan. >> >> 6 new defect(s) introduced to sbabic/swupdate found with Coverity Scan. >> >> >> New defect(s) Reported-by: Coverity Scan >> Showing 6 of 6 defect(s) >> >> >> ** CID 329979: Control flow issues (DEADCODE) >> /fatfs/ff.c: 5817 in create_partition() >> In the following is a code snipped from the complained code: sz_drv32 is the disk size in number of sectors. n_hd is the number of heads with should not become bigger than 255. 5815: n_sc = N_SEC_TRACK; /* N_SEC_TRACK = 63 */ 5816: for (n_hd = 8; n_hd != 0 && sz_drv32 / n_hd / n_sc > 1024; n_hd *= 2) ; 5817: if (n_hd == 0) n_hd = 255; Assuming a sector size of 512, any disk size greater than about 4 GiB will result in n_hd being 0 after finishing the loop in line 5816. In such case the condition in line 5817 will become true for any disk bigger than 4 GiB. Line 5817 being dead code would require, that sz_drv32 is always less than about 0x007f_ffff. sz_drv32 is set by the disk_ioctl function with the GET_SECTOR_COUNT command. All variables involved are at least 32 bits wide. > > But this can be fixed later in a follow up code. I merge the series into > master. > > Best regards, > Stefano Babic > >> >> ________________________________________________________________________________________________________ >> >> *** CID 329979: Control flow issues (DEADCODE) >> /fatfs/ff.c: 5817 in create_partition() >> 5811 } else >> 5812 #endif >> 5813 { /* Create partitions in MBR */ >> 5814 sz_drv32 = (DWORD)sz_drv; >> 5815 n_sc = N_SEC_TRACK; /* Determine drive CHS >> without any consideration of the drive geometry */ >> 5816 for (n_hd = 8; n_hd != 0 && sz_drv32 / n_hd / n_sc > >> 1024; n_hd *= 2) ; >> >>> CID 329979: Control flow issues (DEADCODE) >> >>> Execution cannot reach this statement: "n_hd = 255;". >> 5817 if (n_hd == 0) n_hd = 255; /* Number of heads >> needs to be <256 */ >> 5818 >> 5819 mem_set(buf, 0, FF_MAX_SS); /* Clear MBR */ >> 5820 pte = buf + MBR_Table; /* Partition table in the >> MBR */ >> 5821 for (i = 0, s_lba32 = n_sc; i < 4 && s_lba32 != 0 && >> s_lba32 < sz_drv32; i++, s_lba32 += n_lba32) { >> 5822 n_lba32 = (DWORD)plst[i]; /* Get partition >> size */ >> >> ** CID 314959: Null pointer dereferences (NULL_RETURNS) >> /mongoose/mongoose.c: 8474 in mg_file_upload_handler() >> >> >> ________________________________________________________________________________________________________ >> >> *** CID 314959: Null pointer dereferences (NULL_RETURNS) >> /mongoose/mongoose.c: 8474 in mg_file_upload_handler() >> 8468 fus = (struct file_upload_state *) MG_CALLOC(1, >> sizeof(*fus)); >> 8469 if (fus == NULL) { >> 8470 nc->flags |= MG_F_CLOSE_IMMEDIATELY; >> 8471 return; >> 8472 } >> 8473 fus->lfn = (char *) MG_MALLOC(lfn.len + 1); >> >>> CID 314959: Null pointer dereferences (NULL_RETURNS) >> >>> Dereferencing a pointer that might be "NULL" "fus->lfn" when >> calling "memcpy". >> 8474 memcpy(fus->lfn, lfn.p, lfn.len); >> 8475 fus->lfn[lfn.len] = '\0'; >> 8476 if (lfn.p != mp->file_name) MG_FREE((char *) lfn.p); >> 8477 LOG(LL_DEBUG, >> 8478 ("%p Receiving file %s -> %s", nc, mp->file_name, >> fus->lfn)); >> 8479 fus->fp = mg_fopen(fus->lfn, "wb"); >> >> ** CID 314957: Null pointer dereferences (NULL_RETURNS) >> /mongoose/mongoose.c: 2452 in mg_mgr_init_opt() >> >> >> ________________________________________________________________________________________________________ >> >> *** CID 314957: Null pointer dereferences (NULL_RETURNS) >> /mongoose/mongoose.c: 2452 in mg_mgr_init_opt() >> 2446 opts.ifaces[MG_MAIN_IFACE] = opts.main_iface; >> 2447 } >> 2448 m->num_ifaces = opts.num_ifaces; >> 2449 m->ifaces = >> 2450 (struct mg_iface **) MG_MALLOC(sizeof(*m->ifaces) * >> opts.num_ifaces); >> 2451 for (i = 0; i < opts.num_ifaces; i++) { >> >>> CID 314957: Null pointer dereferences (NULL_RETURNS) >> >>> Dereferencing "m->ifaces", which is known to be "NULL". >> 2452 m->ifaces[i] = mg_if_create_iface(opts.ifaces[i], m); >> 2453 m->ifaces[i]->vtable->init(m->ifaces[i]); >> 2454 } >> 2455 } >> 2456 if (opts.nameserver != NULL) { >> 2457 m->nameserver = strdup(opts.nameserver); >> >> ** CID 314956: Null pointer dereferences (NULL_RETURNS) >> >> >> ________________________________________________________________________________________________________ >> >> *** CID 314956: Null pointer dereferences (NULL_RETURNS) >> /mongoose/mongoose.c: 12007 in mg_resolve_async_eh() >> 12001 req->last_time = now; >> 12002 req->retries++; >> 12003 } >> 12004 break; >> 12005 case MG_EV_RECV: >> 12006 msg = (struct mg_dns_message *) MG_MALLOC(sizeof(*msg)); >> >>> CID 314956: Null pointer dereferences (NULL_RETURNS) >> >>> Dereferencing a pointer that might be "NULL" "msg" when >> calling "mg_parse_dns". >> 12007 if (mg_parse_dns(nc->recv_mbuf.buf, *(int *) data, >> msg) == 0 && >> 12008 msg->num_answers > 0) { >> 12009 req->callback(msg, req->data, MG_RESOLVE_OK); >> 12010 nc->user_data = NULL; >> 12011 MG_FREE(req); >> 12012 } else { >> >> ** CID 314955: Null pointer dereferences (NULL_RETURNS) >> /suricatta/server_hawkbit.c: 1485 in server_send_target_data() >> >> >> ________________________________________________________________________________________________________ >> >> *** CID 314955: Null pointer dereferences (NULL_RETURNS) >> /suricatta/server_hawkbit.c: 1485 in server_send_target_data() >> 1479 if (!len) { >> 1480 server_hawkbit.has_to_send_configData = false; >> 1481 return SERVER_OK; >> 1482 } >> 1483 >> 1484 char *configData = (char *)(malloc(len + 16)); >> >>> CID 314955: Null pointer dereferences (NULL_RETURNS) >> >>> Dereferencing a pointer that might be "NULL" "configData" >> when calling "memset". >> 1485 memset(configData, 0, len + 16); >> 1486 >> 1487 static const char* const config_data = STRINGIFY( >> 1488 %c"%s": "%s" >> 1489 ); >> 1490 >> >> ** CID 314954: Null pointer dereferences (NULL_RETURNS) >> >> >> ________________________________________________________________________________________________________ >> >> *** CID 314954: Null pointer dereferences (NULL_RETURNS) >> /mongoose/mongoose.c: 7464 in mg_parse_http_basic_auth() >> 7458 char fmt[64]; >> 7459 int res = 0; >> 7460 >> 7461 if (mg_strncmp(*hdr, mg_mk_str("Basic "), 6) != 0) return -1; >> 7462 >> 7463 buf = (char *) MG_MALLOC(hdr->len); >> >>> CID 314954: Null pointer dereferences (NULL_RETURNS) >> >>> Dereferencing a pointer that might be "NULL" "buf" when >> calling "cs_base64_decode". >> 7464 cs_base64_decode((unsigned char *) hdr->p + 6, hdr->len, >> buf, NULL); >> 7465 >> 7466 /* e.g. "%123[^:]:%321[^\n]" */ >> 7467 snprintf(fmt, sizeof(fmt), "%%%" SIZE_T_FMT "[^:]:%%%" >> SIZE_T_FMT "[^\n]", >> 7468 user_len - 1, pass_len - 1); >> 7469 if (sscanf(buf, fmt, user, pass) == 0) { >> >> Best regards, >> Stefano Babic >> >> On 25.03.21 16:46, roland.gaudig-oss@weidmueller.com wrote: >>> From: Roland Gaudig <roland.gaudig@weidmueller.com> >>> >>> FatFs is a FAT filesystem implementation for small embedded systems >>> written >>> by ChaN. For more information see: >>> http://elm-chan.org/fsw/ff/00index_e.html >>> >>> Signed-off-by: Roland Gaudig <roland.gaudig@weidmueller.com> >>> --- >>> fatfs/diskio.c | 174 ++ >>> fatfs/diskio.h | 96 + >>> fatfs/ff.c | 6875 ++++++++++++++++++++++++++++++++++++++++++++++++ >>> fatfs/ff.h | 425 +++ >>> fatfs/ffconf.h | 317 +++ >>> 5 files changed, 7887 insertions(+) >>> create mode 100644 fatfs/diskio.c >>> create mode 100644 fatfs/diskio.h >>> create mode 100644 fatfs/ff.c >>> create mode 100644 fatfs/ff.h >>> create mode 100644 fatfs/ffconf.h >>> >>> diff --git a/fatfs/diskio.c b/fatfs/diskio.c >>> new file mode 100644 >>> index 0000000..1976add >>> --- /dev/null >>> +++ b/fatfs/diskio.c >>> @@ -0,0 +1,174 @@ >>> +/* >>> + * Copyright (C) 2021 Weidmueller Interface GmbH & Co. KG >>> + * Roland Gaudig <roland.gaudig@weidmueller.com> >>> + * >>> + * SPDX-License-Identifier: GPL-2.0-or-later >>> + */ >>> + >>> +#include <errno.h> >>> +#include <fcntl.h> >>> +#include <time.h> >>> +#include <unistd.h> >>> +#include <linux/fs.h> >>> +#include <sys/ioctl.h> >>> + >>> +#include "swupdate.h" >>> +#include "util.h" >>> + >>> +#include "ff.h" >>> +#include "diskio.h" >>> + >>> + >>> +#define SECTOR_SIZE 512 >>> + >>> +static int file_descriptor; >>> +static char device_name[MAX_VOLNAME]; >>> +static bool init_status; >>> + >>> +int fatfs_init(char *device); >>> +void fatfs_release(void); >>> + >>> + >>> +int fatfs_init(char *device) >>> +{ >>> + if (strnlen(device_name, MAX_VOLNAME)) { >>> + ERROR("Called fatfs_init second time without fatfs_release"); >>> + return -1; >>> + } >>> + >>> + strncpy(device_name, device, sizeof(device_name)); >>> + file_descriptor = open(device_name, O_RDWR); >>> + >>> + if (file_descriptor < 0) { >>> + ERROR("Device %s cannot be opened: %s", device_name, >>> strerror(errno)); >>> + return -ENODEV; >>> + } >>> + >>> + return 0; >>> +} >>> + >>> +void fatfs_release(void) >>> +{ >>> + (void)close(file_descriptor); >>> + memset(device_name, 0, MAX_VOLNAME); >>> + init_status = false; >>> +} >>> + >>> +DSTATUS disk_status(BYTE pdrv) >>> +{ >>> + DSTATUS status = 0; >>> + (void)pdrv; >>> + >>> + if (!strnlen(device_name, MAX_VOLNAME)) >>> + status |= STA_NODISK; >>> + >>> + if (!init_status) >>> + status |= STA_NOINIT; >>> + >>> + return status; >>> +} >>> + >>> +DSTATUS disk_initialize(BYTE pdrv) >>> +{ >>> + init_status = true; >>> + >>> + return disk_status(pdrv); >>> +} >>> + >>> +DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) >>> +{ >>> + (void)pdrv; >>> + >>> + if (!buff) >>> + return RES_PARERR; >>> + >>> + if (disk_status(pdrv)) >>> + return RES_NOTRDY; >>> + >>> + if (pread(file_descriptor, buff, count * SECTOR_SIZE, sector * >>> SECTOR_SIZE) != count * SECTOR_SIZE) >>> + return RES_ERROR; >>> + >>> + return RES_OK; >>> +} >>> + >>> +#if FF_FS_READONLY == 0 >>> +DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT >>> count) >>> +{ >>> + (void)pdrv; >>> + >>> + if (!buff) >>> + return RES_PARERR; >>> + >>> + if (disk_status(pdrv)) >>> + return RES_NOTRDY; >>> + >>> + if (pwrite(file_descriptor, buff, count * SECTOR_SIZE, sector * >>> SECTOR_SIZE) != count * SECTOR_SIZE) >>> + return RES_ERROR; >>> + >>> + return RES_OK; >>> +} >>> +#endif >>> + >>> +DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) >>> +{ >>> + (void) pdrv; >>> + >>> + if (disk_status(pdrv)) >>> + return RES_NOTRDY; >>> + >>> + switch (cmd) { >>> + case CTRL_SYNC: >>> + if (syncfs(file_descriptor) != 0) >>> + return RES_ERROR; >>> + break; >>> + case GET_SECTOR_COUNT: >>> + { >>> + off_t size = lseek(file_descriptor, 0, SEEK_END) / SECTOR_SIZE; >>> + >>> + if (!buff) >>> + return RES_PARERR; >>> + >>> + *(LBA_t*)buff = size; >>> + break; >>> + } >>> + case GET_SECTOR_SIZE: >>> + if (!buff) >>> + return RES_PARERR; >>> + >>> + *(WORD*)buff = SECTOR_SIZE; >>> + break; >>> + case GET_BLOCK_SIZE: >>> + /* Get erase block size of flash memories, return 1 if not a >>> + * Flash memory or unknown. >>> + */ >>> + if (!buff) >>> + return RES_PARERR; >>> + >>> + *(WORD*)buff = 1; >>> + break; >>> + default: >>> + ERROR("cmd %d not implemented", cmd); >>> + return RES_PARERR; >>> + break; >>> + } >>> + >>> + return RES_OK; >>> +} >>> + >>> +DWORD get_fattime(void) >>> +{ >>> + time_t unix_time = time(NULL); >>> + struct tm *t = gmtime(&unix_time); >>> + >>> + /* FatFs times are based on year 1980 */ >>> + DWORD tdos = ((t->tm_year - 80) & 0x3F) << 25; >>> + /* FatFs months start with 1 */ >>> + tdos |= (t->tm_mon + 1) << 21; >>> + tdos |= t->tm_mday << 16; >>> + tdos |= t->tm_hour << 11; >>> + tdos |= t->tm_min << 5; >>> + /* Don't know how FatFs copes with leap seconds, therefore limit >>> them */ >>> + tdos |= (t->tm_sec % 60) / 2; >>> + >>> + return tdos; >>> +} >>> diff --git a/fatfs/diskio.h b/fatfs/diskio.h >>> new file mode 100644 >>> index 0000000..1f019a1 >>> --- /dev/null >>> +++ b/fatfs/diskio.h >>> @@ -0,0 +1,96 @@ >>> +/*-----------------------------------------------------------------------/ >>> >>> +/ Low level disk interface modlue include file (C)ChaN, >>> 2019 / >>> +/------------------------------------------------------------------------/ >>> >>> +/ >>> +/ Copyright (C) 2019, ChaN, all right reserved. >>> +/ >>> +/ FatFs module is an open source software. Redistribution and use of >>> FatFs in >>> +/ source and binary forms, with or without modification, are >>> permitted provided >>> +/ that the following condition is met: >>> +/ >>> +/ 1. Redistributions of source code must retain the above copyright >>> notice, >>> +/ this condition and the following disclaimer. >>> +/ >>> +/ This software is provided by the copyright holder and contributors >>> "AS IS" >>> +/ and any warranties related to this software are DISCLAIMED. >>> +/ The copyright owner or contributors be NOT LIABLE for any damages >>> caused >>> +/ by use of this software. >>> +/ >>> +/ SPDX-License-Identifier: BSD-1-Clause >>> +/ >>> +/-----------------------------------------------------------------------*/ >>> >>> + >>> + >>> +#ifndef _DISKIO_DEFINED >>> +#define _DISKIO_DEFINED >>> + >>> +#ifdef __cplusplus >>> +extern "C" { >>> +#endif >>> + >>> +/* Status of Disk Functions */ >>> +typedef BYTE DSTATUS; >>> + >>> +/* Results of Disk Functions */ >>> +typedef enum { >>> + RES_OK = 0, /* 0: Successful */ >>> + RES_ERROR, /* 1: R/W Error */ >>> + RES_WRPRT, /* 2: Write Protected */ >>> + RES_NOTRDY, /* 3: Not Ready */ >>> + RES_PARERR /* 4: Invalid Parameter */ >>> +} DRESULT; >>> + >>> + >>> +/*---------------------------------------*/ >>> +/* Prototypes for disk control functions */ >>> + >>> + >>> +DSTATUS disk_initialize (BYTE pdrv); >>> +DSTATUS disk_status (BYTE pdrv); >>> +DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count); >>> +DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT >>> count); >>> +DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); >>> + >>> + >>> +/* Disk Status Bits (DSTATUS) */ >>> + >>> +#define STA_NOINIT 0x01 /* Drive not initialized */ >>> +#define STA_NODISK 0x02 /* No medium in the drive */ >>> +#define STA_PROTECT 0x04 /* Write protected */ >>> + >>> + >>> +/* Command code for disk_ioctrl fucntion */ >>> + >>> +/* Generic command (Used by FatFs) */ >>> +#define CTRL_SYNC 0 /* Complete pending write process >>> (needed at FF_FS_READONLY == 0) */ >>> +#define GET_SECTOR_COUNT 1 /* Get media size (needed at >>> FF_USE_MKFS == 1) */ >>> +#define GET_SECTOR_SIZE 2 /* Get sector size (needed at >>> FF_MAX_SS != FF_MIN_SS) */ >>> +#define GET_BLOCK_SIZE 3 /* Get erase block size (needed >>> at FF_USE_MKFS == 1) */ >>> +#define CTRL_TRIM 4 /* Inform device that the data on >>> the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */ >>> + >>> +/* Generic command (Not used by FatFs) */ >>> +#define CTRL_POWER 5 /* Get/Set power status */ >>> +#define CTRL_LOCK 6 /* Lock/Unlock media removal */ >>> +#define CTRL_EJECT 7 /* Eject media */ >>> +#define CTRL_FORMAT 8 /* Create physical format on the >>> media */ >>> + >>> +/* MMC/SDC specific ioctl command */ >>> +#define MMC_GET_TYPE 10 /* Get card type */ >>> +#define MMC_GET_CSD 11 /* Get CSD */ >>> +#define MMC_GET_CID 12 /* Get CID */ >>> +#define MMC_GET_OCR 13 /* Get OCR */ >>> +#define MMC_GET_SDSTAT 14 /* Get SD status */ >>> +#define ISDIO_READ 55 /* Read data form SD iSDIO >>> register */ >>> +#define ISDIO_WRITE 56 /* Write data to SD iSDIO >>> register */ >>> +#define ISDIO_MRITE 57 /* Masked write data to SD >>> iSDIO register */ >>> + >>> +/* ATA/CF specific ioctl command */ >>> +#define ATA_GET_REV 20 /* Get F/W revision */ >>> +#define ATA_GET_MODEL 21 /* Get model name */ >>> +#define ATA_GET_SN 22 /* Get serial number */ >>> + >>> +#ifdef __cplusplus >>> +} >>> +#endif >>> + >>> +#endif >>> diff --git a/fatfs/ff.c b/fatfs/ff.c >>> new file mode 100644 >>> index 0000000..9281ec8 >>> --- /dev/null >>> +++ b/fatfs/ff.c >>> @@ -0,0 +1,6875 @@ >>> +/*----------------------------------------------------------------------------/ >>> >>> +/ FatFs - Generic FAT Filesystem Module >>> R0.14a / >>> +/-----------------------------------------------------------------------------/ >>> >>> +/ >>> +/ Copyright (C) 2020, ChaN, all right reserved. >>> +/ >>> +/ FatFs module is an open source software. Redistribution and use of >>> FatFs in >>> +/ source and binary forms, with or without modification, are >>> permitted provided >>> +/ that the following condition is met: >>> +/ >>> +/ 1. Redistributions of source code must retain the above copyright >>> notice, >>> +/ this condition and the following disclaimer. >>> +/ >>> +/ This software is provided by the copyright holder and contributors >>> "AS IS" >>> +/ and any warranties related to this software are DISCLAIMED. >>> +/ The copyright owner or contributors be NOT LIABLE for any damages >>> caused >>> +/ by use of this software. >>> +/ >>> +/ SPDX-License-Identifier: BSD-1-Clause >>> +/ >>> +/----------------------------------------------------------------------------*/ >>> >>> + >>> + >>> +#include "ff.h" /* Declarations of FatFs API */ >>> +#include "diskio.h" /* Declarations of device I/O functions */ >>> + >>> + >>> +/*-------------------------------------------------------------------------- >>> >>> + >>> + Module Private Definitions >>> + >>> +---------------------------------------------------------------------------*/ >>> >>> + >>> +#if FF_DEFINED != 80196 /* Revision ID */ >>> +#error Wrong include file (ff.h). >>> +#endif >>> + >>> + >>> +/* Limits and boundaries */ >>> +#define MAX_DIR 0x200000 /* Max size of FAT directory */ >>> +#define MAX_DIR_EX 0x10000000 /* Max size of exFAT >>> directory */ >>> +#define MAX_FAT12 0xFF5 /* Max FAT12 clusters (differs >>> from specs, but right for real DOS/Windows behavior) */ >>> +#define MAX_FAT16 0xFFF5 /* Max FAT16 clusters >>> (differs from specs, but right for real DOS/Windows behavior) */ >>> +#define MAX_FAT32 0x0FFFFFF5 /* Max FAT32 clusters (not >>> specified, practical limit) */ >>> +#define MAX_EXFAT 0x7FFFFFFD /* Max exFAT clusters >>> (differs from specs, implementation limit) */ >>> + >>> + >>> +/* Character code support macros */ >>> +#define IsUpper(c) ((c) >= 'A' && (c) <= 'Z') >>> +#define IsLower(c) ((c) >= 'a' && (c) <= 'z') >>> +#define IsDigit(c) ((c) >= '0' && (c) <= '9') >>> +#define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF) >>> +#define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF) >>> +#define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF) >>> + >>> + >>> +/* Additional file access control and file status flags for internal >>> use */ >>> +#define FA_SEEKEND 0x20 /* Seek to end of the file on file >>> open */ >>> +#define FA_MODIFIED 0x40 /* File has been modified */ >>> +#define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */ >>> + >>> + >>> +/* Additional file attribute bits for internal use */ >>> +#define AM_VOL 0x08 /* Volume label */ >>> +#define AM_LFN 0x0F /* LFN entry */ >>> +#define AM_MASK 0x3F /* Mask of defined bits */ >>> + >>> + >>> +/* Name status flags in fn[11] */ >>> +#define NSFLAG 11 /* Index of the name status byte */ >>> +#define NS_LOSS 0x01 /* Out of 8.3 format */ >>> +#define NS_LFN 0x02 /* Force to create LFN entry */ >>> +#define NS_LAST 0x04 /* Last segment */ >>> +#define NS_BODY 0x08 /* Lower case flag (body) */ >>> +#define NS_EXT 0x10 /* Lower case flag (ext) */ >>> +#define NS_DOT 0x20 /* Dot entry */ >>> +#define NS_NOLFN 0x40 /* Do not find LFN */ >>> +#define NS_NONAME 0x80 /* Not followed */ >>> + >>> + >>> +/* exFAT directory entry types */ >>> +#define ET_BITMAP 0x81 /* Allocation bitmap */ >>> +#define ET_UPCASE 0x82 /* Up-case table */ >>> +#define ET_VLABEL 0x83 /* Volume label */ >>> +#define ET_FILEDIR 0x85 /* File and directory */ >>> +#define ET_STREAM 0xC0 /* Stream extension */ >>> +#define ET_FILENAME 0xC1 /* Name extension */ >>> + >>> + >>> +/* FatFs refers the FAT structure as simple byte array instead of >>> structure member >>> +/ because the C structure is not binary compatible between different >>> platforms */ >>> + >>> +#define BS_JmpBoot 0 /* x86 jump instruction >>> (3-byte) */ >>> +#define BS_OEMName 3 /* OEM name (8-byte) */ >>> +#define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */ >>> +#define BPB_SecPerClus 13 /* Cluster size [sector] >>> (BYTE) */ >>> +#define BPB_RsvdSecCnt 14 /* Size of reserved area >>> [sector] (WORD) */ >>> +#define BPB_NumFATs 16 /* Number of FATs (BYTE) */ >>> +#define BPB_RootEntCnt 17 /* Size of root directory >>> area for FAT [entry] (WORD) */ >>> +#define BPB_TotSec16 19 /* Volume size (16-bit) >>> [sector] (WORD) */ >>> +#define BPB_Media 21 /* Media descriptor byte >>> (BYTE) */ >>> +#define BPB_FATSz16 22 /* FAT size (16-bit) >>> [sector] (WORD) */ >>> +#define BPB_SecPerTrk 24 /* Number of sectors per >>> track for int13h [sector] (WORD) */ >>> +#define BPB_NumHeads 26 /* Number of heads for int13h >>> (WORD) */ >>> +#define BPB_HiddSec 28 /* Volume offset from top >>> of the drive (DWORD) */ >>> +#define BPB_TotSec32 32 /* Volume size (32-bit) >>> [sector] (DWORD) */ >>> +#define BS_DrvNum 36 /* Physical drive number for >>> int13h (BYTE) */ >>> +#define BS_NTres 37 /* WindowsNT error flag (BYTE) */ >>> +#define BS_BootSig 38 /* Extended boot signature >>> (BYTE) */ >>> +#define BS_VolID 39 /* Volume serial number >>> (DWORD) */ >>> +#define BS_VolLab 43 /* Volume label string >>> (8-byte) */ >>> +#define BS_FilSysType 54 /* Filesystem type string >>> (8-byte) */ >>> +#define BS_BootCode 62 /* Boot code (448-byte) */ >>> +#define BS_55AA 510 /* Signature word (WORD) */ >>> + >>> +#define BPB_FATSz32 36 /* FAT32: FAT size [sector] >>> (DWORD) */ >>> +#define BPB_ExtFlags32 40 /* FAT32: Extended flags >>> (WORD) */ >>> +#define BPB_FSVer32 42 /* FAT32: Filesystem >>> version (WORD) */ >>> +#define BPB_RootClus32 44 /* FAT32: Root directory >>> cluster (DWORD) */ >>> +#define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO >>> sector (WORD) */ >>> +#define BPB_BkBootSec32 50 /* FAT32: Offset of backup >>> boot sector (WORD) */ >>> +#define BS_DrvNum32 64 /* FAT32: Physical drive >>> number for int13h (BYTE) */ >>> +#define BS_NTres32 65 /* FAT32: Error flag (BYTE) */ >>> +#define BS_BootSig32 66 /* FAT32: Extended boot >>> signature (BYTE) */ >>> +#define BS_VolID32 67 /* FAT32: Volume serial >>> number (DWORD) */ >>> +#define BS_VolLab32 71 /* FAT32: Volume label >>> string (8-byte) */ >>> +#define BS_FilSysType32 82 /* FAT32: Filesystem type >>> string (8-byte) */ >>> +#define BS_BootCode32 90 /* FAT32: Boot code >>> (420-byte) */ >>> + >>> +#define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */ >>> +#define BPB_VolOfsEx 64 /* exFAT: Volume offset from >>> top of the drive [sector] (QWORD) */ >>> +#define BPB_TotSecEx 72 /* exFAT: Volume size [sector] >>> (QWORD) */ >>> +#define BPB_FatOfsEx 80 /* exFAT: FAT offset from top >>> of the volume [sector] (DWORD) */ >>> +#define BPB_FatSzEx 84 /* exFAT: FAT size [sector] >>> (DWORD) */ >>> +#define BPB_DataOfsEx 88 /* exFAT: Data offset from >>> top of the volume [sector] (DWORD) */ >>> +#define BPB_NumClusEx 92 /* exFAT: Number of clusters >>> (DWORD) */ >>> +#define BPB_RootClusEx 96 /* exFAT: Root directory >>> start cluster (DWORD) */ >>> +#define BPB_VolIDEx 100 /* exFAT: Volume serial >>> number (DWORD) */ >>> +#define BPB_FSVerEx 104 /* exFAT: Filesystem >>> version (WORD) */ >>> +#define BPB_VolFlagEx 106 /* exFAT: Volume flags >>> (WORD) */ >>> +#define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size >>> in unit of byte (BYTE) */ >>> +#define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster >>> size in unit of sector (BYTE) */ >>> +#define BPB_NumFATsEx 110 /* exFAT: Number of FATs >>> (BYTE) */ >>> +#define BPB_DrvNumEx 111 /* exFAT: Physical drive >>> number for int13h (BYTE) */ >>> +#define BPB_PercInUseEx 112 /* exFAT: Percent in use >>> (BYTE) */ >>> +#define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */ >>> +#define BS_BootCodeEx 120 /* exFAT: Boot code >>> (390-byte) */ >>> + >>> +#define DIR_Name 0 /* Short file name (11-byte) */ >>> +#define DIR_Attr 11 /* Attribute (BYTE) */ >>> +#define DIR_NTres 12 /* Lower case flag (BYTE) */ >>> +#define DIR_CrtTime10 13 /* Created time sub-second >>> (BYTE) */ >>> +#define DIR_CrtTime 14 /* Created time (DWORD) */ >>> +#define DIR_LstAccDate 18 /* Last accessed date (WORD) */ >>> +#define DIR_FstClusHI 20 /* Higher 16-bit of first >>> cluster (WORD) */ >>> +#define DIR_ModTime 22 /* Modified time (DWORD) */ >>> +#define DIR_FstClusLO 26 /* Lower 16-bit of first >>> cluster (WORD) */ >>> +#define DIR_FileSize 28 /* File size (DWORD) */ >>> +#define LDIR_Ord 0 /* LFN: LFN order and LLE flag >>> (BYTE) */ >>> +#define LDIR_Attr 11 /* LFN: LFN attribute (BYTE) */ >>> +#define LDIR_Type 12 /* LFN: Entry type (BYTE) */ >>> +#define LDIR_Chksum 13 /* LFN: Checksum of the SFN >>> (BYTE) */ >>> +#define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD) */ >>> +#define XDIR_Type 0 /* exFAT: Type of exFAT >>> directory entry (BYTE) */ >>> +#define XDIR_NumLabel 1 /* exFAT: Number of volume >>> label characters (BYTE) */ >>> +#define XDIR_Label 2 /* exFAT: Volume label >>> (11-WORD) */ >>> +#define XDIR_CaseSum 4 /* exFAT: Sum of case >>> conversion table (DWORD) */ >>> +#define XDIR_NumSec 1 /* exFAT: Number of >>> secondary entries (BYTE) */ >>> +#define XDIR_SetSum 2 /* exFAT: Sum of the set of >>> directory entries (WORD) */ >>> +#define XDIR_Attr 4 /* exFAT: File attribute >>> (WORD) */ >>> +#define XDIR_CrtTime 8 /* exFAT: Created time (DWORD) */ >>> +#define XDIR_ModTime 12 /* exFAT: Modified time >>> (DWORD) */ >>> +#define XDIR_AccTime 16 /* exFAT: Last accessed time >>> (DWORD) */ >>> +#define XDIR_CrtTime10 20 /* exFAT: Created time >>> subsecond (BYTE) */ >>> +#define XDIR_ModTime10 21 /* exFAT: Modified time >>> subsecond (BYTE) */ >>> +#define XDIR_CrtTZ 22 /* exFAT: Created timezone >>> (BYTE) */ >>> +#define XDIR_ModTZ 23 /* exFAT: Modified timezone >>> (BYTE) */ >>> +#define XDIR_AccTZ 24 /* exFAT: Last accessed >>> timezone (BYTE) */ >>> +#define XDIR_GenFlags 33 /* exFAT: General secondary >>> flags (BYTE) */ >>> +#define XDIR_NumName 35 /* exFAT: Number of file name >>> characters (BYTE) */ >>> +#define XDIR_NameHash 36 /* exFAT: Hash of file name >>> (WORD) */ >>> +#define XDIR_ValidFileSize 40 /* exFAT: Valid file size >>> (QWORD) */ >>> +#define XDIR_FstClus 52 /* exFAT: First cluster of the >>> file data (DWORD) */ >>> +#define XDIR_FileSize 56 /* exFAT: File/Directory size >>> (QWORD) */ >>> + >>> +#define SZDIRE 32 /* Size of a directory entry */ >>> +#define DDEM 0xE5 /* Deleted directory entry mark >>> set to DIR_Name[0] */ >>> +#define RDDEM 0x05 /* Replacement of the character >>> collides with DDEM */ >>> +#define LLEF 0x40 /* Last long entry flag in >>> LDIR_Ord */ >>> + >>> +#define FSI_LeadSig 0 /* FAT32 FSI: Leading >>> signature (DWORD) */ >>> +#define FSI_StrucSig 484 /* FAT32 FSI: Structure >>> signature (DWORD) */ >>> +#define FSI_Free_Count 488 /* FAT32 FSI: Number of >>> free clusters (DWORD) */ >>> +#define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated >>> cluster (DWORD) */ >>> + >>> +#define MBR_Table 446 /* MBR: Offset of partition >>> table in the MBR */ >>> +#define SZ_PTE 16 /* MBR: Size of a partition >>> table entry */ >>> +#define PTE_Boot 0 /* MBR PTE: Boot indicator */ >>> +#define PTE_StHead 1 /* MBR PTE: Start head */ >>> +#define PTE_StSec 2 /* MBR PTE: Start sector */ >>> +#define PTE_StCyl 3 /* MBR PTE: Start cylinder */ >>> +#define PTE_System 4 /* MBR PTE: System ID */ >>> +#define PTE_EdHead 5 /* MBR PTE: End head */ >>> +#define PTE_EdSec 6 /* MBR PTE: End sector */ >>> +#define PTE_EdCyl 7 /* MBR PTE: End cylinder */ >>> +#define PTE_StLba 8 /* MBR PTE: Start in LBA */ >>> +#define PTE_SizLba 12 /* MBR PTE: Size in LBA */ >>> + >>> +#define GPTH_Sign 0 /* GPT: Header signature >>> (8-byte) */ >>> +#define GPTH_Rev 8 /* GPT: Revision (DWORD) */ >>> +#define GPTH_Size 12 /* GPT: Header size (DWORD) */ >>> +#define GPTH_Bcc 16 /* GPT: Header BCC (DWORD) */ >>> +#define GPTH_CurLba 24 /* GPT: Main header LBA >>> (QWORD) */ >>> +#define GPTH_BakLba 32 /* GPT: Backup header LBA >>> (QWORD) */ >>> +#define GPTH_FstLba 40 /* GPT: First LBA for >>> partitions (QWORD) */ >>> +#define GPTH_LstLba 48 /* GPT: Last LBA for >>> partitions (QWORD) */ >>> +#define GPTH_DskGuid 56 /* GPT: Disk GUID (16-byte) */ >>> +#define GPTH_PtOfs 72 /* GPT: Partation table LBA >>> (QWORD) */ >>> +#define GPTH_PtNum 80 /* GPT: Number of table >>> entries (DWORD) */ >>> +#define GPTH_PteSize 84 /* GPT: Size of table entry >>> (DWORD) */ >>> +#define GPTH_PtBcc 88 /* GPT: Partation table BCC >>> (DWORD) */ >>> +#define SZ_GPTE 128 /* GPT: Size of partition >>> table entry */ >>> +#define GPTE_PtGuid 0 /* GPT PTE: Partition type >>> GUID (16-byte) */ >>> +#define GPTE_UpGuid 16 /* GPT PTE: Partition >>> unique GUID (16-byte) */ >>> +#define GPTE_FstLba 32 /* GPT PTE: First LBA >>> (QWORD) */ >>> +#define GPTE_LstLba 40 /* GPT PTE: Last LBA >>> inclusive (QWORD) */ >>> +#define GPTE_Flags 48 /* GPT PTE: Flags (QWORD) */ >>> +#define GPTE_Name 56 /* GPT PTE: Name */ >>> + >>> + >>> +/* Post process on fatal error in the file operations */ >>> +#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, >>> res); } >>> + >>> + >>> +/* Re-entrancy related */ >>> +#if FF_FS_REENTRANT >>> +#if FF_USE_LFN == 1 >>> +#error Static LFN work area cannot be used at thread-safe configuration >>> +#endif >>> +#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } >>> +#else >>> +#define LEAVE_FF(fs, res) return res >>> +#endif >>> + >>> + >>> +/* Definitions of logical drive - physical location conversion */ >>> +#if FF_MULTI_PARTITION >>> +#define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number */ >>> +#define LD2PT(vol) VolToPart[vol].pt /* Get partition index */ >>> +#else >>> +#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is >>> associated with the same physical drive number */ >>> +#define LD2PT(vol) 0 /* Find first valid partition or in >>> SFD */ >>> +#endif >>> + >>> + >>> +/* Definitions of sector size */ >>> +#if (FF_MAX_SS < FF_MIN_SS) || (FF_MAX_SS != 512 && FF_MAX_SS != >>> 1024 && FF_MAX_SS != 2048 && FF_MAX_SS != 4096) || (FF_MIN_SS != 512 >>> && FF_MIN_SS != 1024 && FF_MIN_SS != 2048 && FF_MIN_SS != 4096) >>> +#error Wrong sector size configuration >>> +#endif >>> +#if FF_MAX_SS == FF_MIN_SS >>> +#define SS(fs) ((UINT)FF_MAX_SS) /* Fixed sector size */ >>> +#else >>> +#define SS(fs) ((fs)->ssize) /* Variable sector size */ >>> +#endif >>> + >>> + >>> +/* Timestamp */ >>> +#if FF_FS_NORTC == 1 >>> +#if FF_NORTC_YEAR < 1980 || FF_NORTC_YEAR > 2107 || FF_NORTC_MON < 1 >>> || FF_NORTC_MON > 12 || FF_NORTC_MDAY < 1 || FF_NORTC_MDAY > 31 >>> +#error Invalid FF_FS_NORTC settings >>> +#endif >>> +#define GET_FATTIME() ((DWORD)(FF_NORTC_YEAR - 1980) << 25 | >>> (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16) >>> +#else >>> +#define GET_FATTIME() get_fattime() >>> +#endif >>> + >>> + >>> +/* File lock controls */ >>> +#if FF_FS_LOCK != 0 >>> +#if FF_FS_READONLY >>> +#error FF_FS_LOCK must be 0 at read-only configuration >>> +#endif >>> +typedef struct { >>> + FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */ >>> + DWORD clu; /* Object ID 2, containing directory (0:root) */ >>> + DWORD ofs; /* Object ID 3, offset in the directory */ >>> + WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read >>> mode open count, 0x100:write mode */ >>> +} FILESEM; >>> +#endif >>> + >>> + >>> +/* SBCS up-case tables (\x80-\xFF) */ >>> +#define TBL_CT437 >>> {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, >>> \ >>> + >>> 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>> \ >>> + >>> 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>> \ >>> + >>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>> \ >>> + >>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>> \ >>> + >>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>> \ >>> + >>> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >>> \ >>> + >>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>> >>> +#define TBL_CT720 >>> {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >>> \ >>> + >>> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>> \ >>> + >>> 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>> \ >>> + >>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>> \ >>> + >>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>> \ >>> + >>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>> \ >>> + >>> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >>> \ >>> + >>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>> >>> +#define TBL_CT737 >>> {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >>> \ >>> + >>> 0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, >>> \ >>> + >>> 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, >>> \ >>> + >>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>> \ >>> + >>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>> \ >>> + >>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>> \ >>> + >>> 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >>> \ >>> + >>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>> >>> +#define TBL_CT771 >>> {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >>> \ >>> + >>> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>> \ >>> + >>> 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >>> \ >>> + >>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>> \ >>> + >>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>> \ >>> + >>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, >>> \ >>> + >>> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>> \ >>> + >>> 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF} >>> >>> +#define TBL_CT775 >>> {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, >>> \ >>> + >>> 0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, >>> \ >>> + >>> 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>> \ >>> + >>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>> \ >>> + >>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>> \ >>> + >>> 0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>> \ >>> + >>> 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, >>> \ >>> + >>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>> >>> +#define TBL_CT850 >>> {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, >>> \ >>> + >>> 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, >>> \ >>> + >>> 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>> \ >>> + >>> 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>> \ >>> + >>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>> \ >>> + >>> 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, >>> \ >>> + >>> 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, >>> \ >>> + >>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>> >>> +#define TBL_CT852 >>> {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, >>> \ >>> + >>> 0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, >>> \ >>> + >>> 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, >>> \ >>> + >>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, >>> \ >>> + >>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>> \ >>> + >>> 0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>> \ >>> + >>> 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, >>> \ >>> + >>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} >>> >>> +#define TBL_CT855 >>> {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, >>> \ >>> + >>> 0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, >>> \ >>> + >>> 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, >>> \ >>> + >>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, >>> \ >>> + >>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>> \ >>> + >>> 0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, >>> \ >>> + >>> 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, >>> \ >>> + >>> 0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} >>> >>> +#define TBL_CT857 >>> {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, >>> \ >>> + >>> 0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, >>> \ >>> + >>> 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>> \ >>> + >>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>> \ >>> + >>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>> \ >>> + >>> 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>> \ >>> + >>> 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, >>> \ >>> + >>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>> >>> +#define TBL_CT860 >>> {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, >>> \ >>> + >>> 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>> \ >>> + >>> 0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>> \ >>> + >>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>> \ >>> + >>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>> \ >>> + >>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>> \ >>> + >>> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >>> \ >>> + >>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>> >>> +#define TBL_CT861 >>> {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, >>> \ >>> + >>> 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, >>> \ >>> + >>> 0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>> \ >>> + >>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>> \ >>> + >>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>> \ >>> + >>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>> \ >>> + >>> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >>> \ >>> + >>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>> >>> +#define TBL_CT862 >>> {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >>> \ >>> + >>> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>> \ >>> + >>> 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>> \ >>> + >>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>> \ >>> + >>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>> \ >>> + >>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>> \ >>> + >>> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >>> \ >>> + >>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>> >>> +#define TBL_CT863 >>> {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, >>> \ >>> + >>> 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, >>> \ >>> + >>> 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>> \ >>> + >>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>> \ >>> + >>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>> \ >>> + >>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>> \ >>> + >>> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >>> \ >>> + >>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>> >>> +#define TBL_CT864 >>> {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, >>> \ >>> + >>> 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>> \ >>> + >>> 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>> \ >>> + >>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>> \ >>> + >>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>> \ >>> + >>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>> \ >>> + >>> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >>> \ >>> + >>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>> >>> +#define TBL_CT865 >>> {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, >>> \ >>> + >>> 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>> \ >>> + >>> 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>> \ >>> + >>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>> \ >>> + >>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>> \ >>> + >>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>> \ >>> + >>> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >>> \ >>> + >>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>> >>> +#define TBL_CT866 >>> {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >>> \ >>> + >>> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>> \ >>> + >>> 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >>> \ >>> + >>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>> \ >>> + >>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>> \ >>> + >>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>> \ >>> + >>> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>> \ >>> + >>> 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>> >>> +#define TBL_CT869 >>> {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >>> \ >>> + >>> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, >>> \ >>> + >>> 0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>> \ >>> + >>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>> \ >>> + >>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>> \ >>> + >>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, >>> \ >>> + >>> 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, >>> \ >>> + >>> 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF} >>> >>> + >>> + >>> +/* DBCS code range |----- 1st byte -----| |----------- 2nd byte >>> -----------| */ >>> +/* <------> <------> <------> <------> >>> <------> */ >>> +#define TBL_DC932 {0x81, 0x9F, 0xE0, 0xFC, 0x40, 0x7E, 0x80, 0xFC, >>> 0x00, 0x00} >>> +#define TBL_DC936 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0x80, 0xFE, >>> 0x00, 0x00} >>> +#define TBL_DC949 {0x81, 0xFE, 0x00, 0x00, 0x41, 0x5A, 0x61, 0x7A, >>> 0x81, 0xFE} >>> +#define TBL_DC950 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0xA1, 0xFE, >>> 0x00, 0x00} >>> + >>> + >>> +/* Macros for table definitions */ >>> +#define MERGE_2STR(a, b) a ## b >>> +#define MKCVTBL(hd, cp) MERGE_2STR(hd, cp) >>> + >>> + >>> + >>> + >>> +/*-------------------------------------------------------------------------- >>> >>> + >>> + Module Private Work Area >>> + >>> +---------------------------------------------------------------------------*/ >>> >>> +/* Remark: Variables defined here without initial value shall be >>> guaranteed >>> +/ zero/null at start-up. If not, the linker option or start-up >>> routine is >>> +/ not compliance with C standard. */ >>> + >>> +/*--------------------------------*/ >>> +/* File/Volume controls */ >>> +/*--------------------------------*/ >>> + >>> +#if FF_VOLUMES < 1 || FF_VOLUMES > 10 >>> +#error Wrong FF_VOLUMES setting >>> +#endif >>> +static FATFS* FatFs[FF_VOLUMES]; /* Pointer to the filesystem >>> objects (logical drives) */ >>> +static WORD Fsid; /* Filesystem mount ID */ >>> + >>> +#if FF_FS_RPATH != 0 >>> +static BYTE CurrVol; /* Current drive */ >>> +#endif >>> + >>> +#if FF_FS_LOCK != 0 >>> +static FILESEM Files[FF_FS_LOCK]; /* Open object lock semaphores */ >>> +#endif >>> + >>> +#if FF_STR_VOLUME_ID >>> +#ifdef FF_VOLUME_STRS >>> +static const char* const VolumeStr[FF_VOLUMES] = {FF_VOLUME_STRS}; >>> /* Pre-defined volume ID */ >>> +#endif >>> +#endif >>> + >>> +#if FF_LBA64 >>> +#if FF_MIN_GPT > 0x100000000 >>> +#error Wrong FF_MIN_GPT setting >>> +#endif >>> +static const BYTE GUID_MS_Basic[16] = >>> {0xA2,0xA0,0xD0,0xEB,0xE5,0xB9,0x33,0x44,0x87,0xC0,0x68,0xB6,0xB7,0x26,0x99,0xC7}; >>> >>> +#endif >>> + >>> + >>> + >>> +/*--------------------------------*/ >>> +/* LFN/Directory working buffer */ >>> +/*--------------------------------*/ >>> + >>> +#if FF_USE_LFN == 0 /* Non-LFN configuration */ >>> +#if FF_FS_EXFAT >>> +#error LFN must be enabled when enable exFAT >>> +#endif >>> +#define DEF_NAMBUF >>> +#define INIT_NAMBUF(fs) >>> +#define FREE_NAMBUF() >>> +#define LEAVE_MKFS(res) return res >>> + >>> +#else /* LFN configurations */ >>> +#if FF_MAX_LFN < 12 || FF_MAX_LFN > 255 >>> +#error Wrong setting of FF_MAX_LFN >>> +#endif >>> +#if FF_LFN_BUF < FF_SFN_BUF || FF_SFN_BUF < 12 >>> +#error Wrong setting of FF_LFN_BUF or FF_SFN_BUF >>> +#endif >>> +#if FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3 >>> +#error Wrong setting of FF_LFN_UNICODE >>> +#endif >>> +static const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* >>> FAT: Offset of LFN characters in the directory entry */ >>> +#define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) /* exFAT: Size >>> of directory entry block scratchpad buffer needed for the name length */ >>> + >>> +#if FF_USE_LFN == 1 /* LFN enabled with static working buffer */ >>> +#if FF_FS_EXFAT >>> +static BYTE DirBuf[MAXDIRB(FF_MAX_LFN)]; /* Directory entry >>> block scratchpad buffer */ >>> +#endif >>> +static WCHAR LfnBuf[FF_MAX_LFN + 1]; /* LFN working buffer */ >>> +#define DEF_NAMBUF >>> +#define INIT_NAMBUF(fs) >>> +#define FREE_NAMBUF() >>> +#define LEAVE_MKFS(res) return res >>> + >>> +#elif FF_USE_LFN == 2 /* LFN enabled with dynamic working buffer >>> on the stack */ >>> +#if FF_FS_EXFAT >>> +#define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; BYTE >>> dbuf[MAXDIRB(FF_MAX_LFN)]; /* LFN working buffer and directory >>> entry block scratchpad buffer */ >>> +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = >>> dbuf; } >>> +#define FREE_NAMBUF() >>> +#else >>> +#define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; /* LFN >>> working buffer */ >>> +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; } >>> +#define FREE_NAMBUF() >>> +#endif >>> +#define LEAVE_MKFS(res) return res >>> + >>> +#elif FF_USE_LFN == 3 /* LFN enabled with dynamic working buffer >>> on the heap */ >>> +#if FF_FS_EXFAT >>> +#define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working >>> buffer and directory entry block scratchpad buffer */ >>> +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2 + >>> MAXDIRB(FF_MAX_LFN)); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); >>> (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+FF_MAX_LFN+1); } >>> +#define FREE_NAMBUF() ff_memfree(lfn) >>> +#else >>> +#define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working >>> buffer */ >>> +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2); if >>> (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; } >>> +#define FREE_NAMBUF() ff_memfree(lfn) >>> +#endif >>> +#define LEAVE_MKFS(res) { if (!work) ff_memfree(buf); return res; } >>> +#define MAX_MALLOC 0x8000 /* Must be >=FF_MAX_SS */ >>> + >>> +#else >>> +#error Wrong setting of FF_USE_LFN >>> + >>> +#endif /* FF_USE_LFN == 1 */ >>> +#endif /* FF_USE_LFN == 0 */ >>> + >>> + >>> + >>> +/*--------------------------------*/ >>> +/* Code conversion tables */ >>> +/*--------------------------------*/ >>> + >>> +#if FF_CODE_PAGE == 0 /* Run-time code page configuration */ >>> +#define CODEPAGE CodePage >>> +static WORD CodePage; /* Current code page */ >>> +static const BYTE *ExCvt, *DbcTbl; /* Pointer to current SBCS >>> up-case table and DBCS code range table below */ >>> + >>> +static const BYTE Ct437[] = TBL_CT437; >>> +static const BYTE Ct720[] = TBL_CT720; >>> +static const BYTE Ct737[] = TBL_CT737; >>> +static const BYTE Ct771[] = TBL_CT771; >>> +static const BYTE Ct775[] = TBL_CT775; >>> +static const BYTE Ct850[] = TBL_CT850; >>> +static const BYTE Ct852[] = TBL_CT852; >>> +static const BYTE Ct855[] = TBL_CT855; >>> +static const BYTE Ct857[] = TBL_CT857; >>> +static const BYTE Ct860[] = TBL_CT860; >>> +static const BYTE Ct861[] = TBL_CT861; >>> +static const BYTE Ct862[] = TBL_CT862; >>> +static const BYTE Ct863[] = TBL_CT863; >>> +static const BYTE Ct864[] = TBL_CT864; >>> +static const BYTE Ct865[] = TBL_CT865; >>> +static const BYTE Ct866[] = TBL_CT866; >>> +static const BYTE Ct869[] = TBL_CT869; >>> +static const BYTE Dc932[] = TBL_DC932; >>> +static const BYTE Dc936[] = TBL_DC936; >>> +static const BYTE Dc949[] = TBL_DC949; >>> +static const BYTE Dc950[] = TBL_DC950; >>> + >>> +#elif FF_CODE_PAGE < 900 /* Static code page configuration (SBCS) */ >>> +#define CODEPAGE FF_CODE_PAGE >>> +static const BYTE ExCvt[] = MKCVTBL(TBL_CT, FF_CODE_PAGE); >>> + >>> +#else /* Static code page configuration (DBCS) */ >>> +#define CODEPAGE FF_CODE_PAGE >>> +static const BYTE DbcTbl[] = MKCVTBL(TBL_DC, FF_CODE_PAGE); >>> + >>> +#endif >>> + >>> + >>> + >>> + >>> +/*-------------------------------------------------------------------------- >>> >>> + >>> + Module Private Functions >>> + >>> +---------------------------------------------------------------------------*/ >>> >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Load/Store multi-byte word in the FAT >>> structure */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static WORD ld_word (const BYTE* ptr) /* Load a 2-byte >>> little-endian word */ >>> +{ >>> + WORD rv; >>> + >>> + rv = ptr[1]; >>> + rv = rv << 8 | ptr[0]; >>> + return rv; >>> +} >>> + >>> +static DWORD ld_dword (const BYTE* ptr) /* Load a 4-byte >>> little-endian word */ >>> +{ >>> + DWORD rv; >>> + >>> + rv = ptr[3]; >>> + rv = rv << 8 | ptr[2]; >>> + rv = rv << 8 | ptr[1]; >>> + rv = rv << 8 | ptr[0]; >>> + return rv; >>> +} >>> + >>> +#if FF_FS_EXFAT >>> +static QWORD ld_qword (const BYTE* ptr) /* Load an 8-byte >>> little-endian word */ >>> +{ >>> + QWORD rv; >>> + >>> + rv = ptr[7]; >>> + rv = rv << 8 | ptr[6]; >>> + rv = rv << 8 | ptr[5]; >>> + rv = rv << 8 | ptr[4]; >>> + rv = rv << 8 | ptr[3]; >>> + rv = rv << 8 | ptr[2]; >>> + rv = rv << 8 | ptr[1]; >>> + rv = rv << 8 | ptr[0]; >>> + return rv; >>> +} >>> +#endif >>> + >>> +#if !FF_FS_READONLY >>> +static void st_word (BYTE* ptr, WORD val) /* Store a 2-byte word >>> in little-endian */ >>> +{ >>> + *ptr++ = (BYTE)val; val >>= 8; >>> + *ptr++ = (BYTE)val; >>> +} >>> + >>> +static void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte >>> word in little-endian */ >>> +{ >>> + *ptr++ = (BYTE)val; val >>= 8; >>> + *ptr++ = (BYTE)val; val >>= 8; >>> + *ptr++ = (BYTE)val; val >>= 8; >>> + *ptr++ = (BYTE)val; >>> +} >>> + >>> +#if FF_FS_EXFAT >>> +static void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte >>> word in little-endian */ >>> +{ >>> + *ptr++ = (BYTE)val; val >>= 8; >>> + *ptr++ = (BYTE)val; val >>= 8; >>> + *ptr++ = (BYTE)val; val >>= 8; >>> + *ptr++ = (BYTE)val; val >>= 8; >>> + *ptr++ = (BYTE)val; val >>= 8; >>> + *ptr++ = (BYTE)val; val >>= 8; >>> + *ptr++ = (BYTE)val; val >>= 8; >>> + *ptr++ = (BYTE)val; >>> +} >>> +#endif >>> +#endif /* !FF_FS_READONLY */ >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* String >>> functions */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +/* Copy memory to memory */ >>> +static void mem_cpy (void* dst, const void* src, UINT cnt) >>> +{ >>> + BYTE *d = (BYTE*)dst; >>> + const BYTE *s = (const BYTE*)src; >>> + >>> + if (cnt != 0) { >>> + do { >>> + *d++ = *s++; >>> + } while (--cnt); >>> + } >>> +} >>> + >>> + >>> +/* Fill memory block */ >>> +static void mem_set (void* dst, int val, UINT cnt) >>> +{ >>> + BYTE *d = (BYTE*)dst; >>> + >>> + do { >>> + *d++ = (BYTE)val; >>> + } while (--cnt); >>> +} >>> + >>> + >>> +/* Compare memory block */ >>> +static int mem_cmp (const void* dst, const void* src, UINT cnt) >>> /* ZR:same, NZ:different */ >>> +{ >>> + const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src; >>> + int r = 0; >>> + >>> + do { >>> + r = *d++ - *s++; >>> + } while (--cnt && r == 0); >>> + >>> + return r; >>> +} >>> + >>> + >>> +/* Check if chr is contained in the string */ >>> +static int chk_chr (const char* str, int chr) /* NZ:contained, >>> ZR:not contained */ >>> +{ >>> + while (*str && *str != chr) str++; >>> + return *str; >>> +} >>> + >>> + >>> +/* Test if the byte is DBC 1st byte */ >>> +static int dbc_1st (BYTE c) >>> +{ >>> +#if FF_CODE_PAGE == 0 /* Variable code page */ >>> + if (DbcTbl && c >= DbcTbl[0]) { >>> + if (c <= DbcTbl[1]) return 1; /* 1st byte >>> range 1 */ >>> + if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1; /* 1st >>> byte range 2 */ >>> + } >>> +#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */ >>> + if (c >= DbcTbl[0]) { >>> + if (c <= DbcTbl[1]) return 1; >>> + if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1; >>> + } >>> +#else /* SBCS fixed code page */ >>> + if (c != 0) return 0; /* Always false */ >>> +#endif >>> + return 0; >>> +} >>> + >>> + >>> +/* Test if the byte is DBC 2nd byte */ >>> +static int dbc_2nd (BYTE c) >>> +{ >>> +#if FF_CODE_PAGE == 0 /* Variable code page */ >>> + if (DbcTbl && c >= DbcTbl[4]) { >>> + if (c <= DbcTbl[5]) return 1; /* 2nd byte >>> range 1 */ >>> + if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1; /* 2nd >>> byte range 2 */ >>> + if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1; /* 2nd >>> byte range 3 */ >>> + } >>> +#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */ >>> + if (c >= DbcTbl[4]) { >>> + if (c <= DbcTbl[5]) return 1; >>> + if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1; >>> + if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1; >>> + } >>> +#else /* SBCS fixed code page */ >>> + if (c != 0) return 0; /* Always false */ >>> +#endif >>> + return 0; >>> +} >>> + >>> + >>> +#if FF_USE_LFN >>> + >>> +/* Get a Unicode code point from the TCHAR string in defined API >>> encodeing */ >>> +static DWORD tchar2uni ( /* Returns a character in UTF-16 >>> encoding (>=0x10000 on surrogate pair, 0xFFFFFFFF on decode error) */ >>> + const TCHAR** str /* Pointer to pointer to TCHAR string >>> in configured encoding */ >>> +) >>> +{ >>> + DWORD uc; >>> + const TCHAR *p = *str; >>> + >>> +#if FF_LFN_UNICODE == 1 /* UTF-16 input */ >>> + WCHAR wc; >>> + >>> + uc = *p++; /* Get a unit */ >>> + if (IsSurrogate(uc)) { /* Surrogate? */ >>> + wc = *p++; /* Get low surrogate */ >>> + if (!IsSurrogateH(uc) || !IsSurrogateL(wc)) return >>> 0xFFFFFFFF; /* Wrong surrogate? */ >>> + uc = uc << 16 | wc; >>> + } >>> + >>> +#elif FF_LFN_UNICODE == 2 /* UTF-8 input */ >>> + BYTE b; >>> + int nf; >>> + >>> + uc = (BYTE)*p++; /* Get an encoding unit */ >>> + if (uc & 0x80) { /* Multiple byte code? */ >>> + if ((uc & 0xE0) == 0xC0) { /* 2-byte sequence? */ >>> + uc &= 0x1F; nf = 1; >>> + } else { >>> + if ((uc & 0xF0) == 0xE0) { /* 3-byte sequence? */ >>> + uc &= 0x0F; nf = 2; >>> + } else { >>> + if ((uc & 0xF8) == 0xF0) { /* 4-byte sequence? */ >>> + uc &= 0x07; nf = 3; >>> + } else { /* Wrong sequence */ >>> + return 0xFFFFFFFF; >>> + } >>> + } >>> + } >>> + do { /* Get trailing bytes */ >>> + b = (BYTE)*p++; >>> + if ((b & 0xC0) != 0x80) return 0xFFFFFFFF; /* Wrong >>> sequence? */ >>> + uc = uc << 6 | (b & 0x3F); >>> + } while (--nf != 0); >>> + if (uc < 0x80 || IsSurrogate(uc) || uc >= 0x110000) return >>> 0xFFFFFFFF; /* Wrong code? */ >>> + if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & >>> 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */ >>> + } >>> + >>> +#elif FF_LFN_UNICODE == 3 /* UTF-32 input */ >>> + uc = (TCHAR)*p++; /* Get a unit */ >>> + if (uc >= 0x110000 || IsSurrogate(uc)) return 0xFFFFFFFF; /* >>> Wrong code? */ >>> + if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & >>> 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */ >>> + >>> +#else /* ANSI/OEM input */ >>> + BYTE b; >>> + WCHAR wc; >>> + >>> + wc = (BYTE)*p++; /* Get a byte */ >>> + if (dbc_1st((BYTE)wc)) { /* Is it a DBC 1st byte? */ >>> + b = (BYTE)*p++; /* Get 2nd byte */ >>> + if (!dbc_2nd(b)) return 0xFFFFFFFF; /* Invalid code? */ >>> + wc = (wc << 8) + b; /* Make a DBC */ >>> + } >>> + if (wc != 0) { >>> + wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM ==> Unicode */ >>> + if (wc == 0) return 0xFFFFFFFF; /* Invalid code? */ >>> + } >>> + uc = wc; >>> + >>> +#endif >>> + *str = p; /* Next read pointer */ >>> + return uc; >>> +} >>> + >>> + >>> +/* Output a TCHAR string in defined API encoding */ >>> +static BYTE put_utf ( /* Returns number of encoding units written >>> (0:buffer overflow or wrong encoding) */ >>> + DWORD chr, /* UTF-16 encoded character (Surrogate pair if >>> >=0x10000) */ >>> + TCHAR* buf, /* Output buffer */ >>> + UINT szb /* Size of the buffer */ >>> +) >>> +{ >>> +#if FF_LFN_UNICODE == 1 /* UTF-16 output */ >>> + WCHAR hs, wc; >>> + >>> + hs = (WCHAR)(chr >> 16); >>> + wc = (WCHAR)chr; >>> + if (hs == 0) { /* Single encoding unit? */ >>> + if (szb < 1 || IsSurrogate(wc)) return 0; /* Buffer >>> overflow or wrong code? */ >>> + *buf = wc; >>> + return 1; >>> + } >>> + if (szb < 2 || !IsSurrogateH(hs) || !IsSurrogateL(wc)) return >>> 0; /* Buffer overflow or wrong surrogate? */ >>> + *buf++ = hs; >>> + *buf++ = wc; >>> + return 2; >>> + >>> +#elif FF_LFN_UNICODE == 2 /* UTF-8 output */ >>> + DWORD hc; >>> + >>> + if (chr < 0x80) { /* Single byte code? */ >>> + if (szb < 1) return 0; /* Buffer overflow? */ >>> + *buf = (TCHAR)chr; >>> + return 1; >>> + } >>> + if (chr < 0x800) { /* 2-byte sequence? */ >>> + if (szb < 2) return 0; /* Buffer overflow? */ >>> + *buf++ = (TCHAR)(0xC0 | (chr >> 6 & 0x1F)); >>> + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); >>> + return 2; >>> + } >>> + if (chr < 0x10000) { /* 3-byte sequence? */ >>> + if (szb < 3 || IsSurrogate(chr)) return 0; /* Buffer >>> overflow or wrong code? */ >>> + *buf++ = (TCHAR)(0xE0 | (chr >> 12 & 0x0F)); >>> + *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F)); >>> + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); >>> + return 3; >>> + } >>> + /* 4-byte sequence */ >>> + if (szb < 4) return 0; /* Buffer overflow? */ >>> + hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high 10 >>> bits */ >>> + chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 >>> bits */ >>> + if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong >>> surrogate? */ >>> + chr = (hc | chr) + 0x10000; >>> + *buf++ = (TCHAR)(0xF0 | (chr >> 18 & 0x07)); >>> + *buf++ = (TCHAR)(0x80 | (chr >> 12 & 0x3F)); >>> + *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F)); >>> + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); >>> + return 4; >>> + >>> +#elif FF_LFN_UNICODE == 3 /* UTF-32 output */ >>> + DWORD hc; >>> + >>> + if (szb < 1) return 0; /* Buffer overflow? */ >>> + if (chr >= 0x10000) { /* Out of BMP? */ >>> + hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high >>> 10 bits */ >>> + chr = (chr & 0xFFFF) - 0xDC00; /* Get low >>> 10 bits */ >>> + if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong >>> surrogate? */ >>> + chr = (hc | chr) + 0x10000; >>> + } >>> + *buf++ = (TCHAR)chr; >>> + return 1; >>> + >>> +#else /* ANSI/OEM output */ >>> + WCHAR wc; >>> + >>> + wc = ff_uni2oem(chr, CODEPAGE); >>> + if (wc >= 0x100) { /* Is this a DBC? */ >>> + if (szb < 2) return 0; >>> + *buf++ = (char)(wc >> 8); /* Store DBC 1st byte */ >>> + *buf++ = (TCHAR)wc; /* Store DBC 2nd byte */ >>> + return 2; >>> + } >>> + if (wc == 0 || szb < 1) return 0; /* Invalid char or buffer >>> overflow? */ >>> + *buf++ = (TCHAR)wc; /* Store the character */ >>> + return 1; >>> +#endif >>> +} >>> +#endif /* FF_USE_LFN */ >>> + >>> + >>> +#if FF_FS_REENTRANT >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Request/Release grant to access the >>> volume */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> +static int lock_fs ( /* 1:Ok, 0:timeout */ >>> + FATFS* fs /* Filesystem object */ >>> +) >>> +{ >>> + return ff_req_grant(fs->sobj); >>> +} >>> + >>> + >>> +static void unlock_fs ( >>> + FATFS* fs, /* Filesystem object */ >>> + FRESULT res /* Result code to be returned */ >>> +) >>> +{ >>> + if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && >>> res != FR_TIMEOUT) { >>> + ff_rel_grant(fs->sobj); >>> + } >>> +} >>> + >>> +#endif >>> + >>> + >>> + >>> +#if FF_FS_LOCK != 0 >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* File lock control >>> functions */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static FRESULT chk_lock ( /* Check if the file can be accessed */ >>> + DIR* dp, /* Directory object pointing the file to be >>> checked */ >>> + int acc /* Desired access type (0:Read mode open, >>> 1:Write mode open, 2:Delete or rename) */ >>> +) >>> +{ >>> + UINT i, be; >>> + >>> + /* Search open object table for the object */ >>> + be = 0; >>> + for (i = 0; i < FF_FS_LOCK; i++) { >>> + if (Files[i].fs) { /* Existing entry */ >>> + if (Files[i].fs == dp->obj.fs && /* Check if the >>> object matches with an open object */ >>> + Files[i].clu == dp->obj.sclust && >>> + Files[i].ofs == dp->dptr) break; >>> + } else { /* Blank entry */ >>> + be = 1; >>> + } >>> + } >>> + if (i == FF_FS_LOCK) { /* The object has not been opened */ >>> + return (!be && acc != 2) ? FR_TOO_MANY_OPEN_FILES : FR_OK; >>> /* Is there a blank entry for new object? */ >>> + } >>> + >>> + /* The object was opened. Reject any open against writing file >>> and all write mode open */ >>> + return (acc != 0 || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; >>> +} >>> + >>> + >>> +static int enq_lock (void) /* Check if an entry is available for >>> a new object */ >>> +{ >>> + UINT i; >>> + >>> + for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ; >>> + return (i == FF_FS_LOCK) ? 0 : 1; >>> +} >>> + >>> + >>> +static UINT inc_lock ( /* Increment object open counter and >>> returns its index (0:Internal error) */ >>> + DIR* dp, /* Directory object pointing the file to register or >>> increment */ >>> + int acc /* Desired access (0:Read, 1:Write, >>> 2:Delete/Rename) */ >>> +) >>> +{ >>> + UINT i; >>> + >>> + >>> + for (i = 0; i < FF_FS_LOCK; i++) { /* Find the object */ >>> + if (Files[i].fs == dp->obj.fs >>> + && Files[i].clu == dp->obj.sclust >>> + && Files[i].ofs == dp->dptr) break; >>> + } >>> + >>> + if (i == FF_FS_LOCK) { /* Not opened. Register it >>> as new. */ >>> + for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ; >>> + if (i == FF_FS_LOCK) return 0; /* No free entry to >>> register (int err) */ >>> + Files[i].fs = dp->obj.fs; >>> + Files[i].clu = dp->obj.sclust; >>> + Files[i].ofs = dp->dptr; >>> + Files[i].ctr = 0; >>> + } >>> + >>> + if (acc >= 1 && Files[i].ctr) return 0; /* Access violation >>> (int err) */ >>> + >>> + Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set >>> semaphore value */ >>> + >>> + return i + 1; /* Index number origin from 1 */ >>> +} >>> + >>> + >>> +static FRESULT dec_lock ( /* Decrement object open counter */ >>> + UINT i /* Semaphore index (1..) */ >>> +) >>> +{ >>> + WORD n; >>> + FRESULT res; >>> + >>> + >>> + if (--i < FF_FS_LOCK) { /* Index number origin from 0 */ >>> + n = Files[i].ctr; >>> + if (n == 0x100) n = 0; /* If write mode open, delete >>> the entry */ >>> + if (n > 0) n--; /* Decrement read mode open >>> count */ >>> + Files[i].ctr = n; >>> + if (n == 0) Files[i].fs = 0; /* Delete the entry if open >>> count gets zero */ >>> + res = FR_OK; >>> + } else { >>> + res = FR_INT_ERR; /* Invalid index nunber */ >>> + } >>> + return res; >>> +} >>> + >>> + >>> +static void clear_lock ( /* Clear lock entries of the volume */ >>> + FATFS *fs >>> +) >>> +{ >>> + UINT i; >>> + >>> + for (i = 0; i < FF_FS_LOCK; i++) { >>> + if (Files[i].fs == fs) Files[i].fs = 0; >>> + } >>> +} >>> + >>> +#endif /* FF_FS_LOCK != 0 */ >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Move/Flush disk access window in the filesystem >>> object */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> +#if !FF_FS_READONLY >>> +static FRESULT sync_window ( /* Returns FR_OK or FR_DISK_ERR */ >>> + FATFS* fs /* Filesystem object */ >>> +) >>> +{ >>> + FRESULT res = FR_OK; >>> + >>> + >>> + if (fs->wflag) { /* Is the disk access window dirty? */ >>> + if (disk_write(fs->pdrv, fs->win, fs->winsect, 1) == RES_OK) >>> { /* Write it back into the volume */ >>> + fs->wflag = 0; /* Clear window dirty flag */ >>> + if (fs->winsect - fs->fatbase < fs->fsize) { /* Is it >>> in the 1st FAT? */ >>> + if (fs->n_fats == 2) disk_write(fs->pdrv, fs->win, >>> fs->winsect + fs->fsize, 1); /* Reflect it to 2nd FAT if needed */ >>> + } >>> + } else { >>> + res = FR_DISK_ERR; >>> + } >>> + } >>> + return res; >>> +} >>> +#endif >>> + >>> + >>> +static FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERR */ >>> + FATFS* fs, /* Filesystem object */ >>> + LBA_t sect /* Sector LBA to make appearance in the >>> fs->win[] */ >>> +) >>> +{ >>> + FRESULT res = FR_OK; >>> + >>> + >>> + if (sect != fs->winsect) { /* Window offset changed? */ >>> +#if !FF_FS_READONLY >>> + res = sync_window(fs); /* Flush the window */ >>> +#endif >>> + if (res == FR_OK) { /* Fill sector window with >>> new data */ >>> + if (disk_read(fs->pdrv, fs->win, sect, 1) != RES_OK) { >>> + sect = (LBA_t)0 - 1; /* Invalidate window if read >>> data is not valid */ >>> + res = FR_DISK_ERR; >>> + } >>> + fs->winsect = sect; >>> + } >>> + } >>> + return res; >>> +} >>> + >>> + >>> + >>> + >>> +#if !FF_FS_READONLY >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Synchronize filesystem and data on the >>> storage */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */ >>> + FATFS* fs /* Filesystem object */ >>> +) >>> +{ >>> + FRESULT res; >>> + >>> + >>> + res = sync_window(fs); >>> + if (res == FR_OK) { >>> + if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { /* >>> FAT32: Update FSInfo sector if needed */ >>> + /* Create FSInfo structure */ >>> + mem_set(fs->win, 0, sizeof fs->win); >>> + st_word(fs->win + BS_55AA, 0xAA55); >>> /* Boot signature */ >>> + st_dword(fs->win + FSI_LeadSig, 0x41615252); /* >>> Leading signature */ >>> + st_dword(fs->win + FSI_StrucSig, 0x61417272); /* >>> Structure signature */ >>> + st_dword(fs->win + FSI_Free_Count, fs->free_clst); /* >>> Number of free clusters */ >>> + st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); /* >>> Last allocated culuster */ >>> + fs->winsect = fs->volbase + 1; /* >>> Write it into the FSInfo sector (Next to VBR) */ >>> + disk_write(fs->pdrv, fs->win, fs->winsect, 1); >>> + fs->fsi_flag = 0; >>> + } >>> + /* Make sure that no pending write process in the lower >>> layer */ >>> + if (disk_ioctl(fs->pdrv, CTRL_SYNC, 0) != RES_OK) res = >>> FR_DISK_ERR; >>> + } >>> + >>> + return res; >>> +} >>> + >>> +#endif >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Get physical sector number from cluster >>> number */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static LBA_t clst2sect ( /* !=0:Sector number, 0:Failed (invalid >>> cluster#) */ >>> + FATFS* fs, /* Filesystem object */ >>> + DWORD clst /* Cluster# to be converted */ >>> +) >>> +{ >>> + clst -= 2; /* Cluster number is origin from 2 */ >>> + if (clst >= fs->n_fatent - 2) return 0; /* Is it invalid >>> cluster number? */ >>> + return fs->database + (LBA_t)fs->csize * clst; /* Start >>> sector number of the cluster */ >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* FAT access - Read value of a FAT >>> entry */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal >>> error, 2..0x7FFFFFFF:Cluster status */ >>> + FFOBJID* obj, /* Corresponding object */ >>> + DWORD clst /* Cluster number to get the value */ >>> +) >>> +{ >>> + UINT wc, bc; >>> + DWORD val; >>> + FATFS *fs = obj->fs; >>> + >>> + >>> + if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid >>> range */ >>> + val = 1; /* Internal error */ >>> + >>> + } else { >>> + val = 0xFFFFFFFF; /* Default value falls on disk error */ >>> + >>> + switch (fs->fs_type) { >>> + case FS_FAT12 : >>> + bc = (UINT)clst; bc += bc / 2; >>> + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != >>> FR_OK) break; >>> + wc = fs->win[bc++ % SS(fs)]; /* Get 1st byte of >>> the entry */ >>> + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != >>> FR_OK) break; >>> + wc |= fs->win[bc % SS(fs)] << 8; /* Merge 2nd byte of >>> the entry */ >>> + val = (clst & 1) ? (wc >> 4) : (wc & 0xFFF); /* >>> Adjust bit position */ >>> + break; >>> + >>> + case FS_FAT16 : >>> + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) >>> != FR_OK) break; >>> + val = ld_word(fs->win + clst * 2 % SS(fs)); /* >>> Simple WORD array */ >>> + break; >>> + >>> + case FS_FAT32 : >>> + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) >>> != FR_OK) break; >>> + val = ld_dword(fs->win + clst * 4 % SS(fs)) & >>> 0x0FFFFFFF; /* Simple DWORD array but mask out upper 4 bits */ >>> + break; >>> +#if FF_FS_EXFAT >>> + case FS_EXFAT : >>> + if ((obj->objsize != 0 && obj->sclust != 0) || obj->stat >>> == 0) { /* Object except root dir must have valid data length */ >>> + DWORD cofs = clst - obj->sclust; /* Offset from >>> start cluster */ >>> + DWORD clen = (DWORD)((LBA_t)((obj->objsize - 1) / >>> SS(fs)) / fs->csize); /* Number of clusters - 1 */ >>> + >>> + if (obj->stat == 2 && cofs <= clen) { /* Is it a >>> contiguous chain? */ >>> + val = (cofs == clen) ? 0x7FFFFFFF : clst + 1; /* >>> No data on the FAT, generate the value */ >>> + break; >>> + } >>> + if (obj->stat == 3 && cofs < obj->n_cont) { /* Is >>> it in the 1st fragment? */ >>> + val = clst + 1; /* Generate the value */ >>> + break; >>> + } >>> + if (obj->stat != 2) { /* Get value from FAT if >>> FAT chain is valid */ >>> + if (obj->n_frag != 0) { /* Is it on the >>> growing edge? */ >>> + val = 0x7FFFFFFF; /* Generate EOC */ >>> + } else { >>> + if (move_window(fs, fs->fatbase + (clst / >>> (SS(fs) / 4))) != FR_OK) break; >>> + val = ld_dword(fs->win + clst * 4 % SS(fs)) >>> & 0x7FFFFFFF; >>> + } >>> + break; >>> + } >>> + } >>> + val = 1; /* Internal error */ >>> + break; >>> +#endif >>> + default: >>> + val = 1; /* Internal error */ >>> + } >>> + } >>> + >>> + return val; >>> +} >>> + >>> + >>> + >>> + >>> +#if !FF_FS_READONLY >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* FAT access - Change value of a FAT >>> entry */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ >>> + FATFS* fs, /* Corresponding filesystem object */ >>> + DWORD clst, /* FAT index number (cluster number) to be >>> changed */ >>> + DWORD val /* New value to be set to the entry */ >>> +) >>> +{ >>> + UINT bc; >>> + BYTE *p; >>> + FRESULT res = FR_INT_ERR; >>> + >>> + >>> + if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid >>> range */ >>> + switch (fs->fs_type) { >>> + case FS_FAT12: >>> + bc = (UINT)clst; bc += bc / 2; /* bc: byte offset of >>> the entry */ >>> + res = move_window(fs, fs->fatbase + (bc / SS(fs))); >>> + if (res != FR_OK) break; >>> + p = fs->win + bc++ % SS(fs); >>> + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : >>> (BYTE)val; /* Update 1st byte */ >>> + fs->wflag = 1; >>> + res = move_window(fs, fs->fatbase + (bc / SS(fs))); >>> + if (res != FR_OK) break; >>> + p = fs->win + bc % SS(fs); >>> + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | >>> ((BYTE)(val >> 8) & 0x0F)); /* Update 2nd byte */ >>> + fs->wflag = 1; >>> + break; >>> + >>> + case FS_FAT16: >>> + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); >>> + if (res != FR_OK) break; >>> + st_word(fs->win + clst * 2 % SS(fs), (WORD)val); /* >>> Simple WORD array */ >>> + fs->wflag = 1; >>> + break; >>> + >>> + case FS_FAT32: >>> +#if FF_FS_EXFAT >>> + case FS_EXFAT: >>> +#endif >>> + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); >>> + if (res != FR_OK) break; >>> + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { >>> + val = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst >>> * 4 % SS(fs)) & 0xF0000000); >>> + } >>> + st_dword(fs->win + clst * 4 % SS(fs), val); >>> + fs->wflag = 1; >>> + break; >>> + } >>> + } >>> + return res; >>> +} >>> + >>> +#endif /* !FF_FS_READONLY */ >>> + >>> + >>> + >>> + >>> +#if FF_FS_EXFAT && !FF_FS_READONLY >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* exFAT: Accessing FAT and Allocation >>> Bitmap */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +/*--------------------------------------*/ >>> +/* Find a contiguous free cluster block */ >>> +/*--------------------------------------*/ >>> + >>> +static DWORD find_bitmap ( /* 0:Not found, 2..:Cluster block >>> found, 0xFFFFFFFF:Disk error */ >>> + FATFS* fs, /* Filesystem object */ >>> + DWORD clst, /* Cluster number to scan from */ >>> + DWORD ncl /* Number of contiguous clusters to find (1..) */ >>> +) >>> +{ >>> + BYTE bm, bv; >>> + UINT i; >>> + DWORD val, scl, ctr; >>> + >>> + >>> + clst -= 2; /* The first bit in the bitmap corresponds to >>> cluster #2 */ >>> + if (clst >= fs->n_fatent - 2) clst = 0; >>> + scl = val = clst; ctr = 0; >>> + for (;;) { >>> + if (move_window(fs, fs->bitbase + val / 8 / SS(fs)) != >>> FR_OK) return 0xFFFFFFFF; >>> + i = val / 8 % SS(fs); bm = 1 << (val % 8); >>> + do { >>> + do { >>> + bv = fs->win[i] & bm; bm <<= 1; /* Get bit >>> value */ >>> + if (++val >= fs->n_fatent - 2) { /* Next cluster >>> (with wrap-around) */ >>> + val = 0; bm = 0; i = SS(fs); >>> + } >>> + if (bv == 0) { /* Is it a free cluster? */ >>> + if (++ctr == ncl) return scl + 2; /* Check if >>> run length is sufficient for required */ >>> + } else { >>> + scl = val; ctr = 0; /* Encountered a >>> cluster in-use, restart to scan */ >>> + } >>> + if (val == clst) return 0; /* All cluster >>> scanned? */ >>> + } while (bm != 0); >>> + bm = 1; >>> + } while (++i < SS(fs)); >>> + } >>> +} >>> + >>> + >>> +/*----------------------------------------*/ >>> +/* Set/Clear a block of allocation bitmap */ >>> +/*----------------------------------------*/ >>> + >>> +static FRESULT change_bitmap ( >>> + FATFS* fs, /* Filesystem object */ >>> + DWORD clst, /* Cluster number to change from */ >>> + DWORD ncl, /* Number of clusters to be changed */ >>> + int bv /* bit value to be set (0 or 1) */ >>> +) >>> +{ >>> + BYTE bm; >>> + UINT i; >>> + LBA_t sect; >>> + >>> + >>> + clst -= 2; /* The first bit corresponds to cluster #2 */ >>> + sect = fs->bitbase + clst / 8 / SS(fs); /* Sector address */ >>> + i = clst / 8 % SS(fs); /* Byte offset in the >>> sector */ >>> + bm = 1 << (clst % 8); /* Bit mask in the byte */ >>> + for (;;) { >>> + if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR; >>> + do { >>> + do { >>> + if (bv == (int)((fs->win[i] & bm) != 0)) return >>> FR_INT_ERR; /* Is the bit expected value? */ >>> + fs->win[i] ^= bm; /* Flip the bit */ >>> + fs->wflag = 1; >>> + if (--ncl == 0) return FR_OK; /* All bits >>> processed? */ >>> + } while (bm <<= 1); /* Next bit */ >>> + bm = 1; >>> + } while (++i < SS(fs)); /* Next byte */ >>> + i = 0; >>> + } >>> +} >>> + >>> + >>> +/*---------------------------------------------*/ >>> +/* Fill the first fragment of the FAT chain */ >>> +/*---------------------------------------------*/ >>> + >>> +static FRESULT fill_first_frag ( >>> + FFOBJID* obj /* Pointer to the corresponding object */ >>> +) >>> +{ >>> + FRESULT res; >>> + DWORD cl, n; >>> + >>> + >>> + if (obj->stat == 3) { /* Has the object been changed >>> 'fragmented' in this session? */ >>> + for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) { >>> /* Create cluster chain on the FAT */ >>> + res = put_fat(obj->fs, cl, cl + 1); >>> + if (res != FR_OK) return res; >>> + } >>> + obj->stat = 0; /* Change status 'FAT chain is valid' */ >>> + } >>> + return FR_OK; >>> +} >>> + >>> + >>> +/*---------------------------------------------*/ >>> +/* Fill the last fragment of the FAT chain */ >>> +/*---------------------------------------------*/ >>> + >>> +static FRESULT fill_last_frag ( >>> + FFOBJID* obj, /* Pointer to the corresponding object */ >>> + DWORD lcl, /* Last cluster of the fragment */ >>> + DWORD term /* Value to set the last FAT entry */ >>> +) >>> +{ >>> + FRESULT res; >>> + >>> + >>> + while (obj->n_frag > 0) { /* Create the chain of last >>> fragment */ >>> + res = put_fat(obj->fs, lcl - obj->n_frag + 1, (obj->n_frag > >>> 1) ? lcl - obj->n_frag + 2 : term); >>> + if (res != FR_OK) return res; >>> + obj->n_frag--; >>> + } >>> + return FR_OK; >>> +} >>> + >>> +#endif /* FF_FS_EXFAT && !FF_FS_READONLY */ >>> + >>> + >>> + >>> +#if !FF_FS_READONLY >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* FAT handling - Remove a cluster >>> chain */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ >>> + FFOBJID* obj, /* Corresponding object */ >>> + DWORD clst, /* Cluster to remove a chain from */ >>> + DWORD pclst /* Previous cluster of clst (0 if entire >>> chain) */ >>> +) >>> +{ >>> + FRESULT res = FR_OK; >>> + DWORD nxt; >>> + FATFS *fs = obj->fs; >>> +#if FF_FS_EXFAT || FF_USE_TRIM >>> + DWORD scl = clst, ecl = clst; >>> +#endif >>> +#if FF_USE_TRIM >>> + LBA_t rt[2]; >>> +#endif >>> + >>> + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* >>> Check if in valid range */ >>> + >>> + /* Mark the previous cluster 'EOC' on the FAT if it exists */ >>> + if (pclst != 0 && (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT || >>> obj->stat != 2)) { >>> + res = put_fat(fs, pclst, 0xFFFFFFFF); >>> + if (res != FR_OK) return res; >>> + } >>> + >>> + /* Remove the chain */ >>> + do { >>> + nxt = get_fat(obj, clst); /* Get cluster status */ >>> + if (nxt == 0) break; /* Empty cluster? */ >>> + if (nxt == 1) return FR_INT_ERR; /* Internal error? */ >>> + if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error? */ >>> + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { >>> + res = put_fat(fs, clst, 0); /* Mark the cluster >>> 'free' on the FAT */ >>> + if (res != FR_OK) return res; >>> + } >>> + if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */ >>> + fs->free_clst++; >>> + fs->fsi_flag |= 1; >>> + } >>> +#if FF_FS_EXFAT || FF_USE_TRIM >>> + if (ecl + 1 == nxt) { /* Is next cluster contiguous? */ >>> + ecl = nxt; >>> + } else { /* End of contiguous cluster block */ >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { >>> + res = change_bitmap(fs, scl, ecl - scl + 1, 0); >>> /* Mark the cluster block 'free' on the bitmap */ >>> + if (res != FR_OK) return res; >>> + } >>> +#endif >>> +#if FF_USE_TRIM >>> + rt[0] = clst2sect(fs, scl); /* Start >>> of data area to be freed */ >>> + rt[1] = clst2sect(fs, ecl) + fs->csize - 1; /* End of >>> data area to be freed */ >>> + disk_ioctl(fs->pdrv, CTRL_TRIM, rt); /* Inform >>> storage device that the data in the block may be erased */ >>> +#endif >>> + scl = ecl = nxt; >>> + } >>> +#endif >>> + clst = nxt; /* Next cluster */ >>> + } while (clst < fs->n_fatent); /* Repeat while not the last >>> link */ >>> + >>> +#if FF_FS_EXFAT >>> + /* Some post processes for chain status */ >>> + if (fs->fs_type == FS_EXFAT) { >>> + if (pclst == 0) { /* Has the entire chain been removed? */ >>> + obj->stat = 0; /* Change the chain status >>> 'initial' */ >>> + } else { >>> + if (obj->stat == 0) { /* Is it a fragmented chain >>> from the beginning of this session? */ >>> + clst = obj->sclust; /* Follow the chain to >>> check if it gets contiguous */ >>> + while (clst != pclst) { >>> + nxt = get_fat(obj, clst); >>> + if (nxt < 2) return FR_INT_ERR; >>> + if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; >>> + if (nxt != clst + 1) break; /* Not >>> contiguous? */ >>> + clst++; >>> + } >>> + if (clst == pclst) { /* Has the chain got >>> contiguous again? */ >>> + obj->stat = 2; /* Change the chain status >>> 'contiguous' */ >>> + } >>> + } else { >>> + if (obj->stat == 3 && pclst >= obj->sclust && pclst >>> <= obj->sclust + obj->n_cont) { /* Was the chain fragmented in >>> this session and got contiguous again? */ >>> + obj->stat = 2; /* Change the chain status >>> 'contiguous' */ >>> + } >>> + } >>> + } >>> + } >>> +#endif >>> + return FR_OK; >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* FAT handling - Stretch a chain or Create a new >>> chain */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static DWORD create_chain ( /* 0:No free cluster, 1:Internal >>> error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ >>> + FFOBJID* obj, /* Corresponding object */ >>> + DWORD clst /* Cluster# to stretch, 0:Create a new >>> chain */ >>> +) >>> +{ >>> + DWORD cs, ncl, scl; >>> + FRESULT res; >>> + FATFS *fs = obj->fs; >>> + >>> + >>> + if (clst == 0) { /* Create a new chain */ >>> + scl = fs->last_clst; /* Suggested cluster to >>> start to find */ >>> + if (scl == 0 || scl >= fs->n_fatent) scl = 1; >>> + } >>> + else { /* Stretch a chain */ >>> + cs = get_fat(obj, clst); /* Check the cluster >>> status */ >>> + if (cs < 2) return 1; /* Test for insanity */ >>> + if (cs == 0xFFFFFFFF) return cs; /* Test for disk error */ >>> + if (cs < fs->n_fatent) return cs; /* It is already >>> followed by next cluster */ >>> + scl = clst; /* Cluster to start >>> to find */ >>> + } >>> + if (fs->free_clst == 0) return 0; /* No free cluster */ >>> + >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ >>> + ncl = find_bitmap(fs, scl, 1); /* Find a free >>> cluster */ >>> + if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl; /* No free >>> cluster or hard error? */ >>> + res = change_bitmap(fs, ncl, 1, 1); /* Mark the >>> cluster 'in use' */ >>> + if (res == FR_INT_ERR) return 1; >>> + if (res == FR_DISK_ERR) return 0xFFFFFFFF; >>> + if (clst == 0) { /* Is it a new >>> chain? */ >>> + obj->stat = 2; /* Set status >>> 'contiguous' */ >>> + } else { /* It is a >>> stretched chain */ >>> + if (obj->stat == 2 && ncl != scl + 1) { /* Is the >>> chain got fragmented? */ >>> + obj->n_cont = scl - obj->sclust; /* Set size of >>> the contiguous part */ >>> + obj->stat = 3; /* Change >>> status 'just fragmented' */ >>> + } >>> + } >>> + if (obj->stat != 2) { /* Is the file non-contiguous? */ >>> + if (ncl == clst + 1) { /* Is the cluster next to >>> previous one? */ >>> + obj->n_frag = obj->n_frag ? obj->n_frag + 1 : 2; /* >>> Increment size of last framgent */ >>> + } else { /* New fragment */ >>> + if (obj->n_frag == 0) obj->n_frag = 1; >>> + res = fill_last_frag(obj, clst, ncl); /* Fill >>> last fragment on the FAT and link it to new one */ >>> + if (res == FR_OK) obj->n_frag = 1; >>> + } >>> + } >>> + } else >>> +#endif >>> + { /* On the FAT/FAT32 volume */ >>> + ncl = 0; >>> + if (scl == clst) { /* Stretching an >>> existing chain? */ >>> + ncl = scl + 1; /* Test if next >>> cluster is free */ >>> + if (ncl >= fs->n_fatent) ncl = 2; >>> + cs = get_fat(obj, ncl); /* Get next >>> cluster status */ >>> + if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* Test >>> for error */ >>> + if (cs != 0) { /* Not free? */ >>> + cs = fs->last_clst; /* Start at >>> suggested cluster if it is valid */ >>> + if (cs >= 2 && cs < fs->n_fatent) scl = cs; >>> + ncl = 0; >>> + } >>> + } >>> + if (ncl == 0) { /* The new cluster cannot be contiguous >>> and find another fragment */ >>> + ncl = scl; /* Start cluster */ >>> + for (;;) { >>> + ncl++; /* Next cluster */ >>> + if (ncl >= fs->n_fatent) { /* Check >>> wrap-around */ >>> + ncl = 2; >>> + if (ncl > scl) return 0; /* No free cluster >>> found? */ >>> + } >>> + cs = get_fat(obj, ncl); /* Get the >>> cluster status */ >>> + if (cs == 0) break; /* Found a free >>> cluster? */ >>> + if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* >>> Test for error */ >>> + if (ncl == scl) return 0; /* No free cluster >>> found? */ >>> + } >>> + } >>> + res = put_fat(fs, ncl, 0xFFFFFFFF); /* Mark the new >>> cluster 'EOC' */ >>> + if (res == FR_OK && clst != 0) { >>> + res = put_fat(fs, clst, ncl); /* Link it from the >>> previous one if needed */ >>> + } >>> + } >>> + >>> + if (res == FR_OK) { /* Update FSINFO if function >>> succeeded. */ >>> + fs->last_clst = ncl; >>> + if (fs->free_clst <= fs->n_fatent - 2) fs->free_clst--; >>> + fs->fsi_flag |= 1; >>> + } else { >>> + ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; /* Failed. >>> Generate error status */ >>> + } >>> + >>> + return ncl; /* Return new cluster number or error status */ >>> +} >>> + >>> +#endif /* !FF_FS_READONLY */ >>> + >>> + >>> + >>> + >>> +#if FF_USE_FASTSEEK >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* FAT handling - Convert offset into cluster with link map >>> table */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ >>> + FIL* fp, /* Pointer to the file object */ >>> + FSIZE_t ofs /* File offset to be converted to cluster# */ >>> +) >>> +{ >>> + DWORD cl, ncl, *tbl; >>> + FATFS *fs = fp->obj.fs; >>> + >>> + >>> + tbl = fp->cltbl + 1; /* Top of CLMT */ >>> + cl = (DWORD)(ofs / SS(fs) / fs->csize); /* Cluster order from >>> top of the file */ >>> + for (;;) { >>> + ncl = *tbl++; /* Number of cluters in the >>> fragment */ >>> + if (ncl == 0) return 0; /* End of table? (error) */ >>> + if (cl < ncl) break; /* In this fragment? */ >>> + cl -= ncl; tbl++; /* Next fragment */ >>> + } >>> + return cl + *tbl; /* Return the cluster number */ >>> +} >>> + >>> +#endif /* FF_USE_FASTSEEK */ >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Directory handling - Fill a cluster with >>> zeros */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +#if !FF_FS_READONLY >>> +static FRESULT dir_clear ( /* Returns FR_OK or FR_DISK_ERR */ >>> + FATFS *fs, /* Filesystem object */ >>> + DWORD clst /* Directory table to clear */ >>> +) >>> +{ >>> + LBA_t sect; >>> + UINT n, szb; >>> + BYTE *ibuf; >>> + >>> + >>> + if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush >>> disk access window */ >>> + sect = clst2sect(fs, clst); /* Top of the cluster */ >>> + fs->winsect = sect; /* Set window to top of the >>> cluster */ >>> + mem_set(fs->win, 0, sizeof fs->win); /* Clear window buffer */ >>> +#if FF_USE_LFN == 3 /* Quick table clear by using >>> multi-secter write */ >>> + /* Allocate a temporary buffer */ >>> + for (szb = ((DWORD)fs->csize * SS(fs) >= MAX_MALLOC) ? >>> MAX_MALLOC : fs->csize * SS(fs), ibuf = 0; szb > SS(fs) && (ibuf = >>> ff_memalloc(szb)) == 0; szb /= 2) ; >>> + if (szb > SS(fs)) { /* Buffer allocated? */ >>> + mem_set(ibuf, 0, szb); >>> + szb /= SS(fs); /* Bytes -> Sectors */ >>> + for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect >>> + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */ >>> + ff_memfree(ibuf); >>> + } else >>> +#endif >>> + { >>> + ibuf = fs->win; szb = 1; /* Use window buffer (many >>> single-sector writes may take a time) */ >>> + for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect >>> + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */ >>> + } >>> + return (n == fs->csize) ? FR_OK : FR_DISK_ERR; >>> +} >>> +#endif /* !FF_FS_READONLY */ >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Directory handling - Set directory >>> index */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */ >>> + DIR* dp, /* Pointer to directory object */ >>> + DWORD ofs /* Offset of directory table */ >>> +) >>> +{ >>> + DWORD csz, clst; >>> + FATFS *fs = dp->obj.fs; >>> + >>> + >>> + if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? >>> MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) { /* Check range of offset >>> and alignment */ >>> + return FR_INT_ERR; >>> + } >>> + dp->dptr = ofs; /* Set current offset */ >>> + clst = dp->obj.sclust; /* Table start cluster (0:root) */ >>> + if (clst == 0 && fs->fs_type >= FS_FAT32) { /* Replace >>> cluster# 0 with root cluster# */ >>> + clst = (DWORD)fs->dirbase; >>> + if (FF_FS_EXFAT) dp->obj.stat = 0; /* exFAT: Root dir has >>> an FAT chain */ >>> + } >>> + >>> + if (clst == 0) { /* Static table (root-directory on the FAT >>> volume) */ >>> + if (ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR; /* >>> Is index out of range? */ >>> + dp->sect = fs->dirbase; >>> + >>> + } else { /* Dynamic table (sub-directory or >>> root-directory on the FAT32/exFAT volume) */ >>> + csz = (DWORD)fs->csize * SS(fs); /* Bytes per cluster */ >>> + while (ofs >= csz) { /* Follow cluster chain */ >>> + clst = get_fat(&dp->obj, clst); /* Get >>> next cluster */ >>> + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk >>> error */ >>> + if (clst < 2 || clst >= fs->n_fatent) return >>> FR_INT_ERR; /* Reached to end of table or internal error */ >>> + ofs -= csz; >>> + } >>> + dp->sect = clst2sect(fs, clst); >>> + } >>> + dp->clust = clst; /* Current cluster# */ >>> + if (dp->sect == 0) return FR_INT_ERR; >>> + dp->sect += ofs / SS(fs); /* Sector# of the directory >>> entry */ >>> + dp->dir = fs->win + (ofs % SS(fs)); /* Pointer to the entry >>> in the win[] */ >>> + >>> + return FR_OK; >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Directory handling - Move directory table index >>> next */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End >>> of table, FR_DENIED:Could not stretch */ >>> + DIR* dp, /* Pointer to the directory object */ >>> + int stretch /* 0: Do not stretch table, 1: >>> Stretch table if needed */ >>> +) >>> +{ >>> + DWORD ofs, clst; >>> + FATFS *fs = dp->obj.fs; >>> + >>> + >>> + ofs = dp->dptr + SZDIRE; /* Next entry */ >>> + if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? >>> MAX_DIR_EX : MAX_DIR)) dp->sect = 0; /* Disable it if the offset >>> reached the max value */ >>> + if (dp->sect == 0) return FR_NO_FILE; /* Report EOT if it has >>> been disabled */ >>> + >>> + if (ofs % SS(fs) == 0) { /* Sector changed? */ >>> + dp->sect++; /* Next sector */ >>> + >>> + if (dp->clust == 0) { /* Static table */ >>> + if (ofs / SZDIRE >= fs->n_rootdir) { /* Report EOT if >>> it reached end of static table */ >>> + dp->sect = 0; return FR_NO_FILE; >>> + } >>> + } >>> + else { /* Dynamic table */ >>> + if ((ofs / SS(fs) & (fs->csize - 1)) == 0) { /* >>> Cluster changed? */ >>> + clst = get_fat(&dp->obj, dp->clust); /* Get >>> next cluster */ >>> + if (clst <= 1) return FR_INT_ERR; /* >>> Internal error */ >>> + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* >>> Disk error */ >>> + if (clst >= fs->n_fatent) { /* It >>> reached end of dynamic table */ >>> +#if !FF_FS_READONLY >>> + if (!stretch) { >>> /* If no stretch, report EOT */ >>> + dp->sect = 0; return FR_NO_FILE; >>> + } >>> + clst = create_chain(&dp->obj, dp->clust); /* >>> Allocate a cluster */ >>> + if (clst == 0) return FR_DENIED; /* >>> No free cluster */ >>> + if (clst == 1) return FR_INT_ERR; /* >>> Internal error */ >>> + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; >>> /* Disk error */ >>> + if (dir_clear(fs, clst) != FR_OK) return >>> FR_DISK_ERR; /* Clean up the stretched table */ >>> + if (FF_FS_EXFAT) dp->obj.stat |= 4; >>> /* exFAT: The directory has been stretched */ >>> +#else >>> + if (!stretch) dp->sect = 0; >>> /* (this line is to suppress compiler warning) */ >>> + dp->sect = 0; return FR_NO_FILE; /* >>> Report EOT */ >>> +#endif >>> + } >>> + dp->clust = clst; /* Initialize data for new >>> cluster */ >>> + dp->sect = clst2sect(fs, clst); >>> + } >>> + } >>> + } >>> + dp->dptr = ofs; /* Current entry */ >>> + dp->dir = fs->win + ofs % SS(fs); /* Pointer to the entry in >>> the win[] */ >>> + >>> + return FR_OK; >>> +} >>> + >>> + >>> + >>> + >>> +#if !FF_FS_READONLY >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Directory handling - Reserve a block of directory >>> entries */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ >>> + DIR* dp, /* Pointer to the directory object */ >>> + UINT n_ent /* Number of contiguous entries to >>> allocate */ >>> +) >>> +{ >>> + FRESULT res; >>> + UINT n; >>> + FATFS *fs = dp->obj.fs; >>> + >>> + >>> + res = dir_sdi(dp, 0); >>> + if (res == FR_OK) { >>> + n = 0; >>> + do { >>> + res = move_window(fs, dp->sect); >>> + if (res != FR_OK) break; >>> +#if FF_FS_EXFAT >>> + if ((fs->fs_type == FS_EXFAT) ? >>> (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == >>> DDEM || dp->dir[DIR_Name] == 0)) { /* Is the entry free? */ >>> +#else >>> + if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) >>> { /* Is the entry free? */ >>> +#endif >>> + if (++n == n_ent) break; /* Is a block of >>> contiguous free entries found? */ >>> + } else { >>> + n = 0; /* Not a free entry, restart >>> to search */ >>> + } >>> + res = dir_next(dp, 1); /* Next entry with table >>> stretch enabled */ >>> + } while (res == FR_OK); >>> + } >>> + >>> + if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry >>> to allocate */ >>> + return res; >>> +} >>> + >>> +#endif /* !FF_FS_READONLY */ >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* FAT: Directory handling - Load/Store start cluster >>> number */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static DWORD ld_clust ( /* Returns the top cluster value of the >>> SFN entry */ >>> + FATFS* fs, /* Pointer to the fs object */ >>> + const BYTE* dir /* Pointer to the key entry */ >>> +) >>> +{ >>> + DWORD cl; >>> + >>> + cl = ld_word(dir + DIR_FstClusLO); >>> + if (fs->fs_type == FS_FAT32) { >>> + cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16; >>> + } >>> + >>> + return cl; >>> +} >>> + >>> + >>> +#if !FF_FS_READONLY >>> +static void st_clust ( >>> + FATFS* fs, /* Pointer to the fs object */ >>> + BYTE* dir, /* Pointer to the key entry */ >>> + DWORD cl /* Value to be set */ >>> +) >>> +{ >>> + st_word(dir + DIR_FstClusLO, (WORD)cl); >>> + if (fs->fs_type == FS_FAT32) { >>> + st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16)); >>> + } >>> +} >>> +#endif >>> + >>> + >>> + >>> +#if FF_USE_LFN >>> +/*--------------------------------------------------------*/ >>> +/* FAT-LFN: Compare a part of file name with an LFN entry */ >>> +/*--------------------------------------------------------*/ >>> + >>> +static int cmp_lfn ( /* 1:matched, 0:not matched */ >>> + const WCHAR* lfnbuf, /* Pointer to the LFN working buffer to >>> be compared */ >>> + BYTE* dir /* Pointer to the directory entry >>> containing the part of LFN */ >>> +) >>> +{ >>> + UINT i, s; >>> + WCHAR wc, uc; >>> + >>> + >>> + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check >>> LDIR_FstClusLO */ >>> + >>> + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN >>> buffer */ >>> + >>> + for (wc = 1, s = 0; s < 13; s++) { /* Process all >>> characters in the entry */ >>> + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN >>> character */ >>> + if (wc != 0) { >>> + if (i >= FF_MAX_LFN + 1 || ff_wtoupper(uc) != >>> ff_wtoupper(lfnbuf[i++])) { /* Compare it */ >>> + return 0; /* Not matched */ >>> + } >>> + wc = uc; >>> + } else { >>> + if (uc != 0xFFFF) return 0; /* Check filler */ >>> + } >>> + } >>> + >>> + if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) return 0; /* >>> Last segment matched but different length */ >>> + >>> + return 1; /* The part of LFN matched */ >>> +} >>> + >>> + >>> +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || >>> FF_FS_EXFAT >>> +/*-----------------------------------------------------*/ >>> +/* FAT-LFN: Pick a part of file name from an LFN entry */ >>> +/*-----------------------------------------------------*/ >>> + >>> +static int pick_lfn ( /* 1:succeeded, 0:buffer overflow or >>> invalid LFN entry */ >>> + WCHAR* lfnbuf, /* Pointer to the LFN working buffer */ >>> + BYTE* dir /* Pointer to the LFN entry */ >>> +) >>> +{ >>> + UINT i, s; >>> + WCHAR wc, uc; >>> + >>> + >>> + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check >>> LDIR_FstClusLO is 0 */ >>> + >>> + i = ((dir[LDIR_Ord] & ~LLEF) - 1) * 13; /* Offset in the LFN >>> buffer */ >>> + >>> + for (wc = 1, s = 0; s < 13; s++) { /* Process all >>> characters in the entry */ >>> + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN >>> character */ >>> + if (wc != 0) { >>> + if (i >= FF_MAX_LFN + 1) return 0; /* Buffer >>> overflow? */ >>> + lfnbuf[i++] = wc = uc; /* Store it */ >>> + } else { >>> + if (uc != 0xFFFF) return 0; /* Check filler */ >>> + } >>> + } >>> + >>> + if (dir[LDIR_Ord] & LLEF && wc != 0) { /* Put terminator if >>> it is the last LFN part and not terminated */ >>> + if (i >= FF_MAX_LFN + 1) return 0; /* Buffer overflow? */ >>> + lfnbuf[i] = 0; >>> + } >>> + >>> + return 1; /* The part of LFN is valid */ >>> +} >>> +#endif >>> + >>> + >>> +#if !FF_FS_READONLY >>> +/*-----------------------------------------*/ >>> +/* FAT-LFN: Create an entry of LFN entries */ >>> +/*-----------------------------------------*/ >>> + >>> +static void put_lfn ( >>> + const WCHAR* lfn, /* Pointer to the LFN */ >>> + BYTE* dir, /* Pointer to the LFN entry to be created */ >>> + BYTE ord, /* LFN order (1-20) */ >>> + BYTE sum /* Checksum of the corresponding SFN */ >>> +) >>> +{ >>> + UINT i, s; >>> + WCHAR wc; >>> + >>> + >>> + dir[LDIR_Chksum] = sum; /* Set checksum */ >>> + dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ >>> + dir[LDIR_Type] = 0; >>> + st_word(dir + LDIR_FstClusLO, 0); >>> + >>> + i = (ord - 1) * 13; /* Get offset in the LFN >>> working buffer */ >>> + s = wc = 0; >>> + do { >>> + if (wc != 0xFFFF) wc = lfn[i++]; /* Get an effective >>> character */ >>> + st_word(dir + LfnOfs[s], wc); /* Put it */ >>> + if (wc == 0) wc = 0xFFFF; /* Padding characters for >>> following items */ >>> + } while (++s < 13); >>> + if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part is >>> the start of LFN sequence */ >>> + dir[LDIR_Ord] = ord; /* Set the LFN order */ >>> +} >>> + >>> +#endif /* !FF_FS_READONLY */ >>> +#endif /* FF_USE_LFN */ >>> + >>> + >>> + >>> +#if FF_USE_LFN && !FF_FS_READONLY >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* FAT-LFN: Create a Numbered >>> SFN */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static void gen_numname ( >>> + BYTE* dst, /* Pointer to the buffer to store numbered >>> SFN */ >>> + const BYTE* src, /* Pointer to SFN */ >>> + const WCHAR* lfn, /* Pointer to LFN */ >>> + UINT seq /* Sequence number */ >>> +) >>> +{ >>> + BYTE ns[8], c; >>> + UINT i, j; >>> + WCHAR wc; >>> + DWORD sreg; >>> + >>> + >>> + mem_cpy(dst, src, 11); >>> + >>> + if (seq > 5) { /* In case of many collisions, generate a hash >>> number instead of sequential number */ >>> + sreg = seq; >>> + while (*lfn) { /* Create a CRC as hash value */ >>> + wc = *lfn++; >>> + for (i = 0; i < 16; i++) { >>> + sreg = (sreg << 1) + (wc & 1); >>> + wc >>= 1; >>> + if (sreg & 0x10000) sreg ^= 0x11021; >>> + } >>> + } >>> + seq = (UINT)sreg; >>> + } >>> + >>> + /* itoa (hexdecimal) */ >>> + i = 7; >>> + do { >>> + c = (BYTE)((seq % 16) + '0'); >>> + if (c > '9') c += 7; >>> + ns[i--] = c; >>> + seq /= 16; >>> + } while (seq); >>> + ns[i] = '~'; >>> + >>> + /* Append the number to the SFN body */ >>> + for (j = 0; j < i && dst[j] != ' '; j++) { >>> + if (dbc_1st(dst[j])) { >>> + if (j == i - 1) break; >>> + j++; >>> + } >>> + } >>> + do { >>> + dst[j++] = (i < 8) ? ns[i++] : ' '; >>> + } while (j < 8); >>> +} >>> +#endif /* FF_USE_LFN && !FF_FS_READONLY */ >>> + >>> + >>> + >>> +#if FF_USE_LFN >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* FAT-LFN: Calculate checksum of an SFN >>> entry */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static BYTE sum_sfn ( >>> + const BYTE* dir /* Pointer to the SFN entry */ >>> +) >>> +{ >>> + BYTE sum = 0; >>> + UINT n = 11; >>> + >>> + do { >>> + sum = (sum >> 1) + (sum << 7) + *dir++; >>> + } while (--n); >>> + return sum; >>> +} >>> + >>> +#endif /* FF_USE_LFN */ >>> + >>> + >>> + >>> +#if FF_FS_EXFAT >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* exFAT: >>> Checksum */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static WORD xdir_sum ( /* Get checksum of the directoly entry >>> block */ >>> + const BYTE* dir /* Directory entry block to be calculated */ >>> +) >>> +{ >>> + UINT i, szblk; >>> + WORD sum; >>> + >>> + >>> + szblk = (dir[XDIR_NumSec] + 1) * SZDIRE; /* Number of bytes >>> of the entry block */ >>> + for (i = sum = 0; i < szblk; i++) { >>> + if (i == XDIR_SetSum) { /* Skip 2-byte sum field */ >>> + i++; >>> + } else { >>> + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i]; >>> + } >>> + } >>> + return sum; >>> +} >>> + >>> + >>> + >>> +static WORD xname_sum ( /* Get check sum (to be used as hash) of >>> the file name */ >>> + const WCHAR* name /* File name to be calculated */ >>> +) >>> +{ >>> + WCHAR chr; >>> + WORD sum = 0; >>> + >>> + >>> + while ((chr = *name++) != 0) { >>> + chr = (WCHAR)ff_wtoupper(chr); /* File name needs to >>> be up-case converted */ >>> + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF); >>> + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8); >>> + } >>> + return sum; >>> +} >>> + >>> + >>> +#if !FF_FS_READONLY && FF_USE_MKFS >>> +static DWORD xsum32 ( /* Returns 32-bit checksum */ >>> + BYTE dat, /* Byte to be calculated (byte-by-byte >>> processing) */ >>> + DWORD sum /* Previous sum value */ >>> +) >>> +{ >>> + sum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat; >>> + return sum; >>> +} >>> +#endif >>> + >>> + >>> +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 >>> +/*------------------------------------------------------*/ >>> +/* exFAT: Get object information from a directory block */ >>> +/*------------------------------------------------------*/ >>> + >>> +static void get_xfileinfo ( >>> + BYTE* dirb, /* Pointer to the direcotry entry block >>> 85+C0+C1s */ >>> + FILINFO* fno /* Buffer to store the extracted file >>> information */ >>> +) >>> +{ >>> + WCHAR wc, hs; >>> + UINT di, si, nc; >>> + >>> + /* Get file name from the entry block */ >>> + si = SZDIRE * 2; /* 1st C1 entry */ >>> + nc = 0; hs = 0; di = 0; >>> + while (nc < dirb[XDIR_NumName]) { >>> + if (si >= MAXDIRB(FF_MAX_LFN)) { di = 0; break; } /* >>> Truncated directory block? */ >>> + if ((si % SZDIRE) == 0) si += 2; /* Skip entry type >>> field */ >>> + wc = ld_word(dirb + si); si += 2; nc++; /* Get a >>> character */ >>> + if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ >>> + hs = wc; continue; /* Get low surrogate */ >>> + } >>> + wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], >>> FF_LFN_BUF - di); /* Store it in API encoding */ >>> + if (wc == 0) { di = 0; break; } /* Buffer overflow or >>> wrong encoding? */ >>> + di += wc; >>> + hs = 0; >>> + } >>> + if (hs != 0) di = 0; /* Broken surrogate >>> pair? */ >>> + if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object >>> name? */ >>> + fno->fname[di] = 0; /* Terminate the name */ >>> + fno->altname[0] = 0; /* exFAT does not >>> support SFN */ >>> + >>> + fno->fattrib = dirb[XDIR_Attr]; /* Attribute */ >>> + fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + >>> XDIR_FileSize); /* Size */ >>> + fno->ftime = ld_word(dirb + XDIR_ModTime + 0); /* Time */ >>> + fno->fdate = ld_word(dirb + XDIR_ModTime + 2); /* Date */ >>> +} >>> + >>> +#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */ >>> + >>> + >>> +/*-----------------------------------*/ >>> +/* exFAT: Get a directry entry block */ >>> +/*-----------------------------------*/ >>> + >>> +static FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ >>> + DIR* dp /* Reading direcotry object pointing >>> top of the entry block to load */ >>> +) >>> +{ >>> + FRESULT res; >>> + UINT i, sz_ent; >>> + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory >>> direcotry entry block 85+C0+C1s */ >>> + >>> + >>> + /* Load file-directory entry */ >>> + res = move_window(dp->obj.fs, dp->sect); >>> + if (res != FR_OK) return res; >>> + if (dp->dir[XDIR_Type] != ET_FILEDIR) return FR_INT_ERR; /* >>> Invalid order */ >>> + mem_cpy(dirb + 0 * SZDIRE, dp->dir, SZDIRE); >>> + sz_ent = (dirb[XDIR_NumSec] + 1) * SZDIRE; >>> + if (sz_ent < 3 * SZDIRE || sz_ent > 19 * SZDIRE) return FR_INT_ERR; >>> + >>> + /* Load stream-extension entry */ >>> + res = dir_next(dp, 0); >>> + if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */ >>> + if (res != FR_OK) return res; >>> + res = move_window(dp->obj.fs, dp->sect); >>> + if (res != FR_OK) return res; >>> + if (dp->dir[XDIR_Type] != ET_STREAM) return FR_INT_ERR; /* >>> Invalid order */ >>> + mem_cpy(dirb + 1 * SZDIRE, dp->dir, SZDIRE); >>> + if (MAXDIRB(dirb[XDIR_NumName]) > sz_ent) return FR_INT_ERR; >>> + >>> + /* Load file-name entries */ >>> + i = 2 * SZDIRE; /* Name offset to load */ >>> + do { >>> + res = dir_next(dp, 0); >>> + if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */ >>> + if (res != FR_OK) return res; >>> + res = move_window(dp->obj.fs, dp->sect); >>> + if (res != FR_OK) return res; >>> + if (dp->dir[XDIR_Type] != ET_FILENAME) return FR_INT_ERR; /* >>> Invalid order */ >>> + if (i < MAXDIRB(FF_MAX_LFN)) mem_cpy(dirb + i, dp->dir, >>> SZDIRE); >>> + } while ((i += SZDIRE) < sz_ent); >>> + >>> + /* Sanity check (do it for only accessible object) */ >>> + if (i <= MAXDIRB(FF_MAX_LFN)) { >>> + if (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return >>> FR_INT_ERR; >>> + } >>> + return FR_OK; >>> +} >>> + >>> + >>> +/*------------------------------------------------------------------*/ >>> +/* exFAT: Initialize object allocation info with loaded entry block */ >>> +/*------------------------------------------------------------------*/ >>> + >>> +static void init_alloc_info ( >>> + FATFS* fs, /* Filesystem object */ >>> + FFOBJID* obj /* Object allocation information to be >>> initialized */ >>> +) >>> +{ >>> + obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* >>> Start cluster */ >>> + obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); /* Size */ >>> + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; /* >>> Allocation status */ >>> + obj->n_frag = 0; /* No >>> last fragment info */ >>> +} >>> + >>> + >>> + >>> +#if !FF_FS_READONLY || FF_FS_RPATH != 0 >>> +/*------------------------------------------------*/ >>> +/* exFAT: Load the object's directory entry block */ >>> +/*------------------------------------------------*/ >>> + >>> +static FRESULT load_obj_xdir ( >>> + DIR* dp, /* Blank directory object to be used to >>> access containing direcotry */ >>> + const FFOBJID* obj /* Object with its containing directory >>> information */ >>> +) >>> +{ >>> + FRESULT res; >>> + >>> + /* Open object containing directory */ >>> + dp->obj.fs = obj->fs; >>> + dp->obj.sclust = obj->c_scl; >>> + dp->obj.stat = (BYTE)obj->c_size; >>> + dp->obj.objsize = obj->c_size & 0xFFFFFF00; >>> + dp->obj.n_frag = 0; >>> + dp->blk_ofs = obj->c_ofs; >>> + >>> + res = dir_sdi(dp, dp->blk_ofs); /* Goto object's entry block */ >>> + if (res == FR_OK) { >>> + res = load_xdir(dp); /* Load the object's entry block */ >>> + } >>> + return res; >>> +} >>> +#endif >>> + >>> + >>> +#if !FF_FS_READONLY >>> +/*----------------------------------------*/ >>> +/* exFAT: Store the directory entry block */ >>> +/*----------------------------------------*/ >>> + >>> +static FRESULT store_xdir ( >>> + DIR* dp /* Pointer to the direcotry object */ >>> +) >>> +{ >>> + FRESULT res; >>> + UINT nent; >>> + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry >>> entry block 85+C0+C1s */ >>> + >>> + /* Create set sum */ >>> + st_word(dirb + XDIR_SetSum, xdir_sum(dirb)); >>> + nent = dirb[XDIR_NumSec] + 1; >>> + >>> + /* Store the direcotry entry block to the directory */ >>> + res = dir_sdi(dp, dp->blk_ofs); >>> + while (res == FR_OK) { >>> + res = move_window(dp->obj.fs, dp->sect); >>> + if (res != FR_OK) break; >>> + mem_cpy(dp->dir, dirb, SZDIRE); >>> + dp->obj.fs->wflag = 1; >>> + if (--nent == 0) break; >>> + dirb += SZDIRE; >>> + res = dir_next(dp, 0); >>> + } >>> + return (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR; >>> +} >>> + >>> + >>> + >>> +/*-------------------------------------------*/ >>> +/* exFAT: Create a new directory enrty block */ >>> +/*-------------------------------------------*/ >>> + >>> +static void create_xdir ( >>> + BYTE* dirb, /* Pointer to the direcotry entry block >>> buffer */ >>> + const WCHAR* lfn /* Pointer to the object name */ >>> +) >>> +{ >>> + UINT i; >>> + BYTE nc1, nlen; >>> + WCHAR wc; >>> + >>> + >>> + /* Create file-directory and stream-extension entry */ >>> + mem_set(dirb, 0, 2 * SZDIRE); >>> + dirb[0 * SZDIRE + XDIR_Type] = ET_FILEDIR; >>> + dirb[1 * SZDIRE + XDIR_Type] = ET_STREAM; >>> + >>> + /* Create file-name entries */ >>> + i = SZDIRE * 2; /* Top of file_name entries */ >>> + nlen = nc1 = 0; wc = 1; >>> + do { >>> + dirb[i++] = ET_FILENAME; dirb[i++] = 0; >>> + do { /* Fill name field */ >>> + if (wc != 0 && (wc = lfn[nlen]) != 0) nlen++; /* Get >>> a character if exist */ >>> + st_word(dirb + i, wc); /* Store it */ >>> + i += 2; >>> + } while (i % SZDIRE != 0); >>> + nc1++; >>> + } while (lfn[nlen]); /* Fill next entry if any char follows */ >>> + >>> + dirb[XDIR_NumName] = nlen; /* Set name length */ >>> + dirb[XDIR_NumSec] = 1 + nc1; /* Set secondary count (C0 + >>> C1s) */ >>> + st_word(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name >>> hash */ >>> +} >>> + >>> +#endif /* !FF_FS_READONLY */ >>> +#endif /* FF_FS_EXFAT */ >>> + >>> + >>> + >>> +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || >>> FF_FS_EXFAT >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Read an object from the >>> directory */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +#define DIR_READ_FILE(dp) dir_read(dp, 0) >>> +#define DIR_READ_LABEL(dp) dir_read(dp, 1) >>> + >>> +static FRESULT dir_read ( >>> + DIR* dp, /* Pointer to the directory object */ >>> + int vol /* Filtered by 0:file/directory or 1:volume >>> label */ >>> +) >>> +{ >>> + FRESULT res = FR_NO_FILE; >>> + FATFS *fs = dp->obj.fs; >>> + BYTE attr, b; >>> +#if FF_USE_LFN >>> + BYTE ord = 0xFF, sum = 0xFF; >>> +#endif >>> + >>> + while (dp->sect) { >>> + res = move_window(fs, dp->sect); >>> + if (res != FR_OK) break; >>> + b = dp->dir[DIR_Name]; /* Test for the entry type */ >>> + if (b == 0) { >>> + res = FR_NO_FILE; break; /* Reached to end of the >>> directory */ >>> + } >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ >>> + if (FF_USE_LABEL && vol) { >>> + if (b == ET_VLABEL) break; /* Volume label entry? */ >>> + } else { >>> + if (b == ET_FILEDIR) { /* Start of the file >>> entry block? */ >>> + dp->blk_ofs = dp->dptr; /* Get location of >>> the block */ >>> + res = load_xdir(dp); /* Load the entry block */ >>> + if (res == FR_OK) { >>> + dp->obj.attr = fs->dirbuf[XDIR_Attr] & >>> AM_MASK; /* Get attribute */ >>> + } >>> + break; >>> + } >>> + } >>> + } else >>> +#endif >>> + { /* On the FAT/FAT32 volume */ >>> + dp->obj.attr = attr = dp->dir[DIR_Attr] & AM_MASK; /* >>> Get attribute */ >>> +#if FF_USE_LFN /* LFN configuration */ >>> + if (b == DDEM || b == '.' || (int)((attr & ~AM_ARC) == >>> AM_VOL) != vol) { /* An entry without valid data */ >>> + ord = 0xFF; >>> + } else { >>> + if (attr == AM_LFN) { /* An LFN entry is >>> found */ >>> + if (b & LLEF) { /* Is it start of an >>> LFN sequence? */ >>> + sum = dp->dir[LDIR_Chksum]; >>> + b &= (BYTE)~LLEF; ord = b; >>> + dp->blk_ofs = dp->dptr; >>> + } >>> + /* Check LFN validity and capture it */ >>> + ord = (b == ord && sum == dp->dir[LDIR_Chksum] >>> && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; >>> + } else { /* An SFN entry is found */ >>> + if (ord != 0 || sum != sum_sfn(dp->dir)) { /* >>> Is there a valid LFN? */ >>> + dp->blk_ofs = 0xFFFFFFFF; /* It >>> has no LFN. */ >>> + } >>> + break; >>> + } >>> + } >>> +#else /* Non LFN configuration */ >>> + if (b != DDEM && b != '.' && attr != AM_LFN && >>> (int)((attr & ~AM_ARC) == AM_VOL) == vol) { /* Is it a valid >>> entry? */ >>> + break; >>> + } >>> +#endif >>> + } >>> + res = dir_next(dp, 0); /* Next entry */ >>> + if (res != FR_OK) break; >>> + } >>> + >>> + if (res != FR_OK) dp->sect = 0; /* Terminate the read >>> operation on error or EOT */ >>> + return res; >>> +} >>> + >>> +#endif /* FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_RPATH >= 2 */ >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Directory handling - Find an object in the >>> directory */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ >>> + DIR* dp /* Pointer to the directory object >>> with the file name */ >>> +) >>> +{ >>> + FRESULT res; >>> + FATFS *fs = dp->obj.fs; >>> + BYTE c; >>> +#if FF_USE_LFN >>> + BYTE a, ord, sum; >>> +#endif >>> + >>> + res = dir_sdi(dp, 0); /* Rewind directory object */ >>> + if (res != FR_OK) return res; >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ >>> + BYTE nc; >>> + UINT di, ni; >>> + WORD hash = xname_sum(fs->lfnbuf); /* Hash value of >>> the name to find */ >>> + >>> + while ((res = DIR_READ_FILE(dp)) == FR_OK) { /* Read an >>> item */ >>> +#if FF_MAX_LFN < 255 >>> + if (fs->dirbuf[XDIR_NumName] > FF_MAX_LFN) >>> continue; /* Skip comparison if inaccessible object name */ >>> +#endif >>> + if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) >>> continue; /* Skip comparison if hash mismatched */ >>> + for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni >>> = 0; nc; nc--, di += 2, ni++) { /* Compare the name */ >>> + if ((di % SZDIRE) == 0) di += 2; >>> + if (ff_wtoupper(ld_word(fs->dirbuf + di)) != >>> ff_wtoupper(fs->lfnbuf[ni])) break; >>> + } >>> + if (nc == 0 && !fs->lfnbuf[ni]) break; /* Name >>> matched? */ >>> + } >>> + return res; >>> + } >>> +#endif >>> + /* On the FAT/FAT32 volume */ >>> +#if FF_USE_LFN >>> + ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN >>> sequence */ >>> +#endif >>> + do { >>> + res = move_window(fs, dp->sect); >>> + if (res != FR_OK) break; >>> + c = dp->dir[DIR_Name]; >>> + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to >>> end of table */ >>> +#if FF_USE_LFN /* LFN configuration */ >>> + dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; >>> + if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An >>> entry without valid data */ >>> + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN >>> sequence */ >>> + } else { >>> + if (a == AM_LFN) { /* An LFN entry is found */ >>> + if (!(dp->fn[NSFLAG] & NS_NOLFN)) { >>> + if (c & LLEF) { /* Is it start of LFN >>> sequence? */ >>> + sum = dp->dir[LDIR_Chksum]; >>> + c &= (BYTE)~LLEF; ord = c; /* LFN start >>> order */ >>> + dp->blk_ofs = dp->dptr; /* Start offset >>> of LFN */ >>> + } >>> + /* Check validity of the LFN entry and compare >>> it with given name */ >>> + ord = (c == ord && sum == dp->dir[LDIR_Chksum] >>> && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; >>> + } >>> + } else { /* An SFN entry is found */ >>> + if (ord == 0 && sum == sum_sfn(dp->dir)) break; >>> /* LFN matched? */ >>> + if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, >>> dp->fn, 11)) break; /* SFN matched? */ >>> + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset >>> LFN sequence */ >>> + } >>> + } >>> +#else /* Non LFN configuration */ >>> + dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK; >>> + if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, >>> dp->fn, 11)) break; /* Is it a valid entry? */ >>> +#endif >>> + res = dir_next(dp, 0); /* Next entry */ >>> + } while (res == FR_OK); >>> + >>> + return res; >>> +} >>> + >>> + >>> + >>> + >>> +#if !FF_FS_READONLY >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Register an object to the >>> directory */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no >>> free entry or too many SFN collision, FR_DISK_ERR:disk error */ >>> + DIR* dp /* Target directory with object >>> name to be created */ >>> +) >>> +{ >>> + FRESULT res; >>> + FATFS *fs = dp->obj.fs; >>> +#if FF_USE_LFN /* LFN configuration */ >>> + UINT n, len, n_ent; >>> + BYTE sn[12], sum; >>> + >>> + >>> + if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return >>> FR_INVALID_NAME; /* Check name validity */ >>> + for (len = 0; fs->lfnbuf[len]; len++) ; /* Get lfn length */ >>> + >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ >>> + n_ent = (len + 14) / 15 + 2; /* Number of entries to >>> allocate (85+C0+C1s) */ >>> + res = dir_alloc(dp, n_ent); /* Allocate directory >>> entries */ >>> + if (res != FR_OK) return res; >>> + dp->blk_ofs = dp->dptr - SZDIRE * (n_ent - 1); /* Set the >>> allocated entry block offset */ >>> + >>> + if (dp->obj.stat & 4) { /* Has the directory been >>> stretched by new allocation? */ >>> + dp->obj.stat &= ~4; >>> + res = fill_first_frag(&dp->obj); /* Fill the first >>> fragment on the FAT if needed */ >>> + if (res != FR_OK) return res; >>> + res = fill_last_frag(&dp->obj, dp->clust, 0xFFFFFFFF); >>> /* Fill the last fragment on the FAT if needed */ >>> + if (res != FR_OK) return res; >>> + if (dp->obj.sclust != 0) { /* Is it a >>> sub-directory? */ >>> + DIR dj; >>> + >>> + res = load_obj_xdir(&dj, &dp->obj); /* Load the >>> object status */ >>> + if (res != FR_OK) return res; >>> + dp->obj.objsize += (DWORD)fs->csize * >>> SS(fs); /* Increase the directory size by cluster size */ >>> + st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize); >>> + st_qword(fs->dirbuf + XDIR_ValidFileSize, >>> dp->obj.objsize); >>> + fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | >>> 1; /* Update the allocation status */ >>> + res = store_xdir(&dj); /* Store the >>> object status */ >>> + if (res != FR_OK) return res; >>> + } >>> + } >>> + >>> + create_xdir(fs->dirbuf, fs->lfnbuf); /* Create on-memory >>> directory block to be written later */ >>> + return FR_OK; >>> + } >>> +#endif >>> + /* On the FAT/FAT32 volume */ >>> + mem_cpy(sn, dp->fn, 12); >>> + if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 >>> format, generate a numbered name */ >>> + dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */ >>> + for (n = 1; n < 100; n++) { >>> + gen_numname(dp->fn, sn, fs->lfnbuf, n); /* Generate a >>> numbered name */ >>> + res = dir_find(dp); /* Check if the name >>> collides with existing SFN */ >>> + if (res != FR_OK) break; >>> + } >>> + if (n == 100) return FR_DENIED; /* Abort if too many >>> collisions */ >>> + if (res != FR_NO_FILE) return res; /* Abort if the result >>> is other than 'not collided' */ >>> + dp->fn[NSFLAG] = sn[NSFLAG]; >>> + } >>> + >>> + /* Create an SFN with/without LFNs. */ >>> + n_ent = (sn[NSFLAG] & NS_LFN) ? (len + 12) / 13 + 1 : 1; /* >>> Number of entries to allocate */ >>> + res = dir_alloc(dp, n_ent); /* Allocate entries */ >>> + if (res == FR_OK && --n_ent) { /* Set LFN entry if needed */ >>> + res = dir_sdi(dp, dp->dptr - n_ent * SZDIRE); >>> + if (res == FR_OK) { >>> + sum = sum_sfn(dp->fn); /* Checksum value of the SFN >>> tied to the LFN */ >>> + do { /* Store LFN entries in bottom >>> first */ >>> + res = move_window(fs, dp->sect); >>> + if (res != FR_OK) break; >>> + put_lfn(fs->lfnbuf, dp->dir, (BYTE)n_ent, sum); >>> + fs->wflag = 1; >>> + res = dir_next(dp, 0); /* Next entry */ >>> + } while (res == FR_OK && --n_ent); >>> + } >>> + } >>> + >>> +#else /* Non LFN configuration */ >>> + res = dir_alloc(dp, 1); /* Allocate an entry for SFN */ >>> + >>> +#endif >>> + >>> + /* Set SFN entry */ >>> + if (res == FR_OK) { >>> + res = move_window(fs, dp->sect); >>> + if (res == FR_OK) { >>> + mem_set(dp->dir, 0, SZDIRE); /* Clean the entry */ >>> + mem_cpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */ >>> +#if FF_USE_LFN >>> + dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | >>> NS_EXT); /* Put NT flag */ >>> +#endif >>> + fs->wflag = 1; >>> + } >>> + } >>> + >>> + return res; >>> +} >>> + >>> +#endif /* !FF_FS_READONLY */ >>> + >>> + >>> + >>> +#if !FF_FS_READONLY && FF_FS_MINIMIZE == 0 >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Remove an object from the >>> directory */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A >>> disk error */ >>> + DIR* dp /* Directory object pointing the >>> entry to be removed */ >>> +) >>> +{ >>> + FRESULT res; >>> + FATFS *fs = dp->obj.fs; >>> +#if FF_USE_LFN /* LFN configuration */ >>> + DWORD last = dp->dptr; >>> + >>> + res = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, >>> dp->blk_ofs); /* Goto top of the entry block if LFN is exist */ >>> + if (res == FR_OK) { >>> + do { >>> + res = move_window(fs, dp->sect); >>> + if (res != FR_OK) break; >>> + if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On >>> the exFAT volume */ >>> + dp->dir[XDIR_Type] &= 0x7F; /* Clear the entry >>> InUse flag. */ >>> + } else { /* On the >>> FAT/FAT32 volume */ >>> + dp->dir[DIR_Name] = DDEM; /* Mark the entry >>> 'deleted'. */ >>> + } >>> + fs->wflag = 1; >>> + if (dp->dptr >= last) break; /* If reached last entry >>> then all entries of the object has been deleted. */ >>> + res = dir_next(dp, 0); /* Next entry */ >>> + } while (res == FR_OK); >>> + if (res == FR_NO_FILE) res = FR_INT_ERR; >>> + } >>> +#else /* Non LFN configuration */ >>> + >>> + res = move_window(fs, dp->sect); >>> + if (res == FR_OK) { >>> + dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'.*/ >>> + fs->wflag = 1; >>> + } >>> +#endif >>> + >>> + return res; >>> +} >>> + >>> +#endif /* !FF_FS_READONLY && FF_FS_MINIMIZE == 0 */ >>> + >>> + >>> + >>> +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Get file information from directory >>> entry */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static void get_fileinfo ( >>> + DIR* dp, /* Pointer to the directory object */ >>> + FILINFO* fno /* Pointer to the file information to be >>> filled */ >>> +) >>> +{ >>> + UINT si, di; >>> +#if FF_USE_LFN >>> + BYTE lcf; >>> + WCHAR wc, hs; >>> + FATFS *fs = dp->obj.fs; >>> +#else >>> + TCHAR c; >>> +#endif >>> + >>> + >>> + fno->fname[0] = 0; /* Invaidate file info */ >>> + if (dp->sect == 0) return; /* Exit if read pointer has >>> reached end of directory */ >>> + >>> +#if FF_USE_LFN /* LFN configuration */ >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ >>> + get_xfileinfo(fs->dirbuf, fno); >>> + return; >>> + } else >>> +#endif >>> + { /* On the FAT/FAT32 volume */ >>> + if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */ >>> + si = di = hs = 0; >>> + while (fs->lfnbuf[si] != 0) { >>> + wc = fs->lfnbuf[si++]; /* Get an LFN >>> character (UTF-16) */ >>> + if (hs == 0 && IsSurrogate(wc)) { /* Is it a >>> surrogate? */ >>> + hs = wc; continue; /* Get low surrogate */ >>> + } >>> + wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], >>> FF_LFN_BUF - di); /* Store it in UTF-16 or UTF-8 encoding */ >>> + if (wc == 0) { di = 0; break; } /* Invalid char >>> or buffer overflow? */ >>> + di += wc; >>> + hs = 0; >>> + } >>> + if (hs != 0) di = 0; /* Broken surrogate pair? */ >>> + fno->fname[di] = 0; /* Terminate the LFN (null >>> string means LFN is invalid) */ >>> + } >>> + } >>> + >>> + si = di = 0; >>> + while (si < 11) { /* Get SFN from SFN entry */ >>> + wc = dp->dir[si++]; /* Get a char */ >>> + if (wc == ' ') continue; /* Skip padding spaces */ >>> + if (wc == RDDEM) wc = DDEM; /* Restore replaced DDEM >>> character */ >>> + if (si == 9 && di < FF_SFN_BUF) fno->altname[di++] = '.'; /* >>> Insert a . if extension is exist */ >>> +#if FF_LFN_UNICODE >= 1 /* Unicode output */ >>> + if (dbc_1st((BYTE)wc) && si != 8 && si != 11 && >>> dbc_2nd(dp->dir[si])) { /* Make a DBC if needed */ >>> + wc = wc << 8 | dp->dir[si++]; >>> + } >>> + wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM -> Unicode */ >>> + if (wc == 0) { di = 0; break; } /* Wrong char in the >>> current code page? */ >>> + wc = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di); /* >>> Store it in Unicode */ >>> + if (wc == 0) { di = 0; break; } /* Buffer overflow? */ >>> + di += wc; >>> +#else /* ANSI/OEM output */ >>> + fno->altname[di++] = (TCHAR)wc; /* Store it without any >>> conversion */ >>> +#endif >>> + } >>> + fno->altname[di] = 0; /* Terminate the SFN (null string >>> means SFN is invalid) */ >>> + >>> + if (fno->fname[0] == 0) { /* If LFN is invalid, altname[] >>> needs to be copied to fname[] */ >>> + if (di == 0) { /* If LFN and SFN both are invalid, this >>> object is inaccesible */ >>> + fno->fname[di++] = '?'; >>> + } else { >>> + for (si = di = 0, lcf = NS_BODY; fno->altname[si]; si++, >>> di++) { /* Copy altname[] to fname[] with case information */ >>> + wc = (WCHAR)fno->altname[si]; >>> + if (wc == '.') lcf = NS_EXT; >>> + if (IsUpper(wc) && (dp->dir[DIR_NTres] & lcf)) wc += >>> 0x20; >>> + fno->fname[di] = (TCHAR)wc; >>> + } >>> + } >>> + fno->fname[di] = 0; /* Terminate the LFN */ >>> + if (!dp->dir[DIR_NTres]) fno->altname[0] = 0; /* Altname >>> is not needed if neither LFN nor case info is exist. */ >>> + } >>> + >>> +#else /* Non-LFN configuration */ >>> + si = di = 0; >>> + while (si < 11) { /* Copy name body and extension */ >>> + c = (TCHAR)dp->dir[si++]; >>> + if (c == ' ') continue; /* Skip padding spaces */ >>> + if (c == RDDEM) c = DDEM; /* Restore replaced DDEM >>> character */ >>> + if (si == 9) fno->fname[di++] = '.';/* Insert a . if >>> extension is exist */ >>> + fno->fname[di++] = c; >>> + } >>> + fno->fname[di] = 0; >>> +#endif >>> + >>> + fno->fattrib = dp->dir[DIR_Attr]; /* >>> Attribute */ >>> + fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ >>> + fno->ftime = ld_word(dp->dir + DIR_ModTime + 0); /* Time */ >>> + fno->fdate = ld_word(dp->dir + DIR_ModTime + 2); /* Date */ >>> +} >>> + >>> +#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */ >>> + >>> + >>> + >>> +#if FF_USE_FIND && FF_FS_MINIMIZE <= 1 >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Pattern >>> matching */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +#define FIND_RECURS 4 /* Maximum number of wildcard terms in >>> the pattern to limit recursion */ >>> + >>> + >>> +static DWORD get_achar ( /* Get a character and advance ptr */ >>> + const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM >>> or Unicode string */ >>> +) >>> +{ >>> + DWORD chr; >>> + >>> + >>> +#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode input */ >>> + chr = tchar2uni(ptr); >>> + if (chr == 0xFFFFFFFF) chr = 0; /* Wrong UTF encoding is >>> recognized as end of the string */ >>> + chr = ff_wtoupper(chr); >>> + >>> +#else /* ANSI/OEM input */ >>> + chr = (BYTE)*(*ptr)++; /* Get a byte */ >>> + if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */ >>> +#if FF_CODE_PAGE == 0 >>> + if (ExCvt && chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To >>> upper SBCS extended char */ >>> +#elif FF_CODE_PAGE < 900 >>> + if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS >>> extended char */ >>> +#endif >>> +#if FF_CODE_PAGE == 0 || FF_CODE_PAGE >= 900 >>> + if (dbc_1st((BYTE)chr)) { /* Get DBC 2nd byte if needed */ >>> + chr = dbc_2nd((BYTE)**ptr) ? chr << 8 | (BYTE)*(*ptr)++ : 0; >>> + } >>> +#endif >>> + >>> +#endif >>> + return chr; >>> +} >>> + >>> + >>> +static int pattern_match ( /* 0:mismatched, 1:matched */ >>> + const TCHAR* pat, /* Matching pattern */ >>> + const TCHAR* nam, /* String to be tested */ >>> + UINT skip, /* Number of pre-skip chars (number of ?s, >>> b8:infinite (* specified)) */ >>> + UINT recur /* Recursion count */ >>> +) >>> +{ >>> + const TCHAR *pptr, *nptr; >>> + DWORD pchr, nchr; >>> + UINT sk; >>> + >>> + >>> + while ((skip & 0xFF) != 0) { /* Pre-skip name chars */ >>> + if (!get_achar(&nam)) return 0; /* Branch mismatched if >>> less name chars */ >>> + skip--; >>> + } >>> + if (*pat == 0 && skip) return 1; /* Matched? (short circuit) */ >>> + >>> + do { >>> + pptr = pat; nptr = nam; /* Top of pattern and >>> name to match */ >>> + for (;;) { >>> + if (*pptr == '?' || *pptr == '*') { /* Wildcard term? */ >>> + if (recur == 0) return 0; /* Too many wildcard >>> terms? */ >>> + sk = 0; >>> + do { /* Analyze the wildcard term */ >>> + if (*pptr++ == '?') sk++; else sk |= 0x100; >>> + } while (*pptr == '?' || *pptr == '*'); >>> + if (pattern_match(pptr, nptr, sk, recur - 1)) return >>> 1; /* Test new branch (recursive call) */ >>> + nchr = *nptr; break; /* Branch mismatched */ >>> + } >>> + pchr = get_achar(&pptr); /* Get a pattern char */ >>> + nchr = get_achar(&nptr); /* Get a name char */ >>> + if (pchr != nchr) break; /* Branch mismatched? */ >>> + if (pchr == 0) return 1; /* Branch matched? (matched >>> at end of both strings) */ >>> + } >>> + get_achar(&nam); /* nam++ */ >>> + } while (skip && nchr); /* Retry until end of name if >>> infinite search is specified */ >>> + >>> + return 0; >>> +} >>> + >>> +#endif /* FF_USE_FIND && FF_FS_MINIMIZE <= 1 */ >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Pick a top segment and create the object name in directory >>> form */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static FRESULT create_name ( /* FR_OK: successful, >>> FR_INVALID_NAME: could not create */ >>> + DIR* dp, /* Pointer to the directory object */ >>> + const TCHAR** path /* Pointer to pointer to the >>> segment in the path string */ >>> +) >>> +{ >>> +#if FF_USE_LFN /* LFN configuration */ >>> + BYTE b, cf; >>> + WCHAR wc, *lfn; >>> + DWORD uc; >>> + UINT i, ni, si, di; >>> + const TCHAR *p; >>> + >>> + >>> + /* Create LFN into LFN working buffer */ >>> + p = *path; lfn = dp->obj.fs->lfnbuf; di = 0; >>> + for (;;) { >>> + uc = tchar2uni(&p); /* Get a character */ >>> + if (uc == 0xFFFFFFFF) return FR_INVALID_NAME; /* >>> Invalid code or UTF decode error */ >>> + if (uc >= 0x10000) lfn[di++] = (WCHAR)(uc >> 16); /* >>> Store high surrogate if needed */ >>> + wc = (WCHAR)uc; >>> + if (wc < ' ' || wc == '/' || wc == '\\') break; /* Break >>> if end of the path or a separator is found */ >>> + if (wc < 0x80 && chk_chr("\"*:<>\?|\x7F", wc)) return >>> FR_INVALID_NAME; /* Reject illegal characters for LFN */ >>> + if (di >= FF_MAX_LFN) return FR_INVALID_NAME; /* Reject >>> too long name */ >>> + lfn[di++] = wc; /* Store the Unicode >>> character */ >>> + } >>> + if (wc < ' ') { /* End of path? */ >>> + cf = NS_LAST; /* Set last segment flag */ >>> + } else { >>> + cf = 0; /* Next segment follows */ >>> + while (*p == '/' || *p == '\\') p++; /* Skip duplicated >>> separators if exist */ >>> + } >>> + *path = p; /* Return pointer to the next >>> segment */ >>> + >>> +#if FF_FS_RPATH != 0 >>> + if ((di == 1 && lfn[di - 1] == '.') || >>> + (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* >>> Is this segment a dot name? */ >>> + lfn[di] = 0; >>> + for (i = 0; i < 11; i++) { /* Create dot name for SFN >>> entry */ >>> + dp->fn[i] = (i < di) ? '.' : ' '; >>> + } >>> + dp->fn[i] = cf | NS_DOT; /* This is a dot entry */ >>> + return FR_OK; >>> + } >>> +#endif >>> + while (di) { /* Snip off trailing spaces >>> and dots if exist */ >>> + wc = lfn[di - 1]; >>> + if (wc != ' ' && wc != '.') break; >>> + di--; >>> + } >>> + lfn[di] = 0; /* LFN is created into >>> the working buffer */ >>> + if (di == 0) return FR_INVALID_NAME; /* Reject null name */ >>> + >>> + /* Create SFN in directory form */ >>> + for (si = 0; lfn[si] == ' '; si++) ; /* Remove leading spaces */ >>> + if (si > 0 || lfn[si] == '.') cf |= NS_LOSS | NS_LFN; /* Is >>> there any leading space or dot? */ >>> + while (di > 0 && lfn[di - 1] != '.') di--; /* Find last dot >>> (di<=si: no extension) */ >>> + >>> + mem_set(dp->fn, ' ', 11); >>> + i = b = 0; ni = 8; >>> + for (;;) { >>> + wc = lfn[si++]; /* Get an LFN character */ >>> + if (wc == 0) break; /* Break on end of the >>> LFN */ >>> + if (wc == ' ' || (wc == '.' && si != di)) { /* Remove >>> embedded spaces and dots */ >>> + cf |= NS_LOSS | NS_LFN; >>> + continue; >>> + } >>> + >>> + if (i >= ni || si == di) { /* End of field? */ >>> + if (ni == 11) { /* Name extension >>> overflow? */ >>> + cf |= NS_LOSS | NS_LFN; >>> + break; >>> + } >>> + if (si != di) cf |= NS_LOSS | NS_LFN; /* Name body >>> overflow? */ >>> + if (si > di) break; /* No name >>> extension? */ >>> + si = di; i = 8; ni = 11; b <<= 2; /* Enter name >>> extension */ >>> + continue; >>> + } >>> + >>> + if (wc >= 0x80) { /* Is this a non-ASCII character? */ >>> + cf |= NS_LFN; /* LFN entry needs to be created */ >>> +#if FF_CODE_PAGE == 0 >>> + if (ExCvt) { /* At SBCS */ >>> + wc = ff_uni2oem(wc, CODEPAGE); /* Unicode >>> ==> ANSI/OEM code */ >>> + if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert >>> extended character to upper (SBCS) */ >>> + } else { /* At DBCS */ >>> + wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* >>> Unicode ==> Upper convert ==> ANSI/OEM code */ >>> + } >>> +#elif FF_CODE_PAGE < 900 /* SBCS cfg */ >>> + wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> >>> ANSI/OEM code */ >>> + if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert >>> extended character to upper (SBCS) */ >>> +#else /* DBCS cfg */ >>> + wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* >>> Unicode ==> Upper convert ==> ANSI/OEM code */ >>> +#endif >>> + } >>> + >>> + if (wc >= 0x100) { /* Is this a DBC? */ >>> + if (i >= ni - 1) { /* Field overflow? */ >>> + cf |= NS_LOSS | NS_LFN; >>> + i = ni; continue; /* Next field */ >>> + } >>> + dp->fn[i++] = (BYTE)(wc >> 8); /* Put 1st byte */ >>> + } else { /* SBC */ >>> + if (wc == 0 || chk_chr("+,;=[]", wc)) { /* Replace >>> illegal characters for SFN if needed */ >>> + wc = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ >>> + } else { >>> + if (IsUpper(wc)) { /* ASCII upper case? */ >>> + b |= 2; >>> + } >>> + if (IsLower(wc)) { /* ASCII lower case? */ >>> + b |= 1; wc -= 0x20; >>> + } >>> + } >>> + } >>> + dp->fn[i++] = (BYTE)wc; >>> + } >>> + >>> + if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first >>> character collides with DDEM, replace it with RDDEM */ >>> + >>> + if (ni == 8) b <<= 2; /* Shift capital flags if >>> no extension */ >>> + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; >>> /* LFN entry needs to be created if composite capitals */ >>> + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 >>> format without extended character, NT flags are created */ >>> + if (b & 0x01) cf |= NS_EXT; /* NT flag (Extension has >>> small capital letters only) */ >>> + if (b & 0x04) cf |= NS_BODY; /* NT flag (Body has small >>> capital letters only) */ >>> + } >>> + >>> + dp->fn[NSFLAG] = cf; /* SFN is created into dp->fn[] */ >>> + >>> + return FR_OK; >>> + >>> + >>> +#else /* FF_USE_LFN : Non-LFN configuration */ >>> + BYTE c, d, *sfn; >>> + UINT ni, si, i; >>> + const char *p; >>> + >>> + /* Create file name in directory form */ >>> + p = *path; sfn = dp->fn; >>> + mem_set(sfn, ' ', 11); >>> + si = i = 0; ni = 8; >>> +#if FF_FS_RPATH != 0 >>> + if (p[si] == '.') { /* Is this a dot entry? */ >>> + for (;;) { >>> + c = (BYTE)p[si++]; >>> + if (c != '.' || si >= 3) break; >>> + sfn[i++] = c; >>> + } >>> + if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; >>> + *path = p + si; /* Return >>> pointer to the next segment */ >>> + sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* >>> Set last segment flag if end of the path */ >>> + return FR_OK; >>> + } >>> +#endif >>> + for (;;) { >>> + c = (BYTE)p[si++]; /* Get a byte */ >>> + if (c <= ' ') break; /* Break if end of the path >>> name */ >>> + if (c == '/' || c == '\\') { /* Break if a separator is >>> found */ >>> + while (p[si] == '/' || p[si] == '\\') si++; /* Skip >>> duplicated separator if exist */ >>> + break; >>> + } >>> + if (c == '.' || i >= ni) { /* End of body or field >>> overflow? */ >>> + if (ni == 11 || c != '.') return FR_INVALID_NAME; /* >>> Field overflow or invalid dot? */ >>> + i = 8; ni = 11; /* Enter file extension >>> field */ >>> + continue; >>> + } >>> +#if FF_CODE_PAGE == 0 >>> + if (ExCvt && c >= 0x80) { /* Is SBC extended >>> character? */ >>> + c = ExCvt[c & 0x7F]; /* To upper SBC extended >>> character */ >>> + } >>> +#elif FF_CODE_PAGE < 900 >>> + if (c >= 0x80) { /* Is SBC extended >>> character? */ >>> + c = ExCvt[c & 0x7F]; /* To upper SBC extended >>> character */ >>> + } >>> +#endif >>> + if (dbc_1st(c)) { /* Check if it is a DBC 1st >>> byte */ >>> + d = (BYTE)p[si++]; /* Get 2nd byte */ >>> + if (!dbc_2nd(d) || i >= ni - 1) return >>> FR_INVALID_NAME; /* Reject invalid DBC */ >>> + sfn[i++] = c; >>> + sfn[i++] = d; >>> + } else { /* SBC */ >>> + if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return >>> FR_INVALID_NAME; /* Reject illegal chrs for SFN */ >>> + if (IsLower(c)) c -= 0x20; /* To upper */ >>> + sfn[i++] = c; >>> + } >>> + } >>> + *path = p + si; /* Return pointer to the >>> next segment */ >>> + if (i == 0) return FR_INVALID_NAME; /* Reject nul string */ >>> + >>> + if (sfn[0] == DDEM) sfn[0] = RDDEM; /* If the first character >>> collides with DDEM, replace it with RDDEM */ >>> + sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0; /* Set last >>> segment flag if end of the path */ >>> + >>> + return FR_OK; >>> +#endif /* FF_USE_LFN */ >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Follow a file >>> path */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error >>> code */ >>> + DIR* dp, /* Directory object to return last >>> directory and found object */ >>> + const TCHAR* path /* Full-path string to find a file >>> or directory */ >>> +) >>> +{ >>> + FRESULT res; >>> + BYTE ns; >>> + FATFS *fs = dp->obj.fs; >>> + >>> + >>> +#if FF_FS_RPATH != 0 >>> + if (*path != '/' && *path != '\\') { /* Without heading >>> separator */ >>> + dp->obj.sclust = fs->cdir; /* Start from >>> current directory */ >>> + } else >>> +#endif >>> + { /* With heading >>> separator */ >>> + while (*path == '/' || *path == '\\') path++; /* Strip >>> heading separator */ >>> + dp->obj.sclust = 0; /* Start from root >>> directory */ >>> + } >>> +#if FF_FS_EXFAT >>> + dp->obj.n_frag = 0; /* Invalidate last fragment counter of >>> the object */ >>> +#if FF_FS_RPATH != 0 >>> + if (fs->fs_type == FS_EXFAT && dp->obj.sclust) { /* exFAT: >>> Retrieve the sub-directory's status */ >>> + DIR dj; >>> + >>> + dp->obj.c_scl = fs->cdc_scl; >>> + dp->obj.c_size = fs->cdc_size; >>> + dp->obj.c_ofs = fs->cdc_ofs; >>> + res = load_obj_xdir(&dj, &dp->obj); >>> + if (res != FR_OK) return res; >>> + dp->obj.objsize = ld_dword(fs->dirbuf + XDIR_FileSize); >>> + dp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; >>> + } >>> +#endif >>> +#endif >>> + >>> + if ((UINT)*path < ' ') { /* Null path name is the >>> origin directory itself */ >>> + dp->fn[NSFLAG] = NS_NONAME; >>> + res = dir_sdi(dp, 0); >>> + >>> + } else { /* Follow path */ >>> + for (;;) { >>> + res = create_name(dp, &path); /* Get a segment name >>> of the path */ >>> + if (res != FR_OK) break; >>> + res = dir_find(dp); /* Find an object >>> with the segment name */ >>> + ns = dp->fn[NSFLAG]; >>> + if (res != FR_OK) { /* Failed to find the >>> object */ >>> + if (res == FR_NO_FILE) { /* Object is not found */ >>> + if (FF_FS_RPATH && (ns & NS_DOT)) { /* If dot >>> entry is not exist, stay there */ >>> + if (!(ns & NS_LAST)) continue; /* >>> Continue to follow if not last segment */ >>> + dp->fn[NSFLAG] = NS_NONAME; >>> + res = FR_OK; >>> + } else { /* Could not >>> find the object */ >>> + if (!(ns & NS_LAST)) res = FR_NO_PATH; /* >>> Adjust error code if not last segment */ >>> + } >>> + } >>> + break; >>> + } >>> + if (ns & NS_LAST) break; /* Last segment >>> matched. Function completed. */ >>> + /* Get into the sub-directory */ >>> + if (!(dp->obj.attr & AM_DIR)) { /* It is not a >>> sub-directory and cannot follow */ >>> + res = FR_NO_PATH; break; >>> + } >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { /* Save containing >>> directory information for next dir */ >>> + dp->obj.c_scl = dp->obj.sclust; >>> + dp->obj.c_size = ((DWORD)dp->obj.objsize & >>> 0xFFFFFF00) | dp->obj.stat; >>> + dp->obj.c_ofs = dp->blk_ofs; >>> + init_alloc_info(fs, &dp->obj); /* Open next >>> directory */ >>> + } else >>> +#endif >>> + { >>> + dp->obj.sclust = ld_clust(fs, fs->win + dp->dptr % >>> SS(fs)); /* Open next directory */ >>> + } >>> + } >>> + } >>> + >>> + return res; >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Get logical drive number from path >>> name */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static int get_ldnumber ( /* Returns logical drive number >>> (-1:invalid drive number or null pointer) */ >>> + const TCHAR** path /* Pointer to pointer to the path name */ >>> +) >>> +{ >>> + const TCHAR *tp, *tt; >>> + TCHAR tc; >>> + int i, vol = -1; >>> +#if FF_STR_VOLUME_ID /* Find string volume ID */ >>> + const char *sp; >>> + char c; >>> +#endif >>> + >>> + tt = tp = *path; >>> + if (!tp) return vol; /* Invalid path name? */ >>> + do tc = *tt++; while ((UINT)tc >= (FF_USE_LFN ? ' ' : '!') && tc >>> != ':'); /* Find a colon in the path */ >>> + >>> + if (tc == ':') { /* DOS/Windows style volume ID? */ >>> + i = FF_VOLUMES; >>> + if (IsDigit(*tp) && tp + 2 == tt) { /* Is there a numeric >>> volume ID + colon? */ >>> + i = (int)*tp - '0'; /* Get the LD number */ >>> + } >>> +#if FF_STR_VOLUME_ID == 1 /* Arbitrary string is enabled */ >>> + else { >>> + i = 0; >>> + do { >>> + sp = VolumeStr[i]; tp = *path; /* This string >>> volume ID and path name */ >>> + do { /* Compare the volume ID with path name */ >>> + c = *sp++; tc = *tp++; >>> + if (IsLower(c)) c -= 0x20; >>> + if (IsLower(tc)) tc -= 0x20; >>> + } while (c && (TCHAR)c == tc); >>> + } while ((c || tp != tt) && ++i < FF_VOLUMES); /* >>> Repeat for each id until pattern match */ >>> + } >>> +#endif >>> + if (i < FF_VOLUMES) { /* If a volume ID is found, get the >>> drive number and strip it */ >>> + vol = i; /* Drive number */ >>> + *path = tt; /* Snip the drive prefix off */ >>> + } >>> + return vol; >>> + } >>> +#if FF_STR_VOLUME_ID == 2 /* Unix style volume ID is enabled */ >>> + if (*tp == '/') { >>> + i = 0; >>> + do { >>> + sp = VolumeStr[i]; tp = *path; /* This string volume >>> ID and path name */ >>> + do { /* Compare the volume ID with path name */ >>> + c = *sp++; tc = *(++tp); >>> + if (IsLower(c)) c -= 0x20; >>> + if (IsLower(tc)) tc -= 0x20; >>> + } while (c && (TCHAR)c == tc); >>> + } while ((c || (tc != '/' && (UINT)tc >= (FF_USE_LFN ? ' ' : >>> '!'))) && ++i < FF_VOLUMES); /* Repeat for each ID until pattern >>> match */ >>> + if (i < FF_VOLUMES) { /* If a volume ID is found, get the >>> drive number and strip it */ >>> + vol = i; /* Drive number */ >>> + *path = tp; /* Snip the drive prefix off */ >>> + return vol; >>> + } >>> + } >>> +#endif >>> + /* No drive prefix is found */ >>> +#if FF_FS_RPATH != 0 >>> + vol = CurrVol; /* Default drive is current drive */ >>> +#else >>> + vol = 0; /* Default drive is 0 */ >>> +#endif >>> + return vol; /* Return the default drive */ >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* GPT support >>> functions */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +#if FF_LBA64 >>> + >>> +/* Calculate CRC32 in byte-by-byte */ >>> + >>> +static DWORD crc32 ( /* Returns next CRC value */ >>> + DWORD crc, /* Current CRC value */ >>> + BYTE d /* A byte to be processed */ >>> +) >>> +{ >>> + BYTE b; >>> + >>> + >>> + for (b = 1; b; b <<= 1) { >>> + crc ^= (d & b) ? 1 : 0; >>> + crc = (crc & 1) ? crc >> 1 ^ 0xEDB88320 : crc >> 1; >>> + } >>> + return crc; >>> +} >>> + >>> + >>> +/* Check validity of GPT header */ >>> + >>> +static int test_gpt_header ( /* 0:Invalid, 1:Valid */ >>> + const BYTE* gpth /* Pointer to the GPT header */ >>> +) >>> +{ >>> + UINT i; >>> + DWORD bcc; >>> + >>> + >>> + if (mem_cmp(gpth + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", >>> 16)) return 0; /* Check sign, version (1.0) and length (92) */ >>> + for (i = 0, bcc = 0xFFFFFFFF; i < 92; i++) { /* Check >>> header BCC */ >>> + bcc = crc32(bcc, i - GPTH_Bcc < 4 ? 0 : gpth[i]); >>> + } >>> + if (~bcc != ld_dword(gpth + GPTH_Bcc)) return 0; >>> + if (ld_dword(gpth + GPTH_PteSize) != SZ_GPTE) return 0; /* >>> Table entry size (must be SZ_GPTE bytes) */ >>> + if (ld_dword(gpth + GPTH_PtNum) > 128) return 0; /* Table >>> size (must be 128 entries or less) */ >>> + >>> + return 1; >>> +} >>> + >>> +#if !FF_FS_READONLY && FF_USE_MKFS >>> + >>> +/* Generate random value */ >>> +static DWORD make_rand ( >>> + DWORD seed, /* Seed value */ >>> + BYTE* buff, /* Output buffer */ >>> + UINT n /* Data length */ >>> +) >>> +{ >>> + UINT r; >>> + >>> + >>> + if (seed == 0) seed = 1; >>> + do { >>> + for (r = 0; r < 8; r++) seed = seed & 1 ? seed >> 1 ^ >>> 0xA3000000 : seed >> 1; /* Shift 8 bits the 32-bit LFSR */ >>> + *buff++ = (BYTE)seed; >>> + } while (--n); >>> + return seed; >>> +} >>> + >>> +#endif >>> +#endif >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Load a sector and check if it is an FAT >>> VBR */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +/* Check what the sector is */ >>> + >>> +static UINT check_fs ( /* 0:FAT VBR, 1:exFAT VBR, 2:Not FAT and >>> valid BS, 3:Not FAT and invalid BS, 4:Disk error */ >>> + FATFS* fs, /* Filesystem object */ >>> + LBA_t sect /* Sector to load and check if it is an >>> FAT-VBR or not */ >>> +) >>> +{ >>> + WORD w, sign; >>> + BYTE b; >>> + >>> + >>> + fs->wflag = 0; fs->winsect = (LBA_t)0 - 1; /* Invaidate >>> window */ >>> + if (move_window(fs, sect) != FR_OK) return 4; /* Load the >>> boot sector */ >>> + sign = ld_word(fs->win + BS_55AA); >>> +#if FF_FS_EXFAT >>> + if (sign == 0xAA55 && !mem_cmp(fs->win + BS_JmpBoot, >>> "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* It is an exFAT VBR */ >>> +#endif >>> + b = fs->win[BS_JmpBoot]; >>> + if (b == 0xEB || b == 0xE9 || b == 0xE8) { /* Valid JumpBoot >>> code? (short jump, near jump or near call) */ >>> + if (sign == 0xAA55 && !mem_cmp(fs->win + BS_FilSysType32, >>> "FAT32 ", 8)) return 0; /* It is an FAT32 VBR */ >>> + /* FAT volumes formatted with early MS-DOS lack boot >>> signature and FAT string, so that we need to identify the FAT VBR >>> without them. */ >>> + w = ld_word(fs->win + BPB_BytsPerSec); >>> + if ((w & (w - 1)) == 0 && w >= FF_MIN_SS && w <= FF_MAX_SS) >>> { /* Properness of sector size */ >>> + b = fs->win[BPB_SecPerClus]; >>> + if (b != 0 && (b & (b - 1)) == 0 /* Properness of >>> cluster size */ >>> + && (fs->win[BPB_NumFATs] == 1 || fs->win[BPB_NumFATs] == >>> 2) /* Properness of number of FATs */ >>> + && ld_word(fs->win + BPB_RootEntCnt) != 0 /* Properness >>> of root entry count */ >>> + && ld_word(fs->win + BPB_FATSz16) != 0) { /* Properness >>> of FAT size */ >>> + return 0; /* Sector can be presumed an FAT VBR */ >>> + } >>> + } >>> + } >>> + return sign == 0xAA55 ? 2 : 3; /* Not an FAT VBR (valid or >>> invalid BS) */ >>> +} >>> + >>> + >>> +/* Find an FAT volume */ >>> +/* (It supports only generic partitioning rules, MBR, GPT and SFD) */ >>> + >>> +static UINT find_volume ( /* Returns BS status found in the >>> hosting drive */ >>> + FATFS* fs, /* Filesystem object */ >>> + UINT part /* Partition to fined = 0:auto, 1..:forced */ >>> +) >>> +{ >>> + UINT fmt, i; >>> + DWORD mbr_pt[4]; >>> + >>> + >>> + fmt = check_fs(fs, 0); /* Load sector 0 and check >>> if it is an FAT VBR as SFD */ >>> + if (fmt != 2 && (fmt >= 3 || part == 0)) return fmt; /* >>> Returns if it is a FAT VBR as auto scan, not a BS or disk error */ >>> + >>> + /* Sector 0 is not an FAT VBR or forced partition number wants a >>> partition */ >>> + >>> +#if FF_LBA64 >>> + if (fs->win[MBR_Table + PTE_System] == 0xEE) { /* GPT >>> protective MBR? */ >>> + DWORD n_ent, v_ent, ofs; >>> + QWORD pt_lba; >>> + >>> + if (move_window(fs, 1) != FR_OK) return 4; /* Load GPT >>> header sector (next to MBR) */ >>> + if (!test_gpt_header(fs->win)) return 3; /* Check if GPT >>> header is valid */ >>> + n_ent = ld_dword(fs->win + GPTH_PtNum); /* Number of >>> entries */ >>> + pt_lba = ld_qword(fs->win + GPTH_PtOfs); /* Table >>> location */ >>> + for (v_ent = i = 0; i < n_ent; i++) { /* Find FAT >>> partition */ >>> + if (move_window(fs, pt_lba + i * SZ_GPTE / SS(fs)) != >>> FR_OK) return 4; /* PT sector */ >>> + ofs = i * SZ_GPTE % >>> SS(fs); /* Offset in >>> the sector */ >>> + if (!mem_cmp(fs->win + ofs + GPTE_PtGuid, GUID_MS_Basic, >>> 16)) { /* MS basic data partition? */ >>> + v_ent++; >>> + fmt = check_fs(fs, ld_qword(fs->win + ofs + >>> GPTE_FstLba)); /* Load VBR and check status */ >>> + if (part == 0 && fmt <= 1) return fmt; /* >>> Auto search (valid FAT volume found first) */ >>> + if (part != 0 && v_ent == part) return fmt; >>> /* Forced partition order (regardless of it is valid or not) */ >>> + } >>> + } >>> + return 3; /* Not found */ >>> + } >>> +#endif >>> + if (FF_MULTI_PARTITION && part > 4) return 3; /* MBR has 4 >>> partitions max */ >>> + for (i = 0; i < 4; i++) { /* Load partition offset in the >>> MBR */ >>> + mbr_pt[i] = ld_dword(fs->win + MBR_Table + i * SZ_PTE + >>> PTE_StLba); >>> + } >>> + i = part ? part - 1 : 0; /* Table index to find first */ >>> + do { /* Find an FAT volume */ >>> + fmt = mbr_pt[i] ? check_fs(fs, mbr_pt[i]) : 3; /* Check >>> if the partition is FAT */ >>> + } while (part == 0 && fmt >= 2 && ++i < 4); >>> + return fmt; >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Determine logical drive number and mount the volume if >>> needed */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an >>> error occurred */ >>> + const TCHAR** path, /* Pointer to pointer to the path >>> name (drive number) */ >>> + FATFS** rfs, /* Pointer to pointer to the found >>> filesystem object */ >>> + BYTE mode /* !=0: Check write protection for >>> write access */ >>> +) >>> +{ >>> + int vol; >>> + DSTATUS stat; >>> + LBA_t bsect; >>> + DWORD tsect, sysect, fasize, nclst, szbfat; >>> + WORD nrsv; >>> + FATFS *fs; >>> + UINT fmt; >>> + >>> + >>> + /* Get logical drive number */ >>> + *rfs = 0; >>> + vol = get_ldnumber(path); >>> + if (vol < 0) return FR_INVALID_DRIVE; >>> + >>> + /* Check if the filesystem object is valid or not */ >>> + fs = FatFs[vol]; /* Get pointer to the >>> filesystem object */ >>> + if (!fs) return FR_NOT_ENABLED; /* Is the filesystem >>> object available? */ >>> +#if FF_FS_REENTRANT >>> + if (!lock_fs(fs)) return FR_TIMEOUT; /* Lock the volume */ >>> +#endif >>> + *rfs = fs; /* Return pointer to the >>> filesystem object */ >>> + >>> + mode &= (BYTE)~FA_READ; /* Desired access mode, >>> write access or not */ >>> + if (fs->fs_type != 0) { /* If the volume has been >>> mounted */ >>> + stat = disk_status(fs->pdrv); >>> + if (!(stat & STA_NOINIT)) { /* and the physical drive >>> is kept initialized */ >>> + if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { >>> /* Check write protection if needed */ >>> + return FR_WRITE_PROTECTED; >>> + } >>> + return FR_OK; /* The filesystem object is >>> already valid */ >>> + } >>> + } >>> + >>> + /* The filesystem object is not valid. */ >>> + /* Following code attempts to mount the volume. (find a FAT >>> volume, analyze the BPB and initialize the filesystem object) */ >>> + >>> + fs->fs_type = 0; /* Clear the filesystem >>> object */ >>> + fs->pdrv = LD2PD(vol); /* Volume hosting physical >>> drive */ >>> + stat = disk_initialize(fs->pdrv); /* Initialize the physical >>> drive */ >>> + if (stat & STA_NOINIT) { /* Check if the >>> initialization succeeded */ >>> + return FR_NOT_READY; /* Failed to initialize due >>> to no medium or hard error */ >>> + } >>> + if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check >>> disk write protection if needed */ >>> + return FR_WRITE_PROTECTED; >>> + } >>> +#if FF_MAX_SS != FF_MIN_SS /* Get sector size >>> (multiple sector size cfg only) */ >>> + if (disk_ioctl(fs->pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) >>> return FR_DISK_ERR; >>> + if (SS(fs) > FF_MAX_SS || SS(fs) < FF_MIN_SS || (SS(fs) & >>> (SS(fs) - 1))) return FR_DISK_ERR; >>> +#endif >>> + >>> + /* Find an FAT volume on the drive */ >>> + fmt = find_volume(fs, LD2PT(vol)); >>> + if (fmt == 4) return FR_DISK_ERR; /* An error occured in >>> the disk I/O layer */ >>> + if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is >>> found */ >>> + bsect = fs->winsect; /* Volume location */ >>> + >>> + /* An FAT volume is found (bsect). Following code initializes >>> the filesystem object */ >>> + >>> +#if FF_FS_EXFAT >>> + if (fmt == 1) { >>> + QWORD maxlba; >>> + DWORD so, cv, bcl, i; >>> + >>> + for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] >>> == 0; i++) ; /* Check zero filler */ >>> + if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM; >>> + >>> + if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return >>> FR_NO_FILESYSTEM; /* Check exFAT version (must be version 1.0) */ >>> + >>> + if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) { /* >>> (BPB_BytsPerSecEx must be equal to the physical sector size) */ >>> + return FR_NO_FILESYSTEM; >>> + } >>> + >>> + maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* >>> Last LBA + 1 of the volume */ >>> + if (!FF_LBA64 && maxlba >= 0x100000000) return >>> FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */ >>> + >>> + fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of >>> sectors per FAT */ >>> + >>> + fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of >>> FATs */ >>> + if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* >>> (Supports only 1 FAT) */ >>> + >>> + fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* >>> Cluster size */ >>> + if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must >>> be 1..32768) */ >>> + >>> + nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number >>> of clusters */ >>> + if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too >>> many clusters) */ >>> + fs->n_fatent = nclst + 2; >>> + >>> + /* Boundaries and Limits */ >>> + fs->volbase = bsect; >>> + fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx); >>> + fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx); >>> + if (maxlba < (QWORD)fs->database + nclst * fs->csize) return >>> FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the >>> size requiered) */ >>> + fs->dirbase = ld_dword(fs->win + BPB_RootClusEx); >>> + >>> + /* Get bitmap location and check if it is contiguous >>> (implementation assumption) */ >>> + so = i = 0; >>> + for (;;) { /* Find the bitmap entry in the root directory >>> (in only first cluster) */ >>> + if (i == 0) { >>> + if (so >= fs->csize) return FR_NO_FILESYSTEM; /* >>> Not found? */ >>> + if (move_window(fs, clst2sect(fs, >>> (DWORD)fs->dirbase) + so) != FR_OK) return FR_DISK_ERR; >>> + so++; >>> + } >>> + if (fs->win[i] == ET_BITMAP) break; /* Is >>> it a bitmap entry? */ >>> + i = (i + SZDIRE) % SS(fs); /* Next entry */ >>> + } >>> + bcl = ld_dword(fs->win + i + 20); /* >>> Bitmap cluster */ >>> + if (bcl < 2 || bcl >= fs->n_fatent) return FR_NO_FILESYSTEM; >>> + fs->bitbase = fs->database + fs->csize * (bcl - 2); /* >>> Bitmap sector */ >>> + for (;;) { /* Check if bitmap is contiguous */ >>> + if (move_window(fs, fs->fatbase + bcl / (SS(fs) / 4)) != >>> FR_OK) return FR_DISK_ERR; >>> + cv = ld_dword(fs->win + bcl % (SS(fs) / 4) * 4); >>> + if (cv == 0xFFFFFFFF) break; /* Last >>> link? */ >>> + if (cv != ++bcl) return FR_NO_FILESYSTEM; /* >>> Fragmented? */ >>> + } >>> + >>> +#if !FF_FS_READONLY >>> + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* >>> Initialize cluster allocation information */ >>> +#endif >>> + fmt = FS_EXFAT; /* FAT sub-type */ >>> + } else >>> +#endif /* FF_FS_EXFAT */ >>> + { >>> + if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return >>> FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical >>> sector size) */ >>> + >>> + fasize = ld_word(fs->win + BPB_FATSz16); /* Number of >>> sectors per FAT */ >>> + if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32); >>> + fs->fsize = fasize; >>> + >>> + fs->n_fats = fs->win[BPB_NumFATs]; /* Number >>> of FATs */ >>> + if (fs->n_fats != 1 && fs->n_fats != 2) return >>> FR_NO_FILESYSTEM; /* (Must be 1 or 2) */ >>> + fasize *= fs->n_fats; /* Number >>> of sectors for FAT area */ >>> + >>> + fs->csize = fs->win[BPB_SecPerClus]; /* Cluster >>> size */ >>> + if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return >>> FR_NO_FILESYSTEM; /* (Must be power of 2) */ >>> + >>> + fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* >>> Number of root directory entries */ >>> + if (fs->n_rootdir % (SS(fs) / SZDIRE)) return >>> FR_NO_FILESYSTEM; /* (Must be sector aligned) */ >>> + >>> + tsect = ld_word(fs->win + BPB_TotSec16); /* Number of >>> sectors on the volume */ >>> + if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32); >>> + >>> + nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number >>> of reserved sectors */ >>> + if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must >>> not be 0) */ >>> + >>> + /* Determine the FAT sub type */ >>> + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / >>> SZDIRE); /* RSV + FAT + DIR */ >>> + if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid >>> volume size) */ >>> + nclst = (tsect - sysect) / fs->csize; /* Number >>> of clusters */ >>> + if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid >>> volume size) */ >>> + fmt = 0; >>> + if (nclst <= MAX_FAT32) fmt = FS_FAT32; >>> + if (nclst <= MAX_FAT16) fmt = FS_FAT16; >>> + if (nclst <= MAX_FAT12) fmt = FS_FAT12; >>> + if (fmt == 0) return FR_NO_FILESYSTEM; >>> + >>> + /* Boundaries and Limits */ >>> + fs->n_fatent = nclst + 2; /* Number >>> of FAT entries */ >>> + fs->volbase = bsect; /* Volume >>> start sector */ >>> + fs->fatbase = bsect + nrsv; /* FAT start >>> sector */ >>> + fs->database = bsect + sysect; /* Data >>> start sector */ >>> + if (fmt == FS_FAT32) { >>> + if (ld_word(fs->win + BPB_FSVer32) != 0) return >>> FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */ >>> + if (fs->n_rootdir != 0) return FR_NO_FILESYSTEM; /* >>> (BPB_RootEntCnt must be 0) */ >>> + fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* >>> Root directory start cluster */ >>> + szbfat = fs->n_fatent * 4; /* (Needed >>> FAT size) */ >>> + } else { >>> + if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM; >>> /* (BPB_RootEntCnt must not be 0) */ >>> + fs->dirbase = fs->fatbase + fasize; /* Root >>> directory start sector */ >>> + szbfat = (fmt == FS_FAT16) ? /* (Needed >>> FAT size) */ >>> + fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + >>> (fs->n_fatent & 1); >>> + } >>> + if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return >>> FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size >>> needed) */ >>> + >>> +#if !FF_FS_READONLY >>> + /* Get FSInfo if available */ >>> + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* >>> Initialize cluster allocation information */ >>> + fs->fsi_flag = 0x80; >>> +#if (FF_FS_NOFSINFO & 3) != 3 >>> + if (fmt == FS_FAT32 /* Allow to update FSInfo >>> only if BPB_FSInfo32 == 1 */ >>> + && ld_word(fs->win + BPB_FSInfo32) == 1 >>> + && move_window(fs, bsect + 1) == FR_OK) >>> + { >>> + fs->fsi_flag = 0; >>> + if (ld_word(fs->win + BS_55AA) == 0xAA55 /* Load >>> FSInfo data if available */ >>> + && ld_dword(fs->win + FSI_LeadSig) == 0x41615252 >>> + && ld_dword(fs->win + FSI_StrucSig) == 0x61417272) >>> + { >>> +#if (FF_FS_NOFSINFO & 1) == 0 >>> + fs->free_clst = ld_dword(fs->win + FSI_Free_Count); >>> +#endif >>> +#if (FF_FS_NOFSINFO & 2) == 0 >>> + fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free); >>> +#endif >>> + } >>> + } >>> +#endif /* (FF_FS_NOFSINFO & 3) != 3 */ >>> +#endif /* !FF_FS_READONLY */ >>> + } >>> + >>> + fs->fs_type = (BYTE)fmt;/* FAT sub-type */ >>> + fs->id = ++Fsid; /* Volume mount ID */ >>> +#if FF_USE_LFN == 1 >>> + fs->lfnbuf = LfnBuf; /* Static LFN working buffer */ >>> +#if FF_FS_EXFAT >>> + fs->dirbuf = DirBuf; /* Static directory block scratchpad >>> buuffer */ >>> +#endif >>> +#endif >>> +#if FF_FS_RPATH != 0 >>> + fs->cdir = 0; /* Initialize current directory */ >>> +#endif >>> +#if FF_FS_LOCK != 0 /* Clear file lock semaphores */ >>> + clear_lock(fs); >>> +#endif >>> + return FR_OK; >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Check if the file/directory object is valid or >>> not */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ >>> + FFOBJID* obj, /* Pointer to the FFOBJID, the 1st >>> member in the FIL/DIR object, to check validity */ >>> + FATFS** rfs /* Pointer to pointer to the owner >>> filesystem object to return */ >>> +) >>> +{ >>> + FRESULT res = FR_INVALID_OBJECT; >>> + >>> + >>> + if (obj && obj->fs && obj->fs->fs_type && obj->id == >>> obj->fs->id) { /* Test if the object is valid */ >>> +#if FF_FS_REENTRANT >>> + if (lock_fs(obj->fs)) { /* Obtain the filesystem object */ >>> + if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* >>> Test if the phsical drive is kept initialized */ >>> + res = FR_OK; >>> + } else { >>> + unlock_fs(obj->fs, FR_OK); >>> + } >>> + } else { >>> + res = FR_TIMEOUT; >>> + } >>> +#else >>> + if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if >>> the phsical drive is kept initialized */ >>> + res = FR_OK; >>> + } >>> +#endif >>> + } >>> + *rfs = (res == FR_OK) ? obj->fs : 0; /* Corresponding >>> filesystem object */ >>> + return res; >>> +} >>> + >>> + >>> + >>> + >>> +/*--------------------------------------------------------------------------- >>> >>> + >>> + Public Functions (FatFs API) >>> + >>> +----------------------------------------------------------------------------*/ >>> >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Mount/Unmount a Logical >>> Drive */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_mount ( >>> + FATFS* fs, /* Pointer to the filesystem object >>> (NULL:unmount)*/ >>> + const TCHAR* path, /* Logical drive number to be >>> mounted/unmounted */ >>> + BYTE opt /* Mode option 0:Do not mount (delayed >>> mount), 1:Mount immediately */ >>> +) >>> +{ >>> + FATFS *cfs; >>> + int vol; >>> + FRESULT res; >>> + const TCHAR *rp = path; >>> + >>> + >>> + /* Get logical drive number */ >>> + vol = get_ldnumber(&rp); >>> + if (vol < 0) return FR_INVALID_DRIVE; >>> + cfs = FatFs[vol]; /* Pointer to fs object */ >>> + >>> + if (cfs) { >>> +#if FF_FS_LOCK != 0 >>> + clear_lock(cfs); >>> +#endif >>> +#if FF_FS_REENTRANT /* Discard sync object of >>> the current volume */ >>> + if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR; >>> +#endif >>> + cfs->fs_type = 0; /* Clear old fs object */ >>> + } >>> + >>> + if (fs) { >>> + fs->fs_type = 0; /* Clear new fs object */ >>> +#if FF_FS_REENTRANT /* Create sync object for >>> the new volume */ >>> + if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR; >>> +#endif >>> + } >>> + FatFs[vol] = fs; /* Register new fs object */ >>> + >>> + if (opt == 0) return FR_OK; /* Do not mount now, it >>> will be mounted later */ >>> + >>> + res = mount_volume(&path, &fs, 0); /* Force mounted the >>> volume */ >>> + LEAVE_FF(fs, res); >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Open or Create a >>> File */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_open ( >>> + FIL* fp, /* Pointer to the blank file object */ >>> + const TCHAR* path, /* Pointer to the file name */ >>> + BYTE mode /* Access mode and file open mode flags */ >>> +) >>> +{ >>> + FRESULT res; >>> + DIR dj; >>> + FATFS *fs; >>> +#if !FF_FS_READONLY >>> + DWORD cl, bcs, clst, tm; >>> + LBA_t sc; >>> + FSIZE_t ofs; >>> +#endif >>> + DEF_NAMBUF >>> + >>> + >>> + if (!fp) return FR_INVALID_OBJECT; >>> + >>> + /* Get logical drive number */ >>> + mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | >>> FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND; >>> + res = mount_volume(&path, &fs, mode); >>> + if (res == FR_OK) { >>> + dj.obj.fs = fs; >>> + INIT_NAMBUF(fs); >>> + res = follow_path(&dj, path); /* Follow the file path */ >>> +#if !FF_FS_READONLY /* Read/Write configuration */ >>> + if (res == FR_OK) { >>> + if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory >>> itself? */ >>> + res = FR_INVALID_NAME; >>> + } >>> +#if FF_FS_LOCK != 0 >>> + else { >>> + res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : >>> 0); /* Check if the file can be used */ >>> + } >>> +#endif >>> + } >>> + /* Create or Open a file */ >>> + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | >>> FA_CREATE_NEW)) { >>> + if (res != FR_OK) { /* No file, >>> create new */ >>> + if (res == FR_NO_FILE) { /* There is no file >>> to open, create a new entry */ >>> +#if FF_FS_LOCK != 0 >>> + res = enq_lock() ? dir_register(&dj) : >>> FR_TOO_MANY_OPEN_FILES; >>> +#else >>> + res = dir_register(&dj); >>> +#endif >>> + } >>> + mode |= FA_CREATE_ALWAYS; /* File is created */ >>> + } >>> + else { /* Any object with >>> the same name is already existing */ >>> + if (dj.obj.attr & (AM_RDO | AM_DIR)) { /* Cannot >>> overwrite it (R/O or DIR) */ >>> + res = FR_DENIED; >>> + } else { >>> + if (mode & FA_CREATE_NEW) res = FR_EXIST; /* >>> Cannot create as new file */ >>> + } >>> + } >>> + if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* >>> Truncate the file if overwrite mode */ >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { >>> + /* Get current allocation info */ >>> + fp->obj.fs = fs; >>> + init_alloc_info(fs, &fp->obj); >>> + /* Set directory entry block initial state */ >>> + mem_set(fs->dirbuf + 2, 0, 30); /* Clear >>> 85 entry except for NumSec */ >>> + mem_set(fs->dirbuf + 38, 0, 26); /* Clear C0 >>> entry except for NumName and NameHash */ >>> + fs->dirbuf[XDIR_Attr] = AM_ARC; >>> + st_dword(fs->dirbuf + XDIR_CrtTime, GET_FATTIME()); >>> + fs->dirbuf[XDIR_GenFlags] = 1; >>> + res = store_xdir(&dj); >>> + if (res == FR_OK && fp->obj.sclust != 0) { /* >>> Remove the cluster chain if exist */ >>> + res = remove_chain(&fp->obj, fp->obj.sclust, >>> 0); >>> + fs->last_clst = fp->obj.sclust - 1; >>> /* Reuse the cluster hole */ >>> + } >>> + } else >>> +#endif >>> + { >>> + /* Set directory entry initial state */ >>> + tm = GET_FATTIME(); /* Set >>> created time */ >>> + st_dword(dj.dir + DIR_CrtTime, tm); >>> + st_dword(dj.dir + DIR_ModTime, tm); >>> + cl = ld_clust(fs, dj.dir); /* Get >>> current cluster chain */ >>> + dj.dir[DIR_Attr] = AM_ARC; /* Reset >>> attribute */ >>> + st_clust(fs, dj.dir, 0); /* Reset >>> file allocation info */ >>> + st_dword(dj.dir + DIR_FileSize, 0); >>> + fs->wflag = 1; >>> + if (cl != 0) { /* Remove >>> the cluster chain if exist */ >>> + sc = fs->winsect; >>> + res = remove_chain(&dj.obj, cl, 0); >>> + if (res == FR_OK) { >>> + res = move_window(fs, sc); >>> + fs->last_clst = cl - 1; /* Reuse >>> the cluster hole */ >>> + } >>> + } >>> + } >>> + } >>> + } >>> + else { /* Open an existing file */ >>> + if (res == FR_OK) { /* Is the object >>> exsiting? */ >>> + if (dj.obj.attr & AM_DIR) { /* File open >>> against a directory */ >>> + res = FR_NO_FILE; >>> + } else { >>> + if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) >>> { /* Write mode open against R/O file */ >>> + res = FR_DENIED; >>> + } >>> + } >>> + } >>> + } >>> + if (res == FR_OK) { >>> + if (mode & FA_CREATE_ALWAYS) mode |= FA_MODIFIED; /* >>> Set file change flag if created or overwritten */ >>> + fp->dir_sect = fs->winsect; /* Pointer to the >>> directory entry */ >>> + fp->dir_ptr = dj.dir; >>> +#if FF_FS_LOCK != 0 >>> + fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : >>> 0); /* Lock the file for this session */ >>> + if (fp->obj.lockid == 0) res = FR_INT_ERR; >>> +#endif >>> + } >>> +#else /* R/O configuration */ >>> + if (res == FR_OK) { >>> + if (dj.fn[NSFLAG] & NS_NONAME) { /* Is it origin >>> directory itself? */ >>> + res = FR_INVALID_NAME; >>> + } else { >>> + if (dj.obj.attr & AM_DIR) { /* Is it a >>> directory? */ >>> + res = FR_NO_FILE; >>> + } >>> + } >>> + } >>> +#endif >>> + >>> + if (res == FR_OK) { >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { >>> + fp->obj.c_scl = >>> dj.obj.sclust; /* Get containing directory >>> info */ >>> + fp->obj.c_size = ((DWORD)dj.obj.objsize & >>> 0xFFFFFF00) | dj.obj.stat; >>> + fp->obj.c_ofs = dj.blk_ofs; >>> + init_alloc_info(fs, &fp->obj); >>> + } else >>> +#endif >>> + { >>> + fp->obj.sclust = ld_clust(fs, >>> dj.dir); /* Get object allocation info */ >>> + fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize); >>> + } >>> +#if FF_USE_FASTSEEK >>> + fp->cltbl = 0; /* Disable fast seek mode */ >>> +#endif >>> + fp->obj.fs = fs; /* Validate the file object */ >>> + fp->obj.id = fs->id; >>> + fp->flag = mode; /* Set file access mode */ >>> + fp->err = 0; /* Clear error flag */ >>> + fp->sect = 0; /* Invalidate current data >>> sector */ >>> + fp->fptr = 0; /* Set file pointer top of the >>> file */ >>> +#if !FF_FS_READONLY >>> +#if !FF_FS_TINY >>> + mem_set(fp->buf, 0, sizeof fp->buf); /* Clear sector >>> buffer */ >>> +#endif >>> + if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* >>> Seek to end of file if FA_OPEN_APPEND is specified */ >>> + fp->fptr = fp->obj.objsize; /* Offset to >>> seek */ >>> + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size >>> in byte */ >>> + clst = fp->obj.sclust; /* Follow the >>> cluster chain */ >>> + for (ofs = fp->obj.objsize; res == FR_OK && ofs > >>> bcs; ofs -= bcs) { >>> + clst = get_fat(&fp->obj, clst); >>> + if (clst <= 1) res = FR_INT_ERR; >>> + if (clst == 0xFFFFFFFF) res = FR_DISK_ERR; >>> + } >>> + fp->clust = clst; >>> + if (res == FR_OK && ofs % SS(fs)) { /* Fill >>> sector buffer if not on the sector boundary */ >>> + sc = clst2sect(fs, clst); >>> + if (sc == 0) { >>> + res = FR_INT_ERR; >>> + } else { >>> + fp->sect = sc + (DWORD)(ofs / SS(fs)); >>> +#if !FF_FS_TINY >>> + if (disk_read(fs->pdrv, fp->buf, fp->sect, >>> 1) != RES_OK) res = FR_DISK_ERR; >>> +#endif >>> + } >>> + } >>> + } >>> +#endif >>> + } >>> + >>> + FREE_NAMBUF(); >>> + } >>> + >>> + if (res != FR_OK) fp->obj.fs = 0; /* Invalidate file object >>> on error */ >>> + >>> + LEAVE_FF(fs, res); >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Read >>> File */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_read ( >>> + FIL* fp, /* Pointer to the file object */ >>> + void* buff, /* Pointer to data buffer */ >>> + UINT btr, /* Number of bytes to read */ >>> + UINT* br /* Pointer to number of bytes read */ >>> +) >>> +{ >>> + FRESULT res; >>> + FATFS *fs; >>> + DWORD clst; >>> + LBA_t sect; >>> + FSIZE_t remain; >>> + UINT rcnt, cc, csect; >>> + BYTE *rbuff = (BYTE*)buff; >>> + >>> + >>> + *br = 0; /* Clear read byte counter */ >>> + res = validate(&fp->obj, &fs); /* Check validity >>> of the file object */ >>> + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) >>> LEAVE_FF(fs, res); /* Check validity */ >>> + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check >>> access mode */ >>> + remain = fp->obj.objsize - fp->fptr; >>> + if (btr > remain) btr = (UINT)remain; /* Truncate btr by >>> remaining bytes */ >>> + >>> + for ( ; btr; /* Repeat until btr >>> bytes read */ >>> + btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) { >>> + if (fp->fptr % SS(fs) == 0) { /* On the sector >>> boundary? */ >>> + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* >>> Sector offset in the cluster */ >>> + if (csect == 0) { /* On the cluster >>> boundary? */ >>> + if (fp->fptr == 0) { /* On the top of the >>> file? */ >>> + clst = fp->obj.sclust; /* Follow cluster >>> chain from the origin */ >>> + } else { /* Middle or end of >>> the file */ >>> +#if FF_USE_FASTSEEK >>> + if (fp->cltbl) { >>> + clst = clmt_clust(fp, fp->fptr); /* Get >>> cluster# from the CLMT */ >>> + } else >>> +#endif >>> + { >>> + clst = get_fat(&fp->obj, fp->clust); /* >>> Follow cluster chain on the FAT */ >>> + } >>> + } >>> + if (clst < 2) ABORT(fs, FR_INT_ERR); >>> + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); >>> + fp->clust = clst; /* Update current >>> cluster */ >>> + } >>> + sect = clst2sect(fs, fp->clust); /* Get current >>> sector */ >>> + if (sect == 0) ABORT(fs, FR_INT_ERR); >>> + sect += csect; >>> + cc = btr / SS(fs); /* When remaining >>> bytes >= sector size, */ >>> + if (cc > 0) { /* Read maximum >>> contiguous sectors directly */ >>> + if (csect + cc > fs->csize) { /* Clip at cluster >>> boundary */ >>> + cc = fs->csize - csect; >>> + } >>> + if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) >>> ABORT(fs, FR_DISK_ERR); >>> +#if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of >>> the read sectors with cached data if it contains a dirty sector */ >>> +#if FF_FS_TINY >>> + if (fs->wflag && fs->winsect - sect < cc) { >>> + mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), >>> fs->win, SS(fs)); >>> + } >>> +#else >>> + if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) { >>> + mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), >>> fp->buf, SS(fs)); >>> + } >>> +#endif >>> +#endif >>> + rcnt = SS(fs) * cc; /* Number of >>> bytes transferred */ >>> + continue; >>> + } >>> +#if !FF_FS_TINY >>> + if (fp->sect != sect) { /* Load data sector >>> if not in cache */ >>> +#if !FF_FS_READONLY >>> + if (fp->flag & FA_DIRTY) { /* Write-back >>> dirty sector cache */ >>> + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) >>> != RES_OK) ABORT(fs, FR_DISK_ERR); >>> + fp->flag &= (BYTE)~FA_DIRTY; >>> + } >>> +#endif >>> + if (disk_read(fs->pdrv, fp->buf, sect, 1) != >>> RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ >>> + } >>> +#endif >>> + fp->sect = sect; >>> + } >>> + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of >>> bytes remains in the sector */ >>> + if (rcnt > btr) rcnt = btr; /* Clip it by >>> btr if needed */ >>> +#if FF_FS_TINY >>> + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, >>> FR_DISK_ERR); /* Move sector window */ >>> + mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* >>> Extract partial sector */ >>> +#else >>> + mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* >>> Extract partial sector */ >>> +#endif >>> + } >>> + >>> + LEAVE_FF(fs, FR_OK); >>> +} >>> + >>> + >>> + >>> + >>> +#if !FF_FS_READONLY >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Write >>> File */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_write ( >>> + FIL* fp, /* Pointer to the file object */ >>> + const void* buff, /* Pointer to the data to be written */ >>> + UINT btw, /* Number of bytes to write */ >>> + UINT* bw /* Pointer to number of bytes written */ >>> +) >>> +{ >>> + FRESULT res; >>> + FATFS *fs; >>> + DWORD clst; >>> + LBA_t sect; >>> + UINT wcnt, cc, csect; >>> + const BYTE *wbuff = (const BYTE*)buff; >>> + >>> + >>> + *bw = 0; /* Clear write byte counter */ >>> + res = validate(&fp->obj, &fs); /* Check validity of >>> the file object */ >>> + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) >>> LEAVE_FF(fs, res); /* Check validity */ >>> + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check >>> access mode */ >>> + >>> + /* Check fptr wrap-around (file size cannot reach 4 GiB at FAT >>> volume) */ >>> + if ((!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) && >>> (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) { >>> + btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr); >>> + } >>> + >>> + for ( ; btw; /* Repeat until all >>> data written */ >>> + btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, >>> fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : >>> fp->obj.objsize) { >>> + if (fp->fptr % SS(fs) == 0) { /* On the sector >>> boundary? */ >>> + csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* >>> Sector offset in the cluster */ >>> + if (csect == 0) { /* On the cluster >>> boundary? */ >>> + if (fp->fptr == 0) { /* On the top of the >>> file? */ >>> + clst = fp->obj.sclust; /* Follow from the >>> origin */ >>> + if (clst == 0) { /* If no cluster is >>> allocated, */ >>> + clst = create_chain(&fp->obj, 0); /* >>> create a new cluster chain */ >>> + } >>> + } else { /* On the middle or end >>> of the file */ >>> +#if FF_USE_FASTSEEK >>> + if (fp->cltbl) { >>> + clst = clmt_clust(fp, fp->fptr); /* Get >>> cluster# from the CLMT */ >>> + } else >>> +#endif >>> + { >>> + clst = create_chain(&fp->obj, fp->clust); /* >>> Follow or stretch cluster chain on the FAT */ >>> + } >>> + } >>> + if (clst == 0) break; /* Could not allocate a >>> new cluster (disk full) */ >>> + if (clst == 1) ABORT(fs, FR_INT_ERR); >>> + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); >>> + fp->clust = clst; /* Update current >>> cluster */ >>> + if (fp->obj.sclust == 0) fp->obj.sclust = clst; >>> /* Set start cluster if the first write */ >>> + } >>> +#if FF_FS_TINY >>> + if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) >>> ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */ >>> +#else >>> + if (fp->flag & FA_DIRTY) { /* Write-back sector >>> cache */ >>> + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != >>> RES_OK) ABORT(fs, FR_DISK_ERR); >>> + fp->flag &= (BYTE)~FA_DIRTY; >>> + } >>> +#endif >>> + sect = clst2sect(fs, fp->clust); /* Get current >>> sector */ >>> + if (sect == 0) ABORT(fs, FR_INT_ERR); >>> + sect += csect; >>> + cc = btw / SS(fs); /* When remaining >>> bytes >= sector size, */ >>> + if (cc > 0) { /* Write maximum >>> contiguous sectors directly */ >>> + if (csect + cc > fs->csize) { /* Clip at cluster >>> boundary */ >>> + cc = fs->csize - csect; >>> + } >>> + if (disk_write(fs->pdrv, wbuff, sect, cc) != RES_OK) >>> ABORT(fs, FR_DISK_ERR); >>> +#if FF_FS_MINIMIZE <= 2 >>> +#if FF_FS_TINY >>> + if (fs->winsect - sect < cc) { /* Refill sector >>> cache if it gets invalidated by the direct write */ >>> + mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * >>> SS(fs)), SS(fs)); >>> + fs->wflag = 0; >>> + } >>> +#else >>> + if (fp->sect - sect < cc) { /* Refill sector cache >>> if it gets invalidated by the direct write */ >>> + mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * >>> SS(fs)), SS(fs)); >>> + fp->flag &= (BYTE)~FA_DIRTY; >>> + } >>> +#endif >>> +#endif >>> + wcnt = SS(fs) * cc; /* Number of bytes >>> transferred */ >>> + continue; >>> + } >>> +#if FF_FS_TINY >>> + if (fp->fptr >= fp->obj.objsize) { /* Avoid silly >>> cache filling on the growing edge */ >>> + if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); >>> + fs->winsect = sect; >>> + } >>> +#else >>> + if (fp->sect != sect && /* Fill sector cache >>> with file data */ >>> + fp->fptr < fp->obj.objsize && >>> + disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) { >>> + ABORT(fs, FR_DISK_ERR); >>> + } >>> +#endif >>> + fp->sect = sect; >>> + } >>> + wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of >>> bytes remains in the sector */ >>> + if (wcnt > btw) wcnt = btw; /* Clip it by >>> btw if needed */ >>> +#if FF_FS_TINY >>> + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, >>> FR_DISK_ERR); /* Move sector window */ >>> + mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit >>> data to the sector */ >>> + fs->wflag = 1; >>> +#else >>> + mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit >>> data to the sector */ >>> + fp->flag |= FA_DIRTY; >>> +#endif >>> + } >>> + >>> + fp->flag |= FA_MODIFIED; /* Set file change flag */ >>> + >>> + LEAVE_FF(fs, FR_OK); >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Synchronize the >>> File */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_sync ( >>> + FIL* fp /* Pointer to the file object */ >>> +) >>> +{ >>> + FRESULT res; >>> + FATFS *fs; >>> + DWORD tm; >>> + BYTE *dir; >>> + >>> + >>> + res = validate(&fp->obj, &fs); /* Check validity of the file >>> object */ >>> + if (res == FR_OK) { >>> + if (fp->flag & FA_MODIFIED) { /* Is there any change to >>> the file? */ >>> +#if !FF_FS_TINY >>> + if (fp->flag & FA_DIRTY) { /* Write-back cached data >>> if needed */ >>> + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != >>> RES_OK) LEAVE_FF(fs, FR_DISK_ERR); >>> + fp->flag &= (BYTE)~FA_DIRTY; >>> + } >>> +#endif >>> + /* Update the directory entry */ >>> + tm = GET_FATTIME(); /* Modified time */ >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { >>> + res = fill_first_frag(&fp->obj); /* Fill first >>> fragment on the FAT if needed */ >>> + if (res == FR_OK) { >>> + res = fill_last_frag(&fp->obj, fp->clust, >>> 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */ >>> + } >>> + if (res == FR_OK) { >>> + DIR dj; >>> + DEF_NAMBUF >>> + >>> + INIT_NAMBUF(fs); >>> + res = load_obj_xdir(&dj, &fp->obj); /* Load >>> directory entry block */ >>> + if (res == FR_OK) { >>> + fs->dirbuf[XDIR_Attr] |= >>> AM_ARC; /* Set archive attribute to indicate that the >>> file has been changed */ >>> + fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | >>> 1; /* Update file allocation information */ >>> + st_dword(fs->dirbuf + XDIR_FstClus, >>> fp->obj.sclust); /* Update start cluster */ >>> + st_qword(fs->dirbuf + XDIR_FileSize, >>> fp->obj.objsize); /* Update file size */ >>> + st_qword(fs->dirbuf + XDIR_ValidFileSize, >>> fp->obj.objsize); /* (FatFs does not support Valid File Size >>> feature) */ >>> + st_dword(fs->dirbuf + XDIR_ModTime, >>> tm); /* Update modified time */ >>> + fs->dirbuf[XDIR_ModTime10] = 0; >>> + st_dword(fs->dirbuf + XDIR_AccTime, 0); >>> + res = store_xdir(&dj); /* Restore it to >>> the directory */ >>> + if (res == FR_OK) { >>> + res = sync_fs(fs); >>> + fp->flag &= (BYTE)~FA_MODIFIED; >>> + } >>> + } >>> + FREE_NAMBUF(); >>> + } >>> + } else >>> +#endif >>> + { >>> + res = move_window(fs, fp->dir_sect); >>> + if (res == FR_OK) { >>> + dir = fp->dir_ptr; >>> + dir[DIR_Attr] |= AM_ARC; /* Set archive >>> attribute to indicate that the file has been changed */ >>> + st_clust(fp->obj.fs, dir, fp->obj.sclust); /* >>> Update file allocation information */ >>> + st_dword(dir + DIR_FileSize, >>> (DWORD)fp->obj.objsize); /* Update file size */ >>> + st_dword(dir + DIR_ModTime, tm); /* Update >>> modified time */ >>> + st_word(dir + DIR_LstAccDate, 0); >>> + fs->wflag = 1; >>> + res = sync_fs(fs); /* Restore >>> it to the directory */ >>> + fp->flag &= (BYTE)~FA_MODIFIED; >>> + } >>> + } >>> + } >>> + } >>> + >>> + LEAVE_FF(fs, res); >>> +} >>> + >>> +#endif /* !FF_FS_READONLY */ >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Close >>> File */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_close ( >>> + FIL* fp /* Pointer to the file object to be closed */ >>> +) >>> +{ >>> + FRESULT res; >>> + FATFS *fs; >>> + >>> +#if !FF_FS_READONLY >>> + res = f_sync(fp); /* Flush cached data */ >>> + if (res == FR_OK) >>> +#endif >>> + { >>> + res = validate(&fp->obj, &fs); /* Lock volume */ >>> + if (res == FR_OK) { >>> +#if FF_FS_LOCK != 0 >>> + res = dec_lock(fp->obj.lockid); /* Decrement file >>> open counter */ >>> + if (res == FR_OK) fp->obj.fs = 0; /* Invalidate file >>> object */ >>> +#else >>> + fp->obj.fs = 0; /* Invalidate file object */ >>> +#endif >>> +#if FF_FS_REENTRANT >>> + unlock_fs(fs, FR_OK); /* Unlock volume */ >>> +#endif >>> + } >>> + } >>> + return res; >>> +} >>> + >>> + >>> + >>> + >>> +#if FF_FS_RPATH >= 1 >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Change Current Directory or Current Drive, Get Current >>> Directory */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_chdrive ( >>> + const TCHAR* path /* Drive number to set */ >>> +) >>> +{ >>> + int vol; >>> + >>> + >>> + /* Get logical drive number */ >>> + vol = get_ldnumber(&path); >>> + if (vol < 0) return FR_INVALID_DRIVE; >>> + CurrVol = (BYTE)vol; /* Set it as current volume */ >>> + >>> + return FR_OK; >>> +} >>> + >>> + >>> + >>> +FRESULT f_chdir ( >>> + const TCHAR* path /* Pointer to the directory path */ >>> +) >>> +{ >>> +#if FF_STR_VOLUME_ID == 2 >>> + UINT i; >>> +#endif >>> + FRESULT res; >>> + DIR dj; >>> + FATFS *fs; >>> + DEF_NAMBUF >>> + >>> + >>> + /* Get logical drive */ >>> + res = mount_volume(&path, &fs, 0); >>> + if (res == FR_OK) { >>> + dj.obj.fs = fs; >>> + INIT_NAMBUF(fs); >>> + res = follow_path(&dj, path); /* Follow the path */ >>> + if (res == FR_OK) { /* Follow completed */ >>> + if (dj.fn[NSFLAG] & NS_NONAME) { /* Is it the start >>> directory itself? */ >>> + fs->cdir = dj.obj.sclust; >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { >>> + fs->cdc_scl = dj.obj.c_scl; >>> + fs->cdc_size = dj.obj.c_size; >>> + fs->cdc_ofs = dj.obj.c_ofs; >>> + } >>> +#endif >>> + } else { >>> + if (dj.obj.attr & AM_DIR) { /* It is a >>> sub-directory */ >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { >>> + fs->cdir = ld_dword(fs->dirbuf + >>> XDIR_FstClus); /* Sub-directory cluster */ >>> + fs->cdc_scl = >>> dj.obj.sclust; /* Save containing directory >>> information */ >>> + fs->cdc_size = ((DWORD)dj.obj.objsize & >>> 0xFFFFFF00) | dj.obj.stat; >>> + fs->cdc_ofs = dj.blk_ofs; >>> + } else >>> +#endif >>> + { >>> + fs->cdir = ld_clust(fs, >>> dj.dir); /* Sub-directory cluster */ >>> + } >>> + } else { >>> + res = FR_NO_PATH; /* Reached but a file */ >>> + } >>> + } >>> + } >>> + FREE_NAMBUF(); >>> + if (res == FR_NO_FILE) res = FR_NO_PATH; >>> +#if FF_STR_VOLUME_ID == 2 /* Also current drive is changed at >>> Unix style volume ID */ >>> + if (res == FR_OK) { >>> + for (i = FF_VOLUMES - 1; i && fs != FatFs[i]; i--) ; /* >>> Set current drive */ >>> + CurrVol = (BYTE)i; >>> + } >>> +#endif >>> + } >>> + >>> + LEAVE_FF(fs, res); >>> +} >>> + >>> + >>> +#if FF_FS_RPATH >= 2 >>> +FRESULT f_getcwd ( >>> + TCHAR* buff, /* Pointer to the directory path */ >>> + UINT len /* Size of buff in unit of TCHAR */ >>> +) >>> +{ >>> + FRESULT res; >>> + DIR dj; >>> + FATFS *fs; >>> + UINT i, n; >>> + DWORD ccl; >>> + TCHAR *tp = buff; >>> +#if FF_VOLUMES >= 2 >>> + UINT vl; >>> +#if FF_STR_VOLUME_ID >>> + const char *vp; >>> +#endif >>> +#endif >>> + FILINFO fno; >>> + DEF_NAMBUF >>> + >>> + >>> + /* Get logical drive */ >>> + buff[0] = 0; /* Set null string to get current volume */ >>> + res = mount_volume((const TCHAR**)&buff, &fs, 0); /* Get >>> current volume */ >>> + if (res == FR_OK) { >>> + dj.obj.fs = fs; >>> + INIT_NAMBUF(fs); >>> + >>> + /* Follow parent directories and create the path */ >>> + i = len; /* Bottom of buffer (directory stack >>> base) */ >>> + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* (Cannot >>> do getcwd on exFAT and returns root path) */ >>> + dj.obj.sclust = fs->cdir; /* Start to >>> follow upper directory from current directory */ >>> + while ((ccl = dj.obj.sclust) != 0) { /* Repeat while >>> current directory is a sub-directory */ >>> + res = dir_sdi(&dj, 1 * SZDIRE); /* Get parent >>> directory */ >>> + if (res != FR_OK) break; >>> + res = move_window(fs, dj.sect); >>> + if (res != FR_OK) break; >>> + dj.obj.sclust = ld_clust(fs, dj.dir); /* Goto >>> parent directory */ >>> + res = dir_sdi(&dj, 0); >>> + if (res != FR_OK) break; >>> + do { /* Find the entry >>> links to the child directory */ >>> + res = DIR_READ_FILE(&dj); >>> + if (res != FR_OK) break; >>> + if (ccl == ld_clust(fs, dj.dir)) break; /* >>> Found the entry */ >>> + res = dir_next(&dj, 0); >>> + } while (res == FR_OK); >>> + if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot >>> be 'not found'. */ >>> + if (res != FR_OK) break; >>> + get_fileinfo(&dj, &fno); /* Get the directory >>> name and push it to the buffer */ >>> + for (n = 0; fno.fname[n]; n++) ; /* Name length */ >>> + if (i < n + 1) { /* Insufficient space to store >>> the path name? */ >>> + res = FR_NOT_ENOUGH_CORE; break; >>> + } >>> + while (n) buff[--i] = fno.fname[--n]; /* Stack >>> the name */ >>> + buff[--i] = '/'; >>> + } >>> + } >>> + if (res == FR_OK) { >>> + if (i == len) buff[--i] = '/'; /* Is it the >>> root-directory? */ >>> +#if FF_VOLUMES >= 2 /* Put drive prefix */ >>> + vl = 0; >>> +#if FF_STR_VOLUME_ID >= 1 /* String volume ID */ >>> + for (n = 0, vp = (const char*)VolumeStr[CurrVol]; vp[n]; >>> n++) ; >>> + if (i >= n + 2) { >>> + if (FF_STR_VOLUME_ID == 2) *tp++ = (TCHAR)'/'; >>> + for (vl = 0; vl < n; *tp++ = (TCHAR)vp[vl], vl++) ; >>> + if (FF_STR_VOLUME_ID == 1) *tp++ = (TCHAR)':'; >>> + vl++; >>> + } >>> +#else /* Numeric volume ID */ >>> + if (i >= 3) { >>> + *tp++ = (TCHAR)'0' + CurrVol; >>> + *tp++ = (TCHAR)':'; >>> + vl = 2; >>> + } >>> +#endif >>> + if (vl == 0) res = FR_NOT_ENOUGH_CORE; >>> +#endif >>> + /* Add current directory path */ >>> + if (res == FR_OK) { >>> + do *tp++ = buff[i++]; while (i < len); /* Copy >>> stacked path string */ >>> + } >>> + } >>> + FREE_NAMBUF(); >>> + } >>> + >>> + *tp = 0; >>> + LEAVE_FF(fs, res); >>> +} >>> + >>> +#endif /* FF_FS_RPATH >= 2 */ >>> +#endif /* FF_FS_RPATH >= 1 */ >>> + >>> + >>> + >>> +#if FF_FS_MINIMIZE <= 2 >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Seek File Read/Write >>> Pointer */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_lseek ( >>> + FIL* fp, /* Pointer to the file object */ >>> + FSIZE_t ofs /* File pointer from top of file */ >>> +) >>> +{ >>> + FRESULT res; >>> + FATFS *fs; >>> + DWORD clst, bcs; >>> + LBA_t nsect; >>> + FSIZE_t ifptr; >>> +#if FF_USE_FASTSEEK >>> + DWORD cl, pcl, ncl, tcl, tlen, ulen, *tbl; >>> + LBA_t dsc; >>> +#endif >>> + >>> + res = validate(&fp->obj, &fs); /* Check validity of the >>> file object */ >>> + if (res == FR_OK) res = (FRESULT)fp->err; >>> +#if FF_FS_EXFAT && !FF_FS_READONLY >>> + if (res == FR_OK && fs->fs_type == FS_EXFAT) { >>> + res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* >>> Fill last fragment on the FAT if needed */ >>> + } >>> +#endif >>> + if (res != FR_OK) LEAVE_FF(fs, res); >>> + >>> +#if FF_USE_FASTSEEK >>> + if (fp->cltbl) { /* Fast seek */ >>> + if (ofs == CREATE_LINKMAP) { /* Create CLMT */ >>> + tbl = fp->cltbl; >>> + tlen = *tbl++; ulen = 2; /* Given table size and >>> required table size */ >>> + cl = fp->obj.sclust; /* Origin of the chain */ >>> + if (cl != 0) { >>> + do { >>> + /* Get a fragment */ >>> + tcl = cl; ncl = 0; ulen += 2; /* Top, length >>> and used items */ >>> + do { >>> + pcl = cl; ncl++; >>> + cl = get_fat(&fp->obj, cl); >>> + if (cl <= 1) ABORT(fs, FR_INT_ERR); >>> + if (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); >>> + } while (cl == pcl + 1); >>> + if (ulen <= tlen) { /* Store the length >>> and top of the fragment */ >>> + *tbl++ = ncl; *tbl++ = tcl; >>> + } >>> + } while (cl < fs->n_fatent); /* Repeat until end >>> of chain */ >>> + } >>> + *fp->cltbl = ulen; /* Number of items used */ >>> + if (ulen <= tlen) { >>> + *tbl = 0; /* Terminate table */ >>> + } else { >>> + res = FR_NOT_ENOUGH_CORE; /* Given table size is >>> smaller than required */ >>> + } >>> + } else { /* Fast seek */ >>> + if (ofs > fp->obj.objsize) ofs = fp->obj.objsize; /* >>> Clip offset at the file size */ >>> + fp->fptr = ofs; /* Set file pointer */ >>> + if (ofs > 0) { >>> + fp->clust = clmt_clust(fp, ofs - 1); >>> + dsc = clst2sect(fs, fp->clust); >>> + if (dsc == 0) ABORT(fs, FR_INT_ERR); >>> + dsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1); >>> + if (fp->fptr % SS(fs) && dsc != fp->sect) { /* >>> Refill sector cache if needed */ >>> +#if !FF_FS_TINY >>> +#if !FF_FS_READONLY >>> + if (fp->flag & FA_DIRTY) { /* Write-back >>> dirty sector cache */ >>> + if (disk_write(fs->pdrv, fp->buf, fp->sect, >>> 1) != RES_OK) ABORT(fs, FR_DISK_ERR); >>> + fp->flag &= (BYTE)~FA_DIRTY; >>> + } >>> +#endif >>> + if (disk_read(fs->pdrv, fp->buf, dsc, 1) != >>> RES_OK) ABORT(fs, FR_DISK_ERR); /* Load current sector */ >>> +#endif >>> + fp->sect = dsc; >>> + } >>> + } >>> + } >>> + } else >>> +#endif >>> + >>> + /* Normal Seek */ >>> + { >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = >>> 0xFFFFFFFF; /* Clip at 4 GiB - 1 if at FATxx */ >>> +#endif >>> + if (ofs > fp->obj.objsize && (FF_FS_READONLY || !(fp->flag & >>> FA_WRITE))) { /* In read-only mode, clip offset with the file size */ >>> + ofs = fp->obj.objsize; >>> + } >>> + ifptr = fp->fptr; >>> + fp->fptr = nsect = 0; >>> + if (ofs > 0) { >>> + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size >>> (byte) */ >>> + if (ifptr > 0 && >>> + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When >>> seek to same or following cluster, */ >>> + fp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1); /* >>> start from the current cluster */ >>> + ofs -= fp->fptr; >>> + clst = fp->clust; >>> + } else { /* When seek >>> to back cluster, */ >>> + clst = fp->obj.sclust; /* start >>> from the first cluster */ >>> +#if !FF_FS_READONLY >>> + if (clst == 0) { /* If no >>> cluster chain, create a new chain */ >>> + clst = create_chain(&fp->obj, 0); >>> + if (clst == 1) ABORT(fs, FR_INT_ERR); >>> + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); >>> + fp->obj.sclust = clst; >>> + } >>> +#endif >>> + fp->clust = clst; >>> + } >>> + if (clst != 0) { >>> + while (ofs > bcs) { /* >>> Cluster following loop */ >>> + ofs -= bcs; fp->fptr += bcs; >>> +#if !FF_FS_READONLY >>> + if (fp->flag & FA_WRITE) { /* Check >>> if in write mode or not */ >>> + if (FF_FS_EXFAT && fp->fptr > >>> fp->obj.objsize) { /* No FAT chain object needs correct objsize to >>> generate FAT value */ >>> + fp->obj.objsize = fp->fptr; >>> + fp->flag |= FA_MODIFIED; >>> + } >>> + clst = create_chain(&fp->obj, clst); /* >>> Follow chain with forceed stretch */ >>> + if (clst == 0) { /* Clip file >>> size in case of disk full */ >>> + ofs = 0; break; >>> + } >>> + } else >>> +#endif >>> + { >>> + clst = get_fat(&fp->obj, clst); /* Follow >>> cluster chain if not in write mode */ >>> + } >>> + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); >>> + if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, >>> FR_INT_ERR); >>> + fp->clust = clst; >>> + } >>> + fp->fptr += ofs; >>> + if (ofs % SS(fs)) { >>> + nsect = clst2sect(fs, clst); /* Current >>> sector */ >>> + if (nsect == 0) ABORT(fs, FR_INT_ERR); >>> + nsect += (DWORD)(ofs / SS(fs)); >>> + } >>> + } >>> + } >>> + if (!FF_FS_READONLY && fp->fptr > fp->obj.objsize) { /* >>> Set file change flag if the file size is extended */ >>> + fp->obj.objsize = fp->fptr; >>> + fp->flag |= FA_MODIFIED; >>> + } >>> + if (fp->fptr % SS(fs) && nsect != fp->sect) { /* Fill >>> sector cache if needed */ >>> +#if !FF_FS_TINY >>> +#if !FF_FS_READONLY >>> + if (fp->flag & FA_DIRTY) { /* Write-back >>> dirty sector cache */ >>> + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != >>> RES_OK) ABORT(fs, FR_DISK_ERR); >>> + fp->flag &= (BYTE)~FA_DIRTY; >>> + } >>> +#endif >>> + if (disk_read(fs->pdrv, fp->buf, nsect, 1) != RES_OK) >>> ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ >>> +#endif >>> + fp->sect = nsect; >>> + } >>> + } >>> + >>> + LEAVE_FF(fs, res); >>> +} >>> + >>> + >>> + >>> +#if FF_FS_MINIMIZE <= 1 >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Create a Directory >>> Object */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_opendir ( >>> + DIR* dp, /* Pointer to directory object to create */ >>> + const TCHAR* path /* Pointer to the directory path */ >>> +) >>> +{ >>> + FRESULT res; >>> + FATFS *fs; >>> + DEF_NAMBUF >>> + >>> + >>> + if (!dp) return FR_INVALID_OBJECT; >>> + >>> + /* Get logical drive */ >>> + res = mount_volume(&path, &fs, 0); >>> + if (res == FR_OK) { >>> + dp->obj.fs = fs; >>> + INIT_NAMBUF(fs); >>> + res = follow_path(dp, path); /* Follow the path >>> to the directory */ >>> + if (res == FR_OK) { /* Follow >>> completed */ >>> + if (!(dp->fn[NSFLAG] & NS_NONAME)) { /* It is not the >>> origin directory itself */ >>> + if (dp->obj.attr & AM_DIR) { /* This object >>> is a sub-directory */ >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { >>> + dp->obj.c_scl = >>> dp->obj.sclust; /* Get containing >>> directory inforamation */ >>> + dp->obj.c_size = ((DWORD)dp->obj.objsize & >>> 0xFFFFFF00) | dp->obj.stat; >>> + dp->obj.c_ofs = dp->blk_ofs; >>> + init_alloc_info(fs, &dp->obj); /* Get >>> object allocation info */ >>> + } else >>> +#endif >>> + { >>> + dp->obj.sclust = ld_clust(fs, dp->dir); >>> /* Get object allocation info */ >>> + } >>> + } else { /* This object is a >>> file */ >>> + res = FR_NO_PATH; >>> + } >>> + } >>> + if (res == FR_OK) { >>> + dp->obj.id = fs->id; >>> + res = dir_sdi(dp, 0); /* Rewind directory */ >>> +#if FF_FS_LOCK != 0 >>> + if (res == FR_OK) { >>> + if (dp->obj.sclust != 0) { >>> + dp->obj.lockid = inc_lock(dp, 0); /* Lock >>> the sub directory */ >>> + if (!dp->obj.lockid) res = >>> FR_TOO_MANY_OPEN_FILES; >>> + } else { >>> + dp->obj.lockid = 0; /* Root directory >>> need not to be locked */ >>> + } >>> + } >>> +#endif >>> + } >>> + } >>> + FREE_NAMBUF(); >>> + if (res == FR_NO_FILE) res = FR_NO_PATH; >>> + } >>> + if (res != FR_OK) dp->obj.fs = 0; /* Invalidate the >>> directory object if function faild */ >>> + >>> + LEAVE_FF(fs, res); >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Close >>> Directory */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_closedir ( >>> + DIR *dp /* Pointer to the directory object to be closed */ >>> +) >>> +{ >>> + FRESULT res; >>> + FATFS *fs; >>> + >>> + >>> + res = validate(&dp->obj, &fs); /* Check validity of the file >>> object */ >>> + if (res == FR_OK) { >>> +#if FF_FS_LOCK != 0 >>> + if (dp->obj.lockid) res = dec_lock(dp->obj.lockid); /* >>> Decrement sub-directory open counter */ >>> + if (res == FR_OK) dp->obj.fs = 0; /* Invalidate directory >>> object */ >>> +#else >>> + dp->obj.fs = 0; /* Invalidate directory object */ >>> +#endif >>> +#if FF_FS_REENTRANT >>> + unlock_fs(fs, FR_OK); /* Unlock volume */ >>> +#endif >>> + } >>> + return res; >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Read Directory Entries in >>> Sequence */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_readdir ( >>> + DIR* dp, /* Pointer to the open directory object */ >>> + FILINFO* fno /* Pointer to file information to return */ >>> +) >>> +{ >>> + FRESULT res; >>> + FATFS *fs; >>> + DEF_NAMBUF >>> + >>> + >>> + res = validate(&dp->obj, &fs); /* Check validity of the >>> directory object */ >>> + if (res == FR_OK) { >>> + if (!fno) { >>> + res = dir_sdi(dp, 0); /* Rewind the directory >>> object */ >>> + } else { >>> + INIT_NAMBUF(fs); >>> + res = DIR_READ_FILE(dp); /* Read an item */ >>> + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of >>> directory */ >>> + if (res == FR_OK) { /* A valid entry is >>> found */ >>> + get_fileinfo(dp, fno); /* Get the object >>> information */ >>> + res = dir_next(dp, 0); /* Increment index for >>> next */ >>> + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end >>> of directory now */ >>> + } >>> + FREE_NAMBUF(); >>> + } >>> + } >>> + LEAVE_FF(fs, res); >>> +} >>> + >>> + >>> + >>> +#if FF_USE_FIND >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Find Next >>> File */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_findnext ( >>> + DIR* dp, /* Pointer to the open directory object */ >>> + FILINFO* fno /* Pointer to the file information structure */ >>> +) >>> +{ >>> + FRESULT res; >>> + >>> + >>> + for (;;) { >>> + res = f_readdir(dp, fno); /* Get a directory item */ >>> + if (res != FR_OK || !fno || !fno->fname[0]) break; /* >>> Terminate if any error or end of directory */ >>> + if (pattern_match(dp->pat, fno->fname, 0, FIND_RECURS)) >>> break; /* Test for the file name */ >>> +#if FF_USE_LFN && FF_USE_FIND == 2 >>> + if (pattern_match(dp->pat, fno->altname, 0, FIND_RECURS)) >>> break; /* Test for alternative name if exist */ >>> +#endif >>> + } >>> + return res; >>> +} >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Find First >>> File */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_findfirst ( >>> + DIR* dp, /* Pointer to the blank directory object */ >>> + FILINFO* fno, /* Pointer to the file information >>> structure */ >>> + const TCHAR* path, /* Pointer to the directory to open */ >>> + const TCHAR* pattern /* Pointer to the matching pattern */ >>> +) >>> +{ >>> + FRESULT res; >>> + >>> + >>> + dp->pat = pattern; /* Save pointer to pattern string */ >>> + res = f_opendir(dp, path); /* Open the target directory */ >>> + if (res == FR_OK) { >>> + res = f_findnext(dp, fno); /* Find the first item */ >>> + } >>> + return res; >>> +} >>> + >>> +#endif /* FF_USE_FIND */ >>> + >>> + >>> + >>> +#if FF_FS_MINIMIZE == 0 >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Get File >>> Status */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_stat ( >>> + const TCHAR* path, /* Pointer to the file path */ >>> + FILINFO* fno /* Pointer to file information to return */ >>> +) >>> +{ >>> + FRESULT res; >>> + DIR dj; >>> + DEF_NAMBUF >>> + >>> + >>> + /* Get logical drive */ >>> + res = mount_volume(&path, &dj.obj.fs, 0); >>> + if (res == FR_OK) { >>> + INIT_NAMBUF(dj.obj.fs); >>> + res = follow_path(&dj, path); /* Follow the file path */ >>> + if (res == FR_OK) { /* Follow completed */ >>> + if (dj.fn[NSFLAG] & NS_NONAME) { /* It is origin >>> directory */ >>> + res = FR_INVALID_NAME; >>> + } else { /* Found an object */ >>> + if (fno) get_fileinfo(&dj, fno); >>> + } >>> + } >>> + FREE_NAMBUF(); >>> + } >>> + >>> + LEAVE_FF(dj.obj.fs, res); >>> +} >>> + >>> + >>> + >>> +#if !FF_FS_READONLY >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Get Number of Free >>> Clusters */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_getfree ( >>> + const TCHAR* path, /* Logical drive number */ >>> + DWORD* nclst, /* Pointer to a variable to return number >>> of free clusters */ >>> + FATFS** fatfs /* Pointer to return pointer to >>> corresponding filesystem object */ >>> +) >>> +{ >>> + FRESULT res; >>> + FATFS *fs; >>> + DWORD nfree, clst, stat; >>> + LBA_t sect; >>> + UINT i; >>> + FFOBJID obj; >>> + >>> + >>> + /* Get logical drive */ >>> + res = mount_volume(&path, &fs, 0); >>> + if (res == FR_OK) { >>> + *fatfs = fs; /* Return ptr to the fs object */ >>> + /* If free_clst is valid, return it without full FAT scan */ >>> + if (fs->free_clst <= fs->n_fatent - 2) { >>> + *nclst = fs->free_clst; >>> + } else { >>> + /* Scan FAT to obtain number of free clusters */ >>> + nfree = 0; >>> + if (fs->fs_type == FS_FAT12) { /* FAT12: Scan bit >>> field FAT entries */ >>> + clst = 2; obj.fs = fs; >>> + do { >>> + stat = get_fat(&obj, clst); >>> + if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; >>> break; } >>> + if (stat == 1) { res = FR_INT_ERR; break; } >>> + if (stat == 0) nfree++; >>> + } while (++clst < fs->n_fatent); >>> + } else { >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { /* exFAT: Scan >>> allocation bitmap */ >>> + BYTE bm; >>> + UINT b; >>> + >>> + clst = fs->n_fatent - 2; /* Number of >>> clusters */ >>> + sect = fs->bitbase; /* Bitmap sector */ >>> + i = 0; /* Offset in the >>> sector */ >>> + do { /* Counts numbuer of bits with zero in >>> the bitmap */ >>> + if (i == 0) { >>> + res = move_window(fs, sect++); >>> + if (res != FR_OK) break; >>> + } >>> + for (b = 8, bm = fs->win[i]; b && clst; b--, >>> clst--) { >>> + if (!(bm & 1)) nfree++; >>> + bm >>= 1; >>> + } >>> + i = (i + 1) % SS(fs); >>> + } while (clst); >>> + } else >>> +#endif >>> + { /* FAT16/32: Scan WORD/DWORD FAT entries */ >>> + clst = fs->n_fatent; /* Number of entries */ >>> + sect = fs->fatbase; /* Top of the FAT */ >>> + i = 0; /* Offset in the >>> sector */ >>> + do { /* Counts numbuer of entries with zero >>> in the FAT */ >>> + if (i == 0) { >>> + res = move_window(fs, sect++); >>> + if (res != FR_OK) break; >>> + } >>> + if (fs->fs_type == FS_FAT16) { >>> + if (ld_word(fs->win + i) == 0) nfree++; >>> + i += 2; >>> + } else { >>> + if ((ld_dword(fs->win + i) & 0x0FFFFFFF) >>> == 0) nfree++; >>> + i += 4; >>> + } >>> + i %= SS(fs); >>> + } while (--clst); >>> + } >>> + } >>> + *nclst = nfree; /* Return the free clusters */ >>> + fs->free_clst = nfree; /* Now free_clst is valid */ >>> + fs->fsi_flag |= 1; /* FAT32: FSInfo is to be >>> updated */ >>> + } >>> + } >>> + >>> + LEAVE_FF(fs, res); >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Truncate >>> File */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_truncate ( >>> + FIL* fp /* Pointer to the file object */ >>> +) >>> +{ >>> + FRESULT res; >>> + FATFS *fs; >>> + DWORD ncl; >>> + >>> + >>> + res = validate(&fp->obj, &fs); /* Check validity of the file >>> object */ >>> + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) >>> LEAVE_FF(fs, res); >>> + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check >>> access mode */ >>> + >>> + if (fp->fptr < fp->obj.objsize) { /* Process when fptr is not >>> on the eof */ >>> + if (fp->fptr == 0) { /* When set file size to zero, >>> remove entire cluster chain */ >>> + res = remove_chain(&fp->obj, fp->obj.sclust, 0); >>> + fp->obj.sclust = 0; >>> + } else { /* When truncate a part of the file, >>> remove remaining clusters */ >>> + ncl = get_fat(&fp->obj, fp->clust); >>> + res = FR_OK; >>> + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; >>> + if (ncl == 1) res = FR_INT_ERR; >>> + if (res == FR_OK && ncl < fs->n_fatent) { >>> + res = remove_chain(&fp->obj, ncl, fp->clust); >>> + } >>> + } >>> + fp->obj.objsize = fp->fptr; /* Set file size to current >>> read/write point */ >>> + fp->flag |= FA_MODIFIED; >>> +#if !FF_FS_TINY >>> + if (res == FR_OK && (fp->flag & FA_DIRTY)) { >>> + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) { >>> + res = FR_DISK_ERR; >>> + } else { >>> + fp->flag &= (BYTE)~FA_DIRTY; >>> + } >>> + } >>> +#endif >>> + if (res != FR_OK) ABORT(fs, res); >>> + } >>> + >>> + LEAVE_FF(fs, res); >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Delete a >>> File/Directory */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_unlink ( >>> + const TCHAR* path /* Pointer to the file or directory >>> path */ >>> +) >>> +{ >>> + FRESULT res; >>> + DIR dj, sdj; >>> + DWORD dclst = 0; >>> + FATFS *fs; >>> +#if FF_FS_EXFAT >>> + FFOBJID obj; >>> +#endif >>> + DEF_NAMBUF >>> + >>> + >>> + /* Get logical drive */ >>> + res = mount_volume(&path, &fs, FA_WRITE); >>> + if (res == FR_OK) { >>> + dj.obj.fs = fs; >>> + INIT_NAMBUF(fs); >>> + res = follow_path(&dj, path); /* Follow the file path */ >>> + if (FF_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) { >>> + res = FR_INVALID_NAME; /* Cannot remove dot >>> entry */ >>> + } >>> +#if FF_FS_LOCK != 0 >>> + if (res == FR_OK) res = chk_lock(&dj, 2); /* Check if it >>> is an open object */ >>> +#endif >>> + if (res == FR_OK) { /* The object is >>> accessible */ >>> + if (dj.fn[NSFLAG] & NS_NONAME) { >>> + res = FR_INVALID_NAME; /* Cannot remove the >>> origin directory */ >>> + } else { >>> + if (dj.obj.attr & AM_RDO) { >>> + res = FR_DENIED; /* Cannot remove R/O >>> object */ >>> + } >>> + } >>> + if (res == FR_OK) { >>> +#if FF_FS_EXFAT >>> + obj.fs = fs; >>> + if (fs->fs_type == FS_EXFAT) { >>> + init_alloc_info(fs, &obj); >>> + dclst = obj.sclust; >>> + } else >>> +#endif >>> + { >>> + dclst = ld_clust(fs, dj.dir); >>> + } >>> + if (dj.obj.attr & AM_DIR) { /* Is it a >>> sub-directory? */ >>> +#if FF_FS_RPATH != 0 >>> + if (dclst == fs->cdir) { /* Is it >>> the current directory? */ >>> + res = FR_DENIED; >>> + } else >>> +#endif >>> + { >>> + sdj.obj.fs = fs; /* Open the >>> sub-directory */ >>> + sdj.obj.sclust = dclst; >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { >>> + sdj.obj.objsize = obj.objsize; >>> + sdj.obj.stat = obj.stat; >>> + } >>> +#endif >>> + res = dir_sdi(&sdj, 0); >>> + if (res == FR_OK) { >>> + res = DIR_READ_FILE(&sdj); /* >>> Test if the directory is empty */ >>> + if (res == FR_OK) res = FR_DENIED; /* >>> Not empty? */ >>> + if (res == FR_NO_FILE) res = FR_OK; >>> /* Empty? */ >>> + } >>> + } >>> + } >>> + } >>> + if (res == FR_OK) { >>> + res = dir_remove(&dj); /* Remove the >>> directory entry */ >>> + if (res == FR_OK && dclst != 0) { /* Remove the >>> cluster chain if exist */ >>> +#if FF_FS_EXFAT >>> + res = remove_chain(&obj, dclst, 0); >>> +#else >>> + res = remove_chain(&dj.obj, dclst, 0); >>> +#endif >>> + } >>> + if (res == FR_OK) res = sync_fs(fs); >>> + } >>> + } >>> + FREE_NAMBUF(); >>> + } >>> + >>> + LEAVE_FF(fs, res); >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Create a >>> Directory */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_mkdir ( >>> + const TCHAR* path /* Pointer to the directory path */ >>> +) >>> +{ >>> + FRESULT res; >>> + DIR dj; >>> + FFOBJID sobj; >>> + FATFS *fs; >>> + DWORD dcl, pcl, tm; >>> + DEF_NAMBUF >>> + >>> + >>> + res = mount_volume(&path, &fs, FA_WRITE); /* Get logical >>> drive */ >>> + if (res == FR_OK) { >>> + dj.obj.fs = fs; >>> + INIT_NAMBUF(fs); >>> + res = follow_path(&dj, path); /* Follow the file >>> path */ >>> + if (res == FR_OK) res = FR_EXIST; /* Name collision? */ >>> + if (FF_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & >>> NS_DOT)) { /* Invalid name? */ >>> + res = FR_INVALID_NAME; >>> + } >>> + if (res == FR_NO_FILE) { /* It is clear to >>> create a new directory */ >>> + sobj.fs = fs; /* New object id to >>> create a new chain */ >>> + dcl = create_chain(&sobj, 0); /* Allocate a >>> cluster for the new directory */ >>> + res = FR_OK; >>> + if (dcl == 0) res = FR_DENIED; /* No space to >>> allocate a new cluster? */ >>> + if (dcl == 1) res = FR_INT_ERR; /* Any insanity? */ >>> + if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; /* Disk >>> error? */ >>> + tm = GET_FATTIME(); >>> + if (res == FR_OK) { >>> + res = dir_clear(fs, dcl); /* Clean up the new >>> table */ >>> + if (res == FR_OK) { >>> + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { >>> /* Create dot entries (FAT only) */ >>> + mem_set(fs->win + DIR_Name, ' ', 11); /* >>> Create "." entry */ >>> + fs->win[DIR_Name] = '.'; >>> + fs->win[DIR_Attr] = AM_DIR; >>> + st_dword(fs->win + DIR_ModTime, tm); >>> + st_clust(fs, fs->win, dcl); >>> + mem_cpy(fs->win + SZDIRE, fs->win, SZDIRE); >>> /* Create ".." entry */ >>> + fs->win[SZDIRE + 1] = '.'; pcl = dj.obj.sclust; >>> + st_clust(fs, fs->win + SZDIRE, pcl); >>> + fs->wflag = 1; >>> + } >>> + res = dir_register(&dj); /* Register the >>> object to the parent directoy */ >>> + } >>> + } >>> + if (res == FR_OK) { >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { /* Initialize >>> directory entry block */ >>> + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* >>> Created time */ >>> + st_dword(fs->dirbuf + XDIR_FstClus, dcl); /* >>> Table start cluster */ >>> + st_dword(fs->dirbuf + XDIR_FileSize, >>> (DWORD)fs->csize * SS(fs)); /* Directory size needs to be valid */ >>> + st_dword(fs->dirbuf + XDIR_ValidFileSize, >>> (DWORD)fs->csize * SS(fs)); >>> + fs->dirbuf[XDIR_GenFlags] = 3; /* >>> Initialize the object flag */ >>> + fs->dirbuf[XDIR_Attr] = AM_DIR; >>> /* Attribute */ >>> + res = store_xdir(&dj); >>> + } else >>> +#endif >>> + { >>> + st_dword(dj.dir + DIR_ModTime, tm); /* >>> Created time */ >>> + st_clust(fs, dj.dir, dcl); /* Table >>> start cluster */ >>> + dj.dir[DIR_Attr] = AM_DIR; /* >>> Attribute */ >>> + fs->wflag = 1; >>> + } >>> + if (res == FR_OK) { >>> + res = sync_fs(fs); >>> + } >>> + } else { >>> + remove_chain(&sobj, dcl, 0); /* Could not >>> register, remove the allocated cluster */ >>> + } >>> + } >>> + FREE_NAMBUF(); >>> + } >>> + >>> + LEAVE_FF(fs, res); >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Rename a >>> File/Directory */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_rename ( >>> + const TCHAR* path_old, /* Pointer to the object name to be >>> renamed */ >>> + const TCHAR* path_new /* Pointer to the new name */ >>> +) >>> +{ >>> + FRESULT res; >>> + DIR djo, djn; >>> + FATFS *fs; >>> + BYTE buf[FF_FS_EXFAT ? SZDIRE * 2 : SZDIRE], *dir; >>> + LBA_t sect; >>> + DEF_NAMBUF >>> + >>> + >>> + get_ldnumber(&path_new); /* Snip the >>> drive number of new name off */ >>> + res = mount_volume(&path_old, &fs, FA_WRITE); /* Get logical >>> drive of the old object */ >>> + if (res == FR_OK) { >>> + djo.obj.fs = fs; >>> + INIT_NAMBUF(fs); >>> + res = follow_path(&djo, path_old); /* Check old >>> object */ >>> + if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) >>> res = FR_INVALID_NAME; /* Check validity of name */ >>> +#if FF_FS_LOCK != 0 >>> + if (res == FR_OK) { >>> + res = chk_lock(&djo, 2); >>> + } >>> +#endif >>> + if (res == FR_OK) { /* Object to be >>> renamed is found */ >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { /* At exFAT volume */ >>> + BYTE nf, nn; >>> + WORD nh; >>> + >>> + mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save >>> 85+C0 entry of old object */ >>> + mem_cpy(&djn, &djo, sizeof djo); >>> + res = follow_path(&djn, path_new); /* Make >>> sure if new object name is not in use */ >>> + if (res == FR_OK) { /* Is new >>> name already in use by any other object? */ >>> + res = (djn.obj.sclust == djo.obj.sclust && >>> djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; >>> + } >>> + if (res == FR_NO_FILE) { /* It is a >>> valid path and no name collision */ >>> + res = dir_register(&djn); /* Register >>> the new entry */ >>> + if (res == FR_OK) { >>> + nf = fs->dirbuf[XDIR_NumSec]; nn = >>> fs->dirbuf[XDIR_NumName]; >>> + nh = ld_word(fs->dirbuf + XDIR_NameHash); >>> + mem_cpy(fs->dirbuf, buf, SZDIRE * 2); /* >>> Restore 85+C0 entry */ >>> + fs->dirbuf[XDIR_NumSec] = nf; >>> fs->dirbuf[XDIR_NumName] = nn; >>> + st_word(fs->dirbuf + XDIR_NameHash, nh); >>> + if (!(fs->dirbuf[XDIR_Attr] & AM_DIR)) >>> fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute if it is >>> a file */ >>> +/* Start of critical section where an interruption can cause a >>> cross-link */ >>> + res = store_xdir(&djn); >>> + } >>> + } >>> + } else >>> +#endif >>> + { /* At FAT/FAT32 volume */ >>> + mem_cpy(buf, djo.dir, SZDIRE); /* Save >>> directory entry of the object */ >>> + mem_cpy(&djn, &djo, sizeof (DIR)); /* >>> Duplicate the directory object */ >>> + res = follow_path(&djn, path_new); /* Make >>> sure if new object name is not in use */ >>> + if (res == FR_OK) { /* Is new >>> name already in use by any other object? */ >>> + res = (djn.obj.sclust == djo.obj.sclust && >>> djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; >>> + } >>> + if (res == FR_NO_FILE) { /* It is a >>> valid path and no name collision */ >>> + res = dir_register(&djn); /* Register >>> the new entry */ >>> + if (res == FR_OK) { >>> + dir = djn.dir; /* Copy >>> directory entry of the object except name */ >>> + mem_cpy(dir + 13, buf + 13, SZDIRE - 13); >>> + dir[DIR_Attr] = buf[DIR_Attr]; >>> + if (!(dir[DIR_Attr] & AM_DIR)) dir[DIR_Attr] >>> |= AM_ARC; /* Set archive attribute if it is a file */ >>> + fs->wflag = 1; >>> + if ((dir[DIR_Attr] & AM_DIR) && >>> djo.obj.sclust != djn.obj.sclust) { /* Update .. entry in the >>> sub-directory if needed */ >>> + sect = clst2sect(fs, ld_clust(fs, dir)); >>> + if (sect == 0) { >>> + res = FR_INT_ERR; >>> + } else { >>> +/* Start of critical section where an interruption can cause a >>> cross-link */ >>> + res = move_window(fs, sect); >>> + dir = fs->win + SZDIRE * 1; /* >>> Ptr to .. entry */ >>> + if (res == FR_OK && dir[1] == '.') { >>> + st_clust(fs, dir, djn.obj.sclust); >>> + fs->wflag = 1; >>> + } >>> + } >>> + } >>> + } >>> + } >>> + } >>> + if (res == FR_OK) { >>> + res = dir_remove(&djo); /* Remove old entry */ >>> + if (res == FR_OK) { >>> + res = sync_fs(fs); >>> + } >>> + } >>> +/* End of the critical section */ >>> + } >>> + FREE_NAMBUF(); >>> + } >>> + >>> + LEAVE_FF(fs, res); >>> +} >>> + >>> +#endif /* !FF_FS_READONLY */ >>> +#endif /* FF_FS_MINIMIZE == 0 */ >>> +#endif /* FF_FS_MINIMIZE <= 1 */ >>> +#endif /* FF_FS_MINIMIZE <= 2 */ >>> + >>> + >>> + >>> +#if FF_USE_CHMOD && !FF_FS_READONLY >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Change >>> Attribute */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_chmod ( >>> + const TCHAR* path, /* Pointer to the file path */ >>> + BYTE attr, /* Attribute bits */ >>> + BYTE mask /* Attribute mask to change */ >>> +) >>> +{ >>> + FRESULT res; >>> + DIR dj; >>> + FATFS *fs; >>> + DEF_NAMBUF >>> + >>> + >>> + res = mount_volume(&path, &fs, FA_WRITE); /* Get logical >>> drive */ >>> + if (res == FR_OK) { >>> + dj.obj.fs = fs; >>> + INIT_NAMBUF(fs); >>> + res = follow_path(&dj, path); /* Follow the file path */ >>> + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) >>> res = FR_INVALID_NAME; /* Check object validity */ >>> + if (res == FR_OK) { >>> + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid >>> attribute mask */ >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { >>> + fs->dirbuf[XDIR_Attr] = (attr & mask) | >>> (fs->dirbuf[XDIR_Attr] & (BYTE)~mask); /* Apply attribute change */ >>> + res = store_xdir(&dj); >>> + } else >>> +#endif >>> + { >>> + dj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] >>> & (BYTE)~mask); /* Apply attribute change */ >>> + fs->wflag = 1; >>> + } >>> + if (res == FR_OK) { >>> + res = sync_fs(fs); >>> + } >>> + } >>> + FREE_NAMBUF(); >>> + } >>> + >>> + LEAVE_FF(fs, res); >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Change >>> Timestamp */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_utime ( >>> + const TCHAR* path, /* Pointer to the file/directory name */ >>> + const FILINFO* fno /* Pointer to the timestamp to be set */ >>> +) >>> +{ >>> + FRESULT res; >>> + DIR dj; >>> + FATFS *fs; >>> + DEF_NAMBUF >>> + >>> + >>> + res = mount_volume(&path, &fs, FA_WRITE); /* Get logical >>> drive */ >>> + if (res == FR_OK) { >>> + dj.obj.fs = fs; >>> + INIT_NAMBUF(fs); >>> + res = follow_path(&dj, path); /* Follow the file path */ >>> + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) >>> res = FR_INVALID_NAME; /* Check object validity */ >>> + if (res == FR_OK) { >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { >>> + st_dword(fs->dirbuf + XDIR_ModTime, >>> (DWORD)fno->fdate << 16 | fno->ftime); >>> + res = store_xdir(&dj); >>> + } else >>> +#endif >>> + { >>> + st_dword(dj.dir + DIR_ModTime, (DWORD)fno->fdate << >>> 16 | fno->ftime); >>> + fs->wflag = 1; >>> + } >>> + if (res == FR_OK) { >>> + res = sync_fs(fs); >>> + } >>> + } >>> + FREE_NAMBUF(); >>> + } >>> + >>> + LEAVE_FF(fs, res); >>> +} >>> + >>> +#endif /* FF_USE_CHMOD && !FF_FS_READONLY */ >>> + >>> + >>> + >>> +#if FF_USE_LABEL >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Get Volume >>> Label */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_getlabel ( >>> + const TCHAR* path, /* Logical drive number */ >>> + TCHAR* label, /* Buffer to store the volume label */ >>> + DWORD* vsn /* Variable to store the volume serial >>> number */ >>> +) >>> +{ >>> + FRESULT res; >>> + DIR dj; >>> + FATFS *fs; >>> + UINT si, di; >>> + WCHAR wc; >>> + >>> + /* Get logical drive */ >>> + res = mount_volume(&path, &fs, 0); >>> + >>> + /* Get volume label */ >>> + if (res == FR_OK && label) { >>> + dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */ >>> + res = dir_sdi(&dj, 0); >>> + if (res == FR_OK) { >>> + res = DIR_READ_LABEL(&dj); /* Find a volume label >>> entry */ >>> + if (res == FR_OK) { >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { >>> + WCHAR hs; >>> + >>> + for (si = di = hs = 0; si < >>> dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 >>> entry */ >>> + wc = ld_word(dj.dir + XDIR_Label + si * 2); >>> + if (hs == 0 && IsSurrogate(wc)) { /* Is >>> the code a surrogate? */ >>> + hs = wc; continue; >>> + } >>> + wc = put_utf((DWORD)hs << 16 | wc, >>> &label[di], 4); >>> + if (wc == 0) { di = 0; break; } >>> + di += wc; >>> + hs = 0; >>> + } >>> + if (hs != 0) di = 0; /* Broken surrogate >>> pair? */ >>> + label[di] = 0; >>> + } else >>> +#endif >>> + { >>> + si = di = 0; /* Extract volume label from >>> AM_VOL entry */ >>> + while (si < 11) { >>> + wc = dj.dir[si++]; >>> +#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode output */ >>> + if (dbc_1st((BYTE)wc) && si < 11) wc = wc << >>> 8 | dj.dir[si++]; /* Is it a DBC? */ >>> + wc = ff_oem2uni(wc, >>> CODEPAGE); /* Convert it into Unicode */ >>> + if (wc != 0) wc = put_utf(wc, &label[di], >>> 4); /* Put it in Unicode */ >>> + if (wc == 0) { di = 0; break; } >>> + di += wc; >>> +#else /* ANSI/OEM output */ >>> + label[di++] = (TCHAR)wc; >>> +#endif >>> + } >>> + do { /* Truncate trailing spaces */ >>> + label[di] = 0; >>> + if (di == 0) break; >>> + } while (label[--di] == ' '); >>> + } >>> + } >>> + } >>> + if (res == FR_NO_FILE) { /* No label entry and return nul >>> string */ >>> + label[0] = 0; >>> + res = FR_OK; >>> + } >>> + } >>> + >>> + /* Get volume serial number */ >>> + if (res == FR_OK && vsn) { >>> + res = move_window(fs, fs->volbase); >>> + if (res == FR_OK) { >>> + switch (fs->fs_type) { >>> + case FS_EXFAT: >>> + di = BPB_VolIDEx; >>> + break; >>> + >>> + case FS_FAT32: >>> + di = BS_VolID32; >>> + break; >>> + >>> + default: >>> + di = BS_VolID; >>> + } >>> + *vsn = ld_dword(fs->win + di); >>> + } >>> + } >>> + >>> + LEAVE_FF(fs, res); >>> +} >>> + >>> + >>> + >>> +#if !FF_FS_READONLY >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Set Volume >>> Label */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_setlabel ( >>> + const TCHAR* label /* Volume label to set with heading >>> logical drive number */ >>> +) >>> +{ >>> + FRESULT res; >>> + DIR dj; >>> + FATFS *fs; >>> + BYTE dirvn[22]; >>> + UINT di; >>> + WCHAR wc; >>> + static const char badchr[] = "+.,;=[]/\\\"*:<>\?|\x7F"; /* >>> [0..] for FAT, [7..] for exFAT */ >>> +#if FF_USE_LFN >>> + DWORD dc; >>> +#endif >>> + >>> + /* Get logical drive */ >>> + res = mount_volume(&label, &fs, FA_WRITE); >>> + if (res != FR_OK) LEAVE_FF(fs, res); >>> + >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ >>> + mem_set(dirvn, 0, 22); >>> + di = 0; >>> + while ((UINT)*label >= ' ') { /* Create volume label */ >>> + dc = tchar2uni(&label); /* Get a Unicode character */ >>> + if (dc >= 0x10000) { >>> + if (dc == 0xFFFFFFFF || di >= 10) { /* Wrong >>> surrogate or buffer overflow */ >>> + dc = 0; >>> + } else { >>> + st_word(dirvn + di * 2, (WCHAR)(dc >> 16)); di++; >>> + } >>> + } >>> + if (dc == 0 || chk_chr(badchr + 7, (int)dc) || di >= 11) >>> { /* Check validity of the volume label */ >>> + LEAVE_FF(fs, FR_INVALID_NAME); >>> + } >>> + st_word(dirvn + di * 2, (WCHAR)dc); di++; >>> + } >>> + } else >>> +#endif >>> + { /* On the FAT/FAT32 volume */ >>> + mem_set(dirvn, ' ', 11); >>> + di = 0; >>> + while ((UINT)*label >= ' ') { /* Create volume label */ >>> +#if FF_USE_LFN >>> + dc = tchar2uni(&label); >>> + wc = (dc < 0x10000) ? ff_uni2oem(ff_wtoupper(dc), >>> CODEPAGE) : 0; >>> +#else /* ANSI/OEM input */ >>> + wc = (BYTE)*label++; >>> + if (dbc_1st((BYTE)wc)) wc = dbc_2nd((BYTE)*label) ? wc >>> << 8 | (BYTE)*label++ : 0; >>> + if (IsLower(wc)) wc -= 0x20; /* To upper ASCII >>> characters */ >>> +#if FF_CODE_PAGE == 0 >>> + if (ExCvt && wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To >>> upper extended characters (SBCS cfg) */ >>> +#elif FF_CODE_PAGE < 900 >>> + if (wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper >>> extended characters (SBCS cfg) */ >>> +#endif >>> +#endif >>> + if (wc == 0 || chk_chr(badchr + 0, (int)wc) || di >= >>> (UINT)((wc >= 0x100) ? 10 : 11)) { /* Reject invalid characters >>> for volume label */ >>> + LEAVE_FF(fs, FR_INVALID_NAME); >>> + } >>> + if (wc >= 0x100) dirvn[di++] = (BYTE)(wc >> 8); >>> + dirvn[di++] = (BYTE)wc; >>> + } >>> + if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* >>> Reject illegal name (heading DDEM) */ >>> + while (di && dirvn[di - 1] == ' ') di--; /* >>> Snip trailing spaces */ >>> + } >>> + >>> + /* Set volume label */ >>> + dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */ >>> + res = dir_sdi(&dj, 0); >>> + if (res == FR_OK) { >>> + res = DIR_READ_LABEL(&dj); /* Get volume label entry */ >>> + if (res == FR_OK) { >>> + if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { >>> + dj.dir[XDIR_NumLabel] = (BYTE)di; /* Change the >>> volume label */ >>> + mem_cpy(dj.dir + XDIR_Label, dirvn, 22); >>> + } else { >>> + if (di != 0) { >>> + mem_cpy(dj.dir, dirvn, 11); /* Change the >>> volume label */ >>> + } else { >>> + dj.dir[DIR_Name] = DDEM; /* Remove the volume >>> label */ >>> + } >>> + } >>> + fs->wflag = 1; >>> + res = sync_fs(fs); >>> + } else { /* No volume label entry or an error */ >>> + if (res == FR_NO_FILE) { >>> + res = FR_OK; >>> + if (di != 0) { /* Create a volume label entry */ >>> + res = dir_alloc(&dj, 1); /* Allocate an entry */ >>> + if (res == FR_OK) { >>> + mem_set(dj.dir, 0, SZDIRE); /* Clean the >>> entry */ >>> + if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { >>> + dj.dir[XDIR_Type] = ET_VLABEL; /* >>> Create volume label entry */ >>> + dj.dir[XDIR_NumLabel] = (BYTE)di; >>> + mem_cpy(dj.dir + XDIR_Label, dirvn, 22); >>> + } else { >>> + dj.dir[DIR_Attr] = AM_VOL; /* >>> Create volume label entry */ >>> + mem_cpy(dj.dir, dirvn, 11); >>> + } >>> + fs->wflag = 1; >>> + res = sync_fs(fs); >>> + } >>> + } >>> + } >>> + } >>> + } >>> + >>> + LEAVE_FF(fs, res); >>> +} >>> + >>> +#endif /* !FF_FS_READONLY */ >>> +#endif /* FF_USE_LABEL */ >>> + >>> + >>> + >>> +#if FF_USE_EXPAND && !FF_FS_READONLY >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Allocate a Contiguous Blocks to the >>> File */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_expand ( >>> + FIL* fp, /* Pointer to the file object */ >>> + FSIZE_t fsz, /* File size to be expanded to */ >>> + BYTE opt /* Operation mode 0:Find and prepare or 1:Find >>> and allocate */ >>> +) >>> +{ >>> + FRESULT res; >>> + FATFS *fs; >>> + DWORD n, clst, stcl, scl, ncl, tcl, lclst; >>> + >>> + >>> + res = validate(&fp->obj, &fs); /* Check validity of the >>> file object */ >>> + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) >>> LEAVE_FF(fs, res); >>> + if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) >>> LEAVE_FF(fs, FR_DENIED); >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, >>> FR_DENIED); /* Check if in size limit */ >>> +#endif >>> + n = (DWORD)fs->csize * SS(fs); /* Cluster size */ >>> + tcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0); /* Number >>> of clusters required */ >>> + stcl = fs->last_clst; lclst = 0; >>> + if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2; >>> + >>> +#if FF_FS_EXFAT >>> + if (fs->fs_type == FS_EXFAT) { >>> + scl = find_bitmap(fs, stcl, tcl); /* Find a >>> contiguous cluster block */ >>> + if (scl == 0) res = FR_DENIED; /* No >>> contiguous cluster block was found */ >>> + if (scl == 0xFFFFFFFF) res = FR_DISK_ERR; >>> + if (res == FR_OK) { /* A contiguous free area is found */ >>> + if (opt) { /* Allocate it now */ >>> + res = change_bitmap(fs, scl, tcl, 1); /* Mark the >>> cluster block 'in use' */ >>> + lclst = scl + tcl - 1; >>> + } else { /* Set it as suggested point for next >>> allocation */ >>> + lclst = scl - 1; >>> + } >>> + } >>> + } else >>> +#endif >>> + { >>> + scl = clst = stcl; ncl = 0; >>> + for (;;) { /* Find a contiguous cluster block */ >>> + n = get_fat(&fp->obj, clst); >>> + if (++clst >= fs->n_fatent) clst = 2; >>> + if (n == 1) { res = FR_INT_ERR; break; } >>> + if (n == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } >>> + if (n == 0) { /* Is it a free cluster? */ >>> + if (++ncl == tcl) break; /* Break if a contiguous >>> cluster block is found */ >>> + } else { >>> + scl = clst; ncl = 0; /* Not a free cluster */ >>> + } >>> + if (clst == stcl) { res = FR_DENIED; break; } /* No >>> contiguous cluster? */ >>> + } >>> + if (res == FR_OK) { /* A contiguous free area is found */ >>> + if (opt) { /* Allocate it now */ >>> + for (clst = scl, n = tcl; n; clst++, n--) { /* >>> Create a cluster chain on the FAT */ >>> + res = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : >>> clst + 1); >>> + if (res != FR_OK) break; >>> + lclst = clst; >>> + } >>> + } else { /* Set it as suggested point for next >>> allocation */ >>> + lclst = scl - 1; >>> + } >>> + } >>> + } >>> + >>> + if (res == FR_OK) { >>> + fs->last_clst = lclst; /* Set suggested start cluster >>> to start next */ >>> + if (opt) { /* Is it allocated now? */ >>> + fp->obj.sclust = scl; /* Update object allocation >>> information */ >>> + fp->obj.objsize = fsz; >>> + if (FF_FS_EXFAT) fp->obj.stat = 2; /* Set status >>> 'contiguous chain' */ >>> + fp->flag |= FA_MODIFIED; >>> + if (fs->free_clst <= fs->n_fatent - 2) { /* Update >>> FSINFO */ >>> + fs->free_clst -= tcl; >>> + fs->fsi_flag |= 1; >>> + } >>> + } >>> + } >>> + >>> + LEAVE_FF(fs, res); >>> +} >>> + >>> +#endif /* FF_USE_EXPAND && !FF_FS_READONLY */ >>> + >>> + >>> + >>> +#if FF_USE_FORWARD >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Forward Data to the Stream >>> Directly */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_forward ( >>> + FIL* fp, /* Pointer to the file object */ >>> + UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming >>> function */ >>> + UINT btf, /* Number of bytes to forward */ >>> + UINT* bf /* Pointer to number of bytes >>> forwarded */ >>> +) >>> +{ >>> + FRESULT res; >>> + FATFS *fs; >>> + DWORD clst; >>> + LBA_t sect; >>> + FSIZE_t remain; >>> + UINT rcnt, csect; >>> + BYTE *dbuf; >>> + >>> + >>> + *bf = 0; /* Clear transfer byte counter */ >>> + res = validate(&fp->obj, &fs); /* Check validity of the >>> file object */ >>> + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) >>> LEAVE_FF(fs, res); >>> + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check >>> access mode */ >>> + >>> + remain = fp->obj.objsize - fp->fptr; >>> + if (btf > remain) btf = (UINT)remain; /* Truncate btf >>> by remaining bytes */ >>> + >>> + for ( ; btf && (*func)(0, 0); /* Repeat >>> until all data transferred or stream goes busy */ >>> + fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { >>> + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* >>> Sector offset in the cluster */ >>> + if (fp->fptr % SS(fs) == 0) { /* On the >>> sector boundary? */ >>> + if (csect == 0) { /* On the >>> cluster boundary? */ >>> + clst = (fp->fptr == 0) ? /* On the top of >>> the file? */ >>> + fp->obj.sclust : get_fat(&fp->obj, fp->clust); >>> + if (clst <= 1) ABORT(fs, FR_INT_ERR); >>> + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); >>> + fp->clust = clst; /* Update >>> current cluster */ >>> + } >>> + } >>> + sect = clst2sect(fs, fp->clust); /* Get current >>> data sector */ >>> + if (sect == 0) ABORT(fs, FR_INT_ERR); >>> + sect += csect; >>> +#if FF_FS_TINY >>> + if (move_window(fs, sect) != FR_OK) ABORT(fs, >>> FR_DISK_ERR); /* Move sector window to the file data */ >>> + dbuf = fs->win; >>> +#else >>> + if (fp->sect != sect) { /* Fill sector cache with >>> file data */ >>> +#if !FF_FS_READONLY >>> + if (fp->flag & FA_DIRTY) { /* Write-back dirty >>> sector cache */ >>> + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != >>> RES_OK) ABORT(fs, FR_DISK_ERR); >>> + fp->flag &= (BYTE)~FA_DIRTY; >>> + } >>> +#endif >>> + if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) >>> ABORT(fs, FR_DISK_ERR); >>> + } >>> + dbuf = fp->buf; >>> +#endif >>> + fp->sect = sect; >>> + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of >>> bytes remains in the sector */ >>> + if (rcnt > btf) rcnt = btf; /* Clip it by >>> btr if needed */ >>> + rcnt = (*func)(dbuf + ((UINT)fp->fptr % SS(fs)), rcnt); >>> /* Forward the file data */ >>> + if (rcnt == 0) ABORT(fs, FR_INT_ERR); >>> + } >>> + >>> + LEAVE_FF(fs, FR_OK); >>> +} >>> +#endif /* FF_USE_FORWARD */ >>> + >>> + >>> + >>> +#if !FF_FS_READONLY && FF_USE_MKFS >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Create FAT/exFAT >>> volume */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +#define N_SEC_TRACK 63 /* Sectors per track for >>> determination of drive CHS */ >>> +#define GPT_ALIGN 0x100000 /* Alignment of partitions in >>> GPT [byte] (>=128KB) */ >>> +#define GPT_ITEMS 128 /* Number of GPT table size >>> (>=128, sector aligned) */ >>> + >>> + >>> +/* Create partitions on the physical drive in format of MBR or GPT */ >>> + >>> +static FRESULT create_partition ( >>> + BYTE drv, /* Physical drive number */ >>> + const LBA_t plst[], /* Partition list */ >>> + BYTE sys, /* System ID (for only MBR, temp setting) */ >>> + BYTE* buf /* Working buffer for a sector */ >>> +) >>> +{ >>> + UINT i, cy; >>> + LBA_t sz_drv; >>> + DWORD sz_drv32, s_lba32, n_lba32; >>> + BYTE *pte, hd, n_hd, sc, n_sc; >>> + >>> + /* Get drive size */ >>> + if (disk_ioctl(drv, GET_SECTOR_COUNT, &sz_drv) != RES_OK) return >>> FR_DISK_ERR; >>> + >>> +#if FF_LBA64 >>> + if (sz_drv >= FF_MIN_GPT) { /* Create partitions in GPT */ >>> + WORD ss; >>> + UINT sz_pt, pi, si, ofs; >>> + DWORD bcc, rnd, align; >>> + QWORD s_lba64, n_lba64, sz_pool, s_bpt; >>> + static const BYTE gpt_mbr[16] = {0x00, 0x00, 0x02, 0x00, >>> 0xEE, 0xFE, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; >>> + >>> +#if FF_MAX_SS != FF_MIN_SS >>> + if (disk_ioctl(drv, GET_SECTOR_SIZE, &ss) != RES_OK) return >>> FR_DISK_ERR; /* Get sector size */ >>> + if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) >>> return FR_DISK_ERR; >>> +#else >>> + ss = FF_MAX_SS; >>> +#endif >>> + rnd = GET_FATTIME(); /* Random seed */ >>> + align = GPT_ALIGN / ss; /* Partition alignment >>> [sector] */ >>> + sz_pt = GPT_ITEMS * SZ_GPTE / ss; /* Size of PT [sector] */ >>> + s_bpt = sz_drv - sz_pt - 1; /* Backup PT start sector */ >>> + s_lba64 = 2 + sz_pt; /* First allocatable sector */ >>> + sz_pool = s_bpt - s_lba64; /* Size of allocatable >>> area */ >>> + bcc = 0xFFFFFFFF; n_lba64 = 1; >>> + pi = si = 0; /* partition table index, size table index */ >>> + do { >>> + if (pi * SZ_GPTE % ss == 0) mem_set(buf, 0, ss); /* >>> Clean the buffer if needed */ >>> + if (n_lba64 != 0) { /* Is the size table not >>> termintated? */ >>> + s_lba64 = (s_lba64 + align - 1) & ((QWORD)0 - >>> align); /* Align partition start */ >>> + n_lba64 = plst[si++]; /* Get a partition size */ >>> + if (n_lba64 <= 100) { /* Is the size in >>> percentage? */ >>> + n_lba64 = sz_pool * n_lba64 / 100; >>> + n_lba64 = (n_lba64 + align - 1) & ((QWORD)0 - >>> align); /* Align partition end (only if in percentage) */ >>> + } >>> + if (s_lba64 + n_lba64 > s_bpt) { /* Clip at end >>> of the pool */ >>> + n_lba64 = (s_lba64 < s_bpt) ? s_bpt - s_lba64 : 0; >>> + } >>> + } >>> + if (n_lba64 != 0) { /* Add a partition? */ >>> + ofs = pi * SZ_GPTE % ss; >>> + mem_cpy(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, >>> 16); /* Partition GUID (Microsoft Basic Data) */ >>> + rnd = make_rand(rnd, buf + ofs + GPTE_UpGuid, >>> 16); /* Unique partition GUID */ >>> + st_qword(buf + ofs + GPTE_FstLba, >>> s_lba64); /* Partition start LBA */ >>> + st_qword(buf + ofs + GPTE_LstLba, s_lba64 + n_lba64 >>> - 1); /* Partition end LBA */ >>> + s_lba64 += n_lba64; /* Next partition LBA */ >>> + } >>> + if ((pi + 1) * SZ_GPTE % ss == 0) { /* Write the >>> buffer if it is filled up */ >>> + for (i = 0; i < ss; bcc = crc32(bcc, buf[i++])) ; /* >>> Calculate table check sum */ >>> + if (disk_write(drv, buf, 2 + pi * SZ_GPTE / ss, 1) >>> != RES_OK) return FR_DISK_ERR; /* Primary table */ >>> + if (disk_write(drv, buf, s_bpt + pi * SZ_GPTE / ss, >>> 1) != RES_OK) return FR_DISK_ERR; /* Secondary table */ >>> + } >>> + } while (++pi < GPT_ITEMS); >>> + >>> + /* Create primary GPT header */ >>> + mem_set(buf, 0, ss); >>> + mem_cpy(buf + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", >>> 16); /* Signature, version (1.0) and size (92) */ >>> + st_dword(buf + GPTH_PtBcc, ~bcc); /* Table >>> check sum */ >>> + st_qword(buf + GPTH_CurLba, 1); /* LBA of >>> this header */ >>> + st_qword(buf + GPTH_BakLba, sz_drv - 1); /* LBA of >>> another header */ >>> + st_qword(buf + GPTH_FstLba, 2 + sz_pt); /* LBA of >>> first allocatable sector */ >>> + st_qword(buf + GPTH_LstLba, s_bpt - 1); /* LBA of >>> last allocatable sector */ >>> + st_dword(buf + GPTH_PteSize, SZ_GPTE); /* Size of >>> a table entry */ >>> + st_dword(buf + GPTH_PtNum, GPT_ITEMS); /* Number >>> of table entries */ >>> + st_dword(buf + GPTH_PtOfs, 2); /* LBA of >>> this table */ >>> + rnd = make_rand(rnd, buf + GPTH_DskGuid, 16); /* Disk >>> GUID */ >>> + for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, >>> buf[i++])) ; /* Calculate header check sum */ >>> + st_dword(buf + GPTH_Bcc, ~bcc); /* Header >>> check sum */ >>> + if (disk_write(drv, buf, 1, 1) != RES_OK) return FR_DISK_ERR; >>> + >>> + /* Create secondary GPT header */ >>> + st_qword(buf + GPTH_CurLba, sz_drv - 1); /* LBA of >>> this header */ >>> + st_qword(buf + GPTH_BakLba, 1); /* LBA of >>> another header */ >>> + st_qword(buf + GPTH_PtOfs, s_bpt); /* LBA of >>> this table */ >>> + st_dword(buf + GPTH_Bcc, 0); >>> + for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, >>> buf[i++])) ; /* Calculate header check sum */ >>> + st_dword(buf + GPTH_Bcc, ~bcc); /* Header >>> check sum */ >>> + if (disk_write(drv, buf, sz_drv - 1, 1) != RES_OK) return >>> FR_DISK_ERR; >>> + >>> + /* Create protective MBR */ >>> + mem_set(buf, 0, ss); >>> + mem_cpy(buf + MBR_Table, gpt_mbr, 16); /* Create >>> a GPT partition */ >>> + st_word(buf + BS_55AA, 0xAA55); >>> + if (disk_write(drv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; >>> + >>> + } else >>> +#endif >>> + { /* Create partitions in MBR */ >>> + sz_drv32 = (DWORD)sz_drv; >>> + n_sc = N_SEC_TRACK; /* Determine drive CHS without >>> any consideration of the drive geometry */ >>> + for (n_hd = 8; n_hd != 0 && sz_drv32 / n_hd / n_sc > 1024; >>> n_hd *= 2) ; >>> + if (n_hd == 0) n_hd = 255; /* Number of heads needs to be >>> <256 */ >>> + >>> + mem_set(buf, 0, FF_MAX_SS); /* Clear MBR */ >>> + pte = buf + MBR_Table; /* Partition table in the MBR */ >>> + for (i = 0, s_lba32 = n_sc; i < 4 && s_lba32 != 0 && s_lba32 >>> < sz_drv32; i++, s_lba32 += n_lba32) { >>> + n_lba32 = (DWORD)plst[i]; /* Get partition size */ >>> + if (n_lba32 <= 100) n_lba32 = (n_lba32 == 100) ? >>> sz_drv32 : sz_drv32 / 100 * n_lba32; /* Size in percentage? */ >>> + if (s_lba32 + n_lba32 > sz_drv32 || s_lba32 + n_lba32 < >>> s_lba32) n_lba32 = sz_drv32 - s_lba32; /* Clip at drive size */ >>> + if (n_lba32 == 0) break; /* End of table or no sector >>> to allocate? */ >>> + >>> + st_dword(pte + PTE_StLba, s_lba32); /* Start LBA */ >>> + st_dword(pte + PTE_SizLba, n_lba32); /* Number of >>> sectors */ >>> + pte[PTE_System] = sys; /* System type */ >>> + >>> + cy = (UINT)(s_lba32 / n_sc / n_hd); /* Start >>> cylinder */ >>> + hd = (BYTE)(s_lba32 / n_sc % n_hd); /* Start head */ >>> + sc = (BYTE)(s_lba32 % n_sc + 1); /* Start sector */ >>> + pte[PTE_StHead] = hd; >>> + pte[PTE_StSec] = (BYTE)((cy >> 2 & 0xC0) | sc); >>> + pte[PTE_StCyl] = (BYTE)cy; >>> + >>> + cy = (UINT)((s_lba32 + n_lba32 - 1) / n_sc / n_hd); >>> /* End cylinder */ >>> + hd = (BYTE)((s_lba32 + n_lba32 - 1) / n_sc % n_hd); >>> /* End head */ >>> + sc = (BYTE)((s_lba32 + n_lba32 - 1) % n_sc + 1); /* >>> End sector */ >>> + pte[PTE_EdHead] = hd; >>> + pte[PTE_EdSec] = (BYTE)((cy >> 2 & 0xC0) | sc); >>> + pte[PTE_EdCyl] = (BYTE)cy; >>> + >>> + pte += SZ_PTE; /* Next entry */ >>> + } >>> + >>> + st_word(buf + BS_55AA, 0xAA55); /* MBR signature */ >>> + if (disk_write(drv, buf, 0, 1) != RES_OK) return >>> FR_DISK_ERR; /* Write it to the MBR */ >>> + } >>> + >>> + return FR_OK; >>> +} >>> + >>> + >>> + >>> +FRESULT f_mkfs ( >>> + const TCHAR* path, /* Logical drive number */ >>> + const MKFS_PARM* opt, /* Format options */ >>> + void* work, /* Pointer to working buffer (null: >>> use heap memory) */ >>> + UINT len /* Size of working buffer [byte] */ >>> +) >>> +{ >>> + static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* >>> Cluster size boundary for FAT volume (4Ks unit) */ >>> + static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* >>> Cluster size boundary for FAT32 volume (128Ks unit) */ >>> + static const MKFS_PARM defopt = {FM_ANY, 0, 0, 0, 0}; /* >>> Default parameter */ >>> + BYTE fsopt, fsty, sys, *buf, *pte, pdrv, ipart; >>> + WORD ss; /* Sector size */ >>> + DWORD sz_buf, sz_blk, n_clst, pau, nsect, n; >>> + LBA_t sz_vol, b_vol, b_fat, b_data; /* Size of volume, >>> Base LBA of volume, fat, data */ >>> + LBA_t sect, lba[2]; >>> + DWORD sz_rsv, sz_fat, sz_dir, sz_au; /* Size of reserved, >>> fat, dir, data, cluster */ >>> + UINT n_fat, n_root, i; /* Index, Number of >>> FATs and Number of roor dir entries */ >>> + int vol; >>> + DSTATUS ds; >>> + FRESULT fr; >>> + >>> + >>> + /* Check mounted drive and clear work area */ >>> + vol = get_ldnumber(&path); /* Get target >>> logical drive */ >>> + if (vol < 0) return FR_INVALID_DRIVE; >>> + if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear the fs >>> object if mounted */ >>> + pdrv = LD2PD(vol); /* Physical drive */ >>> + ipart = LD2PT(vol); /* Partition (0:create as new, >>> 1..:get from partition table) */ >>> + if (!opt) opt = &defopt; /* Use default parameter if it is >>> not given */ >>> + >>> + /* Get physical drive status (sz_drv, sz_blk, ss) */ >>> + ds = disk_initialize(pdrv); >>> + if (ds & STA_NOINIT) return FR_NOT_READY; >>> + if (ds & STA_PROTECT) return FR_WRITE_PROTECTED; >>> + sz_blk = opt->align; >>> + if (sz_blk == 0 && disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != >>> RES_OK) sz_blk = 1; >>> + if (sz_blk == 0 || sz_blk > 0x8000 || (sz_blk & (sz_blk - 1))) >>> sz_blk = 1; >>> +#if FF_MAX_SS != FF_MIN_SS >>> + if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return >>> FR_DISK_ERR; >>> + if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) return >>> FR_DISK_ERR; >>> +#else >>> + ss = FF_MAX_SS; >>> +#endif >>> + /* Options for FAT sub-type and FAT parameters */ >>> + fsopt = opt->fmt & (FM_ANY | FM_SFD); >>> + n_fat = (opt->n_fat >= 1 && opt->n_fat <= 2) ? opt->n_fat : 1; >>> + n_root = (opt->n_root >= 1 && opt->n_root <= 32768 && >>> (opt->n_root % (ss / SZDIRE)) == 0) ? opt->n_root : 512; >>> + sz_au = (opt->au_size <= 0x1000000 && (opt->au_size & >>> (opt->au_size - 1)) == 0) ? opt->au_size : 0; >>> + sz_au /= ss; /* Byte --> Sector */ >>> + >>> + /* Get working buffer */ >>> + sz_buf = len / ss; /* Size of working buffer [sector] */ >>> + if (sz_buf == 0) return FR_NOT_ENOUGH_CORE; >>> + buf = (BYTE*)work; /* Working buffer */ >>> +#if FF_USE_LFN == 3 >>> + if (!buf) buf = ff_memalloc(sz_buf * ss); /* Use heap memory >>> for working buffer */ >>> +#endif >>> + if (!buf) return FR_NOT_ENOUGH_CORE; >>> + >>> + /* Determine where the volume to be located (b_vol, sz_vol) */ >>> + b_vol = sz_vol = 0; >>> + if (FF_MULTI_PARTITION && ipart != 0) { /* Is the volume >>> associated with any specific partition? */ >>> + /* Get partition location from the existing partition table */ >>> + if (disk_read(pdrv, buf, 0, 1) != RES_OK) >>> LEAVE_MKFS(FR_DISK_ERR); /* Load MBR */ >>> + if (ld_word(buf + BS_55AA) != 0xAA55) >>> LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if MBR is valid */ >>> +#if FF_LBA64 >>> + if (buf[MBR_Table + PTE_System] == 0xEE) { /* GPT >>> protective MBR? */ >>> + DWORD n_ent, ofs; >>> + QWORD pt_lba; >>> + >>> + /* Get the partition location from GPT */ >>> + if (disk_read(pdrv, buf, 1, 1) != RES_OK) >>> LEAVE_MKFS(FR_DISK_ERR); /* Load GPT header sector (next to MBR) */ >>> + if (!test_gpt_header(buf)) >>> LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if GPT header is valid */ >>> + n_ent = ld_dword(buf + GPTH_PtNum); /* Number of >>> entries */ >>> + pt_lba = ld_qword(buf + GPTH_PtOfs); /* Table start >>> sector */ >>> + ofs = i = 0; >>> + while (n_ent) { /* Find MS Basic partition with >>> order of ipart */ >>> + if (ofs == 0 && disk_read(pdrv, buf, pt_lba++, 1) != >>> RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Get PT sector */ >>> + if (!mem_cmp(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, >>> 16) && ++i == ipart) { /* MS basic data partition? */ >>> + b_vol = ld_qword(buf + ofs + GPTE_FstLba); >>> + sz_vol = ld_qword(buf + ofs + GPTE_LstLba) - >>> b_vol + 1; >>> + break; >>> + } >>> + n_ent--; ofs = (ofs + SZ_GPTE) % ss; /* Next >>> entry */ >>> + } >>> + if (n_ent == 0) LEAVE_MKFS(FR_MKFS_ABORTED); /* >>> Partition not found */ >>> + fsopt |= 0x80; /* Partitioning is in GPT */ >>> + } else >>> +#endif >>> + { /* Get the partition location from MBR partition table */ >>> + pte = buf + (MBR_Table + (ipart - 1) * SZ_PTE); >>> + if (ipart > 4 || pte[PTE_System] == 0) >>> LEAVE_MKFS(FR_MKFS_ABORTED); /* No partition? */ >>> + b_vol = ld_dword(pte + PTE_StLba); /* Get volume >>> start sector */ >>> + sz_vol = ld_dword(pte + PTE_SizLba); /* Get volume >>> size */ >>> + } >>> + } else { /* The volume is associated with a physical drive */ >>> + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) >>> LEAVE_MKFS(FR_DISK_ERR); >>> + if (!(fsopt & FM_SFD)) { /* To be partitioned? */ >>> + /* Create a single-partition on the drive in this >>> function */ >>> +#if FF_LBA64 >>> + if (sz_vol >= FF_MIN_GPT) { /* Which partition type >>> to create, MBR or GPT? */ >>> + fsopt |= 0x80; /* Partitioning is in GPT */ >>> + b_vol = GPT_ALIGN / ss; sz_vol -= b_vol + GPT_ITEMS >>> * SZ_GPTE / ss + 1; /* Estimated partition offset and size */ >>> + } else >>> +#endif >>> + { /* Partitioning is in MBR */ >>> + if (sz_vol > N_SEC_TRACK) { >>> + b_vol = N_SEC_TRACK; sz_vol -= b_vol; /* >>> Estimated partition offset and size */ >>> + } >>> + } >>> + } >>> + } >>> + if (sz_vol < 128) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if >>> volume size is >=128s */ >>> + >>> + /* Now start to create a FAT volume at b_vol and sz_vol */ >>> + >>> + do { /* Pre-determine the FAT type */ >>> + if (FF_FS_EXFAT && (fsopt & FM_EXFAT)) { /* exFAT >>> possible? */ >>> + if ((fsopt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 >>> || sz_au > 128) { /* exFAT only, vol >= 64MS or sz_au > 128S ? */ >>> + fsty = FS_EXFAT; break; >>> + } >>> + } >>> +#if FF_LBA64 >>> + if (sz_vol >= 0x100000000) LEAVE_MKFS(FR_MKFS_ABORTED); >>> /* Too large volume for FAT/FAT32 */ >>> +#endif >>> + if (sz_au > 128) sz_au = 128; /* Invalid AU for >>> FAT/FAT32? */ >>> + if (fsopt & FM_FAT32) { /* FAT32 possible? */ >>> + if (!(fsopt & FM_FAT)) { /* no-FAT? */ >>> + fsty = FS_FAT32; break; >>> + } >>> + } >>> + if (!(fsopt & FM_FAT)) LEAVE_MKFS(FR_INVALID_PARAMETER); /* >>> no-FAT? */ >>> + fsty = FS_FAT16; >>> + } while (0); >>> + >>> +#if FF_FS_EXFAT >>> + if (fsty == FS_EXFAT) { /* Create an exFAT volume */ >>> + DWORD szb_bit, szb_case, sum, nbit, clu, clen[3]; >>> + WCHAR ch, si; >>> + UINT j, st; >>> + >>> + if (sz_vol < 0x1000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too >>> small volume for exFAT? */ >>> +#if FF_USE_TRIM >>> + lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform >>> storage device that the volume area may be erased */ >>> + disk_ioctl(pdrv, CTRL_TRIM, lba); >>> +#endif >>> + /* Determine FAT location, data location and number of >>> clusters */ >>> + if (sz_au == 0) { /* AU auto-selection */ >>> + sz_au = 8; >>> + if (sz_vol >= 0x80000) sz_au = 64; /* >= 512Ks */ >>> + if (sz_vol >= 0x4000000) sz_au = 256; /* >= 64Ms */ >>> + } >>> + b_fat = b_vol + 32; >>> /* FAT start at offset 32 */ >>> + sz_fat = (DWORD)((sz_vol / sz_au + 2) * 4 + ss - 1) / ss; /* >>> Number of FAT sectors */ >>> + b_data = (b_fat + sz_fat + sz_blk - 1) & ~((LBA_t)sz_blk - >>> 1); /* Align data area to the erase block boundary */ >>> + if (b_data - b_vol >= sz_vol / 2) >>> LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */ >>> + n_clst = (DWORD)(sz_vol - (b_data - b_vol)) / sz_au; /* >>> Number of clusters */ >>> + if (n_clst <16) LEAVE_MKFS(FR_MKFS_ABORTED); /* >>> Too few clusters? */ >>> + if (n_clst > MAX_EXFAT) LEAVE_MKFS(FR_MKFS_ABORTED); /* >>> Too many clusters? */ >>> + >>> + szb_bit = (n_clst + 7) / 8; >>> /* Size of allocation bitmap */ >>> + clen[0] = (szb_bit + sz_au * ss - 1) / (sz_au * ss); /* >>> Number of allocation bitmap clusters */ >>> + >>> + /* Create a compressed up-case table */ >>> + sect = b_data + sz_au * clen[0]; /* Table start sector */ >>> + sum = 0; /* Table checksum to be >>> stored in the 82 entry */ >>> + st = 0; si = 0; i = 0; j = 0; szb_case = 0; >>> + do { >>> + switch (st) { >>> + case 0: >>> + ch = (WCHAR)ff_wtoupper(si); /* Get an up-case >>> char */ >>> + if (ch != si) { >>> + si++; break; /* Store the up-case char if >>> exist */ >>> + } >>> + for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == >>> ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case >>> block */ >>> + if (j >= 128) { >>> + ch = 0xFFFF; st = 2; break; /* Compress the >>> no-case block if run is >= 128 chars */ >>> + } >>> + st = 1; /* Do not compress short run */ >>> + /* FALLTHROUGH */ >>> + case 1: >>> + ch = si++; /* Fill the short run */ >>> + if (--j == 0) st = 0; >>> + break; >>> + >>> + default: >>> + ch = (WCHAR)j; si += (WCHAR)j; /* Number of chars >>> to skip */ >>> + st = 0; >>> + } >>> + sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put >>> it into the write buffer */ >>> + sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum); >>> + i += 2; szb_case += 2; >>> + if (si == 0 || i == sz_buf * ss) { /* Write >>> buffered data when buffer full or end of process */ >>> + n = (i + ss - 1) / ss; >>> + if (disk_write(pdrv, buf, sect, n) != RES_OK) >>> LEAVE_MKFS(FR_DISK_ERR); >>> + sect += n; i = 0; >>> + } >>> + } while (si); >>> + clen[1] = (szb_case + sz_au * ss - 1) / (sz_au * ss); /* >>> Number of up-case table clusters */ >>> + clen[2] = 1; /* Number of root dir clusters */ >>> + >>> + /* Initialize the allocation bitmap */ >>> + sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start >>> of bitmap and number of bitmap sectors */ >>> + nbit = clen[0] + clen[1] + clen[2]; /* Number >>> of clusters in-use by system (bitmap, up-case and root-dir) */ >>> + do { >>> + mem_set(buf, 0, sz_buf * ss); /* >>> Initialize bitmap buffer */ >>> + for (i = 0; nbit != 0 && i / 8 < sz_buf * ss; buf[i / 8] >>> |= 1 << (i % 8), i++, nbit--) ; /* Mark used clusters */ >>> + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write >>> the buffered data */ >>> + if (disk_write(pdrv, buf, sect, n) != RES_OK) >>> LEAVE_MKFS(FR_DISK_ERR); >>> + sect += n; nsect -= n; >>> + } while (nsect); >>> + >>> + /* Initialize the FAT */ >>> + sect = b_fat; nsect = sz_fat; /* Start of FAT and number >>> of FAT sectors */ >>> + j = nbit = clu = 0; >>> + do { >>> + mem_set(buf, 0, sz_buf * ss); i = 0; /* Clear work >>> area and reset write offset */ >>> + if (clu == 0) { /* Initialize FAT [0] and FAT[1] */ >>> + st_dword(buf + i, 0xFFFFFFF8); i += 4; clu++; >>> + st_dword(buf + i, 0xFFFFFFFF); i += 4; clu++; >>> + } >>> + do { /* Create chains of bitmap, up-case and >>> root dir */ >>> + while (nbit != 0 && i < sz_buf * ss) { /* Create >>> a chain */ >>> + st_dword(buf + i, (nbit > 1) ? clu + 1 : >>> 0xFFFFFFFF); >>> + i += 4; clu++; nbit--; >>> + } >>> + if (nbit == 0 && j < 3) nbit = clen[j++]; /* Get >>> next chain length */ >>> + } while (nbit != 0 && i < sz_buf * ss); >>> + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the >>> buffered data */ >>> + if (disk_write(pdrv, buf, sect, n) != RES_OK) >>> LEAVE_MKFS(FR_DISK_ERR); >>> + sect += n; nsect -= n; >>> + } while (nsect); >>> + >>> + /* Initialize the root directory */ >>> + mem_set(buf, 0, sz_buf * ss); >>> + buf[SZDIRE * 0 + 0] = ET_VLABEL; /* >>> Volume label entry (no label) */ >>> + buf[SZDIRE * 1 + 0] = ET_BITMAP; /* >>> Bitmap entry */ >>> + st_dword(buf + SZDIRE * 1 + 20, 2); /* >>> cluster */ >>> + st_dword(buf + SZDIRE * 1 + 24, szb_bit); /* >>> size */ >>> + buf[SZDIRE * 2 + 0] = ET_UPCASE; /* >>> Up-case table entry */ >>> + st_dword(buf + SZDIRE * 2 + 4, sum); /* sum */ >>> + st_dword(buf + SZDIRE * 2 + 20, 2 + clen[0]); /* >>> cluster */ >>> + st_dword(buf + SZDIRE * 2 + 24, szb_case); /* >>> size */ >>> + sect = b_data + sz_au * (clen[0] + clen[1]); nsect = >>> sz_au; /* Start of the root directory and number of sectors */ >>> + do { /* Fill root directory sectors */ >>> + n = (nsect > sz_buf) ? sz_buf : nsect; >>> + if (disk_write(pdrv, buf, sect, n) != RES_OK) >>> LEAVE_MKFS(FR_DISK_ERR); >>> + mem_set(buf, 0, ss); /* Rest of entries are filled >>> with zero */ >>> + sect += n; nsect -= n; >>> + } while (nsect); >>> + >>> + /* Create two set of the exFAT VBR blocks */ >>> + sect = b_vol; >>> + for (n = 0; n < 2; n++) { >>> + /* Main record (+0) */ >>> + mem_set(buf, 0, ss); >>> + mem_cpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", >>> 11); /* Boot jump code (x86), OEM name */ >>> + st_qword(buf + BPB_VolOfsEx, b_vol); /* Volume offset in >>> the physical drive [sector] */ >>> + st_qword(buf + BPB_TotSecEx, sz_vol); /* Volume size >>> [sector] */ >>> + st_dword(buf + BPB_FatOfsEx, (DWORD)(b_fat - b_vol)); /* >>> FAT offset [sector] */ >>> + st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size >>> [sector] */ >>> + st_dword(buf + BPB_DataOfsEx, (DWORD)(b_data - >>> b_vol)); /* Data offset [sector] */ >>> + st_dword(buf + BPB_NumClusEx, n_clst); /* Number of >>> clusters */ >>> + st_dword(buf + BPB_RootClusEx, 2 + clen[0] + clen[1]); >>> /* Root dir cluster # */ >>> + st_dword(buf + BPB_VolIDEx, >>> GET_FATTIME()); /* VSN */ >>> + st_word(buf + BPB_FSVerEx, 0x100); /* Filesystem version >>> (1.00) */ >>> + for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; >>> buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */ >>> + for (buf[BPB_SecPerClusEx] = 0, i = sz_au; i >>= 1; >>> buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */ >>> + buf[BPB_NumFATsEx] = 1; /* Number of >>> FATs */ >>> + buf[BPB_DrvNumEx] = 0x80; /* Drive number >>> (for int13) */ >>> + st_word(buf + BS_BootCodeEx, 0xFEEB); /* Boot code >>> (x86) */ >>> + st_word(buf + BS_55AA, 0xAA55); /* Signature >>> (placed here regardless of sector size) */ >>> + for (i = sum = 0; i < ss; i++) { /* VBR checksum */ >>> + if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && >>> i != BPB_PercInUseEx) sum = xsum32(buf[i], sum); >>> + } >>> + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) >>> LEAVE_MKFS(FR_DISK_ERR); >>> + /* Extended bootstrap record (+1..+8) */ >>> + mem_set(buf, 0, ss); >>> + st_word(buf + ss - 2, 0xAA55); /* Signature (placed >>> at end of sector) */ >>> + for (j = 1; j < 9; j++) { >>> + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; >>> /* VBR checksum */ >>> + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) >>> LEAVE_MKFS(FR_DISK_ERR); >>> + } >>> + /* OEM/Reserved record (+9..+10) */ >>> + mem_set(buf, 0, ss); >>> + for ( ; j < 11; j++) { >>> + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; >>> /* VBR checksum */ >>> + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) >>> LEAVE_MKFS(FR_DISK_ERR); >>> + } >>> + /* Sum record (+11) */ >>> + for (i = 0; i < ss; i += 4) st_dword(buf + i, >>> sum); /* Fill with checksum value */ >>> + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) >>> LEAVE_MKFS(FR_DISK_ERR); >>> + } >>> + >>> + } else >>> +#endif /* FF_FS_EXFAT */ >>> + { /* Create an FAT/FAT32 volume */ >>> + do { >>> + pau = sz_au; >>> + /* Pre-determine number of clusters and FAT sub-type */ >>> + if (fsty == FS_FAT32) { /* FAT32 volume */ >>> + if (pau == 0) { /* AU auto-selection */ >>> + n = (DWORD)sz_vol / 0x20000; /* Volume size >>> in unit of 128KS */ >>> + for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; >>> i++, pau <<= 1) ; /* Get from table */ >>> + } >>> + n_clst = (DWORD)sz_vol / pau; /* Number of >>> clusters */ >>> + sz_fat = (n_clst * 4 + 8 + ss - 1) / ss; /* FAT >>> size [sector] */ >>> + sz_rsv = 32; /* Number of reserved sectors */ >>> + sz_dir = 0; /* No static directory */ >>> + if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) >>> LEAVE_MKFS(FR_MKFS_ABORTED); >>> + } else { /* FAT volume */ >>> + if (pau == 0) { /* au auto-selection */ >>> + n = (DWORD)sz_vol / 0x1000; /* Volume size in >>> unit of 4KS */ >>> + for (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, >>> pau <<= 1) ; /* Get from table */ >>> + } >>> + n_clst = (DWORD)sz_vol / pau; >>> + if (n_clst > MAX_FAT12) { >>> + n = n_clst * 2 + 4; /* FAT size [byte] */ >>> + } else { >>> + fsty = FS_FAT12; >>> + n = (n_clst * 3 + 1) / 2 + 3; /* FAT size >>> [byte] */ >>> + } >>> + sz_fat = (n + ss - 1) / ss; /* FAT size >>> [sector] */ >>> + sz_rsv = 1; /* Number of >>> reserved sectors */ >>> + sz_dir = (DWORD)n_root * SZDIRE / ss; /* Root dir >>> size [sector] */ >>> + } >>> + b_fat = b_vol + sz_rsv; /* FAT >>> base */ >>> + b_data = b_fat + sz_fat * n_fat + sz_dir; /* Data >>> base */ >>> + >>> + /* Align data area to erase block boundary (for flash >>> memory media) */ >>> + n = (DWORD)(((b_data + sz_blk - 1) & ~(sz_blk - 1)) - >>> b_data); /* Sectors to next nearest from current data base */ >>> + if (fsty == FS_FAT32) { /* FAT32: Move FAT */ >>> + sz_rsv += n; b_fat += n; >>> + } else { /* FAT: Expand FAT */ >>> + if (n % n_fat) { /* Adjust fractional error if >>> needed */ >>> + n--; sz_rsv++; b_fat++; >>> + } >>> + sz_fat += n / n_fat; >>> + } >>> + >>> + /* Determine number of clusters and final check of >>> validity of the FAT sub-type */ >>> + if (sz_vol < b_data + pau * 16 - b_vol) >>> LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */ >>> + n_clst = ((DWORD)sz_vol - sz_rsv - sz_fat * n_fat - >>> sz_dir) / pau; >>> + if (fsty == FS_FAT32) { >>> + if (n_clst <= MAX_FAT16) { /* Too few clusters >>> for FAT32? */ >>> + if (sz_au == 0 && (sz_au = pau / 2) != 0) >>> continue; /* Adjust cluster size and retry */ >>> + LEAVE_MKFS(FR_MKFS_ABORTED); >>> + } >>> + } >>> + if (fsty == FS_FAT16) { >>> + if (n_clst > MAX_FAT16) { /* Too many clusters >>> for FAT16 */ >>> + if (sz_au == 0 && (pau * 2) <= 64) { >>> + sz_au = pau * 2; continue; /* Adjust >>> cluster size and retry */ >>> + } >>> + if ((fsopt & FM_FAT32)) { >>> + fsty = FS_FAT32; continue; /* Switch type >>> to FAT32 and retry */ >>> + } >>> + if (sz_au == 0 && (sz_au = pau * 2) <= 128) >>> continue; /* Adjust cluster size and retry */ >>> + LEAVE_MKFS(FR_MKFS_ABORTED); >>> + } >>> + if (n_clst <= MAX_FAT12) { /* Too few clusters >>> for FAT16 */ >>> + if (sz_au == 0 && (sz_au = pau * 2) <= 128) >>> continue; /* Adjust cluster size and retry */ >>> + LEAVE_MKFS(FR_MKFS_ABORTED); >>> + } >>> + } >>> + if (fsty == FS_FAT12 && n_clst > MAX_FAT12) >>> LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters for FAT12 */ >>> + >>> + /* Ok, it is the valid cluster configuration */ >>> + break; >>> + } while (1); >>> + >>> +#if FF_USE_TRIM >>> + lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform >>> storage device that the volume area may be erased */ >>> + disk_ioctl(pdrv, CTRL_TRIM, lba); >>> +#endif >>> + /* Create FAT VBR */ >>> + mem_set(buf, 0, ss); >>> + mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* >>> Boot jump code (x86), OEM name */ >>> + st_word(buf + BPB_BytsPerSec, ss); /* Sector >>> size [byte] */ >>> + buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster >>> size [sector] */ >>> + st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of >>> reserved area */ >>> + buf[BPB_NumFATs] = (BYTE)n_fat; /* Number >>> of FATs */ >>> + st_word(buf + BPB_RootEntCnt, (WORD)((fsty == FS_FAT32) ? 0 >>> : n_root)); /* Number of root directory entries */ >>> + if (sz_vol < 0x10000) { >>> + st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume >>> size in 16-bit LBA */ >>> + } else { >>> + st_dword(buf + BPB_TotSec32, (DWORD)sz_vol); /* >>> Volume size in 32-bit LBA */ >>> + } >>> + buf[BPB_Media] = 0xF8; /* Media >>> descriptor byte */ >>> + st_word(buf + BPB_SecPerTrk, 63); /* Number >>> of sectors per track (for int13) */ >>> + st_word(buf + BPB_NumHeads, 255); /* Number >>> of heads (for int13) */ >>> + st_dword(buf + BPB_HiddSec, (DWORD)b_vol); /* Volume >>> offset in the physical drive [sector] */ >>> + if (fsty == FS_FAT32) { >>> + st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */ >>> + st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size >>> [sector] */ >>> + st_dword(buf + BPB_RootClus32, 2); /* Root >>> directory cluster # (2) */ >>> + st_word(buf + BPB_FSInfo32, 1); /* Offset >>> of FSINFO sector (VBR + 1) */ >>> + st_word(buf + BPB_BkBootSec32, 6); /* Offset >>> of backup VBR (VBR + 6) */ >>> + buf[BS_DrvNum32] = 0x80; /* Drive >>> number (for int13) */ >>> + buf[BS_BootSig32] = 0x29; /* Extended >>> boot signature */ >>> + mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", >>> 19); /* Volume label, FAT signature */ >>> + } else { >>> + st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */ >>> + st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size >>> [sector] */ >>> + buf[BS_DrvNum] = 0x80; /* Drive >>> number (for int13) */ >>> + buf[BS_BootSig] = 0x29; /* >>> Extended boot signature */ >>> + mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", >>> 19); /* Volume label, FAT signature */ >>> + } >>> + st_word(buf + BS_55AA, 0xAA55); /* >>> Signature (offset is fixed here regardless of sector size) */ >>> + if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) >>> LEAVE_MKFS(FR_DISK_ERR); /* Write it to the VBR sector */ >>> + >>> + /* Create FSINFO record if needed */ >>> + if (fsty == FS_FAT32) { >>> + disk_write(pdrv, buf, b_vol + 6, 1); /* Write >>> backup VBR (VBR + 6) */ >>> + mem_set(buf, 0, ss); >>> + st_dword(buf + FSI_LeadSig, 0x41615252); >>> + st_dword(buf + FSI_StrucSig, 0x61417272); >>> + st_dword(buf + FSI_Free_Count, n_clst - 1); /* Number >>> of free clusters */ >>> + st_dword(buf + FSI_Nxt_Free, 2); /* Last >>> allocated cluster# */ >>> + st_word(buf + BS_55AA, 0xAA55); >>> + disk_write(pdrv, buf, b_vol + 7, 1); /* Write >>> backup FSINFO (VBR + 7) */ >>> + disk_write(pdrv, buf, b_vol + 1, 1); /* Write >>> original FSINFO (VBR + 1) */ >>> + } >>> + >>> + /* Initialize FAT area */ >>> + mem_set(buf, 0, sz_buf * ss); >>> + sect = b_fat; /* FAT start sector */ >>> + for (i = 0; i < n_fat; i++) { /* Initialize FATs >>> each */ >>> + if (fsty == FS_FAT32) { >>> + st_dword(buf + 0, 0xFFFFFFF8); /* FAT[0] */ >>> + st_dword(buf + 4, 0xFFFFFFFF); /* FAT[1] */ >>> + st_dword(buf + 8, 0x0FFFFFFF); /* FAT[2] (root >>> directory) */ >>> + } else { >>> + st_dword(buf + 0, (fsty == FS_FAT12) ? 0xFFFFF8 : >>> 0xFFFFFFF8); /* FAT[0] and FAT[1] */ >>> + } >>> + nsect = sz_fat; /* Number of FAT sectors */ >>> + do { /* Fill FAT sectors */ >>> + n = (nsect > sz_buf) ? sz_buf : nsect; >>> + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) >>> LEAVE_MKFS(FR_DISK_ERR); >>> + mem_set(buf, 0, ss); /* Rest of FAT all are >>> cleared */ >>> + sect += n; nsect -= n; >>> + } while (nsect); >>> + } >>> + >>> + /* Initialize root directory (fill with zero) */ >>> + nsect = (fsty == FS_FAT32) ? pau : sz_dir; /* Number of >>> root directory sectors */ >>> + do { >>> + n = (nsect > sz_buf) ? sz_buf : nsect; >>> + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) >>> LEAVE_MKFS(FR_DISK_ERR); >>> + sect += n; nsect -= n; >>> + } while (nsect); >>> + } >>> + >>> + /* A FAT volume has been created here */ >>> + >>> + /* Determine system ID in the MBR partition table */ >>> + if (FF_FS_EXFAT && fsty == FS_EXFAT) { >>> + sys = 0x07; /* exFAT */ >>> + } else { >>> + if (fsty == FS_FAT32) { >>> + sys = 0x0C; /* FAT32X */ >>> + } else { >>> + if (sz_vol >= 0x10000) { >>> + sys = 0x06; /* FAT12/16 (large) */ >>> + } else { >>> + sys = (fsty == FS_FAT16) ? 0x04 : 0x01; /* FAT16 >>> : FAT12 */ >>> + } >>> + } >>> + } >>> + >>> + /* Update partition information */ >>> + if (FF_MULTI_PARTITION && ipart != 0) { /* Volume is in the >>> existing partition */ >>> + if (!FF_LBA64 || !(fsopt & 0x80)) { >>> + /* Update system ID in the partition table */ >>> + if (disk_read(pdrv, buf, 0, 1) != RES_OK) >>> LEAVE_MKFS(FR_DISK_ERR); /* Read the MBR */ >>> + buf[MBR_Table + (ipart - 1) * SZ_PTE + PTE_System] = >>> sys; /* Set system ID */ >>> + if (disk_write(pdrv, buf, 0, 1) != RES_OK) >>> LEAVE_MKFS(FR_DISK_ERR); /* Write it back to the MBR */ >>> + } >>> + } else { /* Volume as a new >>> single partition */ >>> + if (!(fsopt & FM_SFD)) { /* Create partition table if not >>> in SFD */ >>> + lba[0] = sz_vol, lba[1] = 0; >>> + fr = create_partition(pdrv, lba, sys, buf); >>> + if (fr != FR_OK) LEAVE_MKFS(fr); >>> + } >>> + } >>> + >>> + if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) >>> LEAVE_MKFS(FR_DISK_ERR); >>> + >>> + LEAVE_MKFS(FR_OK); >>> +} >>> + >>> + >>> + >>> + >>> +#if FF_MULTI_PARTITION >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Create Partition Table on the Physical >>> Drive */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_fdisk ( >>> + BYTE pdrv, /* Physical drive number */ >>> + const LBA_t ptbl[], /* Pointer to the size table for each >>> partitions */ >>> + void* work /* Pointer to the working buffer (null: >>> use heap memory) */ >>> +) >>> +{ >>> + BYTE *buf = (BYTE*)work; >>> + DSTATUS stat; >>> + >>> + >>> + stat = disk_initialize(pdrv); >>> + if (stat & STA_NOINIT) return FR_NOT_READY; >>> + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; >>> +#if FF_USE_LFN == 3 >>> + if (!buf) buf = ff_memalloc(FF_MAX_SS); /* Use heap memory >>> for working buffer */ >>> +#endif >>> + if (!buf) return FR_NOT_ENOUGH_CORE; >>> + >>> + LEAVE_MKFS(create_partition(pdrv, ptbl, 0x07, buf)); >>> +} >>> + >>> +#endif /* FF_MULTI_PARTITION */ >>> +#endif /* !FF_FS_READONLY && FF_USE_MKFS */ >>> + >>> + >>> + >>> + >>> +#if FF_USE_STRFUNC >>> +#if FF_USE_LFN && FF_LFN_UNICODE && (FF_STRF_ENCODE < 0 || >>> FF_STRF_ENCODE > 3) >>> +#error Wrong FF_STRF_ENCODE setting >>> +#endif >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Get a String from the >>> File */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +TCHAR* f_gets ( >>> + TCHAR* buff, /* Pointer to the buffer to store read string */ >>> + int len, /* Size of string buffer (items) */ >>> + FIL* fp /* Pointer to the file object */ >>> +) >>> +{ >>> + int nc = 0; >>> + TCHAR *p = buff; >>> + BYTE s[4]; >>> + UINT rc; >>> + DWORD dc; >>> +#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE <= 2 >>> + WCHAR wc; >>> +#endif >>> +#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE == 3 >>> + UINT ct; >>> +#endif >>> + >>> +#if FF_USE_LFN && FF_LFN_UNICODE /* With code conversion >>> (Unicode API) */ >>> + /* Make a room for the character and terminator */ >>> + if (FF_LFN_UNICODE == 1) len -= (FF_STRF_ENCODE == 0) ? 1 : 2; >>> + if (FF_LFN_UNICODE == 2) len -= (FF_STRF_ENCODE == 0) ? 3 : 4; >>> + if (FF_LFN_UNICODE == 3) len -= 1; >>> + while (nc < len) { >>> +#if FF_STRF_ENCODE == 0 /* Read a character in >>> ANSI/OEM */ >>> + f_read(fp, s, 1, &rc); /* Get a code unit */ >>> + if (rc != 1) break; /* EOF? */ >>> + wc = s[0]; >>> + if (dbc_1st((BYTE)wc)) { /* DBC 1st byte? */ >>> + f_read(fp, s, 1, &rc); /* Get DBC 2nd byte */ >>> + if (rc != 1 || !dbc_2nd(s[0])) continue; /* Wrong >>> code? */ >>> + wc = wc << 8 | s[0]; >>> + } >>> + dc = ff_oem2uni(wc, CODEPAGE); /* OEM --> */ >>> + if (dc == 0) continue; >>> +#elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2 /* Read a >>> character in UTF-16LE/BE */ >>> + f_read(fp, s, 2, &rc); /* Get a code unit */ >>> + if (rc != 2) break; /* EOF? */ >>> + dc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1]; >>> + if (IsSurrogateL(dc)) continue; /* Broken surrogate pair? */ >>> + if (IsSurrogateH(dc)) { /* High surrogate? */ >>> + f_read(fp, s, 2, &rc); /* Get low surrogate */ >>> + if (rc != 2) break; /* EOF? */ >>> + wc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1]; >>> + if (!IsSurrogateL(wc)) continue; /* Broken surrogate >>> pair? */ >>> + dc = ((dc & 0x3FF) + 0x40) << 10 | (wc & 0x3FF); /* >>> Merge surrogate pair */ >>> + } >>> +#else /* Read a character in UTF-8 */ >>> + f_read(fp, s, 1, &rc); /* Get a code unit */ >>> + if (rc != 1) break; /* EOF? */ >>> + dc = s[0]; >>> + if (dc >= 0x80) { /* Multi-byte sequence? */ >>> + ct = 0; >>> + if ((dc & 0xE0) == 0xC0) { dc &= 0x1F; ct = 1; } /* >>> 2-byte sequence? */ >>> + if ((dc & 0xF0) == 0xE0) { dc &= 0x0F; ct = 2; } /* >>> 3-byte sequence? */ >>> + if ((dc & 0xF8) == 0xF0) { dc &= 0x07; ct = 3; } /* >>> 4-byte sequence? */ >>> + if (ct == 0) continue; >>> + f_read(fp, s, ct, &rc); /* Get trailing bytes */ >>> + if (rc != ct) break; >>> + rc = 0; >>> + do { /* Merge the byte sequence */ >>> + if ((s[rc] & 0xC0) != 0x80) break; >>> + dc = dc << 6 | (s[rc] & 0x3F); >>> + } while (++rc < ct); >>> + if (rc != ct || dc < 0x80 || IsSurrogate(dc) || dc >= >>> 0x110000) continue; /* Wrong encoding? */ >>> + } >>> +#endif >>> + /* A code point is avaialble in dc to be output */ >>> + >>> + if (FF_USE_STRFUNC == 2 && dc == '\r') continue; /* Strip >>> \r off if needed */ >>> +#if FF_LFN_UNICODE == 1 || FF_LFN_UNICODE == 3 /* Output it in >>> UTF-16/32 encoding */ >>> + if (FF_LFN_UNICODE == 1 && dc >= 0x10000) { /* Out of BMP >>> at UTF-16? */ >>> + *p++ = (TCHAR)(0xD800 | ((dc >> 10) - 0x40)); nc++; >>> /* Make and output high surrogate */ >>> + dc = 0xDC00 | (dc & 0x3FF); /* Make low surrogate */ >>> + } >>> + *p++ = (TCHAR)dc; nc++; >>> + if (dc == '\n') break; /* End of line? */ >>> +#elif FF_LFN_UNICODE == 2 /* Output it in UTF-8 encoding */ >>> + if (dc < 0x80) { /* Single byte? */ >>> + *p++ = (TCHAR)dc; >>> + nc++; >>> + if (dc == '\n') break; /* End of line? */ >>> + } else { >>> + if (dc < 0x800) { /* 2-byte sequence? */ >>> + *p++ = (TCHAR)(0xC0 | (dc >> 6 & 0x1F)); >>> + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); >>> + nc += 2; >>> + } else { >>> + if (dc < 0x10000) { /* 3-byte sequence? */ >>> + *p++ = (TCHAR)(0xE0 | (dc >> 12 & 0x0F)); >>> + *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F)); >>> + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); >>> + nc += 3; >>> + } else { /* 4-byte sequence? */ >>> + *p++ = (TCHAR)(0xF0 | (dc >> 18 & 0x07)); >>> + *p++ = (TCHAR)(0x80 | (dc >> 12 & 0x3F)); >>> + *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F)); >>> + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); >>> + nc += 4; >>> + } >>> + } >>> + } >>> +#endif >>> + } >>> + >>> +#else /* Byte-by-byte read without any conversion >>> (ANSI/OEM API) */ >>> + len -= 1; /* Make a room for the terminator */ >>> + while (nc < len) { >>> + f_read(fp, s, 1, &rc); /* Get a byte */ >>> + if (rc != 1) break; /* EOF? */ >>> + dc = s[0]; >>> + if (FF_USE_STRFUNC == 2 && dc == '\r') continue; >>> + *p++ = (TCHAR)dc; nc++; >>> + if (dc == '\n') break; >>> + } >>> +#endif >>> + >>> + *p = 0; /* Terminate the string */ >>> + return nc ? buff : 0; /* When no data read due to EOF or >>> error, return with error. */ >>> +} >>> + >>> + >>> + >>> + >>> +#if !FF_FS_READONLY >>> +#include <stdarg.h> >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Put a Character to the File >>> (sub-functions) */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +/* Putchar output buffer and work area */ >>> + >>> +typedef struct { >>> + FIL *fp; /* Ptr to the writing file */ >>> + int idx, nchr; /* Write index of buf[] (-1:error), number of >>> encoding units written */ >>> +#if FF_USE_LFN && FF_LFN_UNICODE == 1 >>> + WCHAR hs; >>> +#elif FF_USE_LFN && FF_LFN_UNICODE == 2 >>> + BYTE bs[4]; >>> + UINT wi, ct; >>> +#endif >>> + BYTE buf[64]; /* Write buffer */ >>> +} putbuff; >>> + >>> + >>> +/* Buffered write with code conversion */ >>> + >>> +static void putc_bfd (putbuff* pb, TCHAR c) >>> +{ >>> + UINT n; >>> + int i, nc; >>> +#if FF_USE_LFN && FF_LFN_UNICODE >>> + WCHAR hs, wc; >>> +#if FF_LFN_UNICODE == 2 >>> + DWORD dc; >>> + const TCHAR *tp; >>> +#endif >>> +#endif >>> + >>> + if (FF_USE_STRFUNC == 2 && c == '\n') { /* LF -> CRLF >>> conversion */ >>> + putc_bfd(pb, '\r'); >>> + } >>> + >>> + i = pb->idx; /* Write index of pb->buf[] */ >>> + if (i < 0) return; >>> + nc = pb->nchr; /* Write unit counter */ >>> + >>> +#if FF_USE_LFN && FF_LFN_UNICODE >>> +#if FF_LFN_UNICODE == 1 /* UTF-16 input */ >>> + if (IsSurrogateH(c)) { /* High surrogate? */ >>> + pb->hs = c; return; /* Save it for next */ >>> + } >>> + hs = pb->hs; pb->hs = 0; >>> + if (hs != 0) { /* There is a leading high surrogate */ >>> + if (!IsSurrogateL(c)) hs = 0; /* Discard high surrogate >>> if not a surrogate pair */ >>> + } else { >>> + if (IsSurrogateL(c)) return; /* Discard stray low >>> surrogate */ >>> + } >>> + wc = c; >>> +#elif FF_LFN_UNICODE == 2 /* UTF-8 input */ >>> + for (;;) { >>> + if (pb->ct == 0) { /* Out of multi-byte sequence? */ >>> + pb->bs[pb->wi = 0] = (BYTE)c; /* Save 1st byte */ >>> + if ((BYTE)c < 0x80) break; /* Single >>> byte? */ >>> + if (((BYTE)c & 0xE0) == 0xC0) pb->ct = 1; /* 2-byte >>> sequence? */ >>> + if (((BYTE)c & 0xF0) == 0xE0) pb->ct = 2; /* 3-byte >>> sequence? */ >>> + if (((BYTE)c & 0xF1) == 0xF0) pb->ct = 3; /* 4-byte >>> sequence? */ >>> + return; >>> + } else { /* In the multi-byte sequence */ >>> + if (((BYTE)c & 0xC0) != 0x80) { /* Broken sequence? */ >>> + pb->ct = 0; continue; >>> + } >>> + pb->bs[++pb->wi] = (BYTE)c; /* Save the trailing byte */ >>> + if (--pb->ct == 0) break; /* End of multi-byte >>> sequence? */ >>> + return; >>> + } >>> + } >>> + tp = (const TCHAR*)pb->bs; >>> + dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */ >>> + if (dc == 0xFFFFFFFF) return; /* Wrong code? */ >>> + wc = (WCHAR)dc; >>> + hs = (WCHAR)(dc >> 16); >>> +#elif FF_LFN_UNICODE == 3 /* UTF-32 input */ >>> + if (IsSurrogate(c) || c >= 0x110000) return; /* Discard >>> invalid code */ >>> + if (c >= 0x10000) { /* Out of BMP? */ >>> + hs = (WCHAR)(0xD800 | ((c >> 10) - 0x40)); /* Make high >>> surrogate */ >>> + wc = 0xDC00 | (c & 0x3FF); /* Make low >>> surrogate */ >>> + } else { >>> + hs = 0; >>> + wc = (WCHAR)c; >>> + } >>> +#endif >>> + /* A code point in UTF-16 is available in hs and wc */ >>> + >>> +#if FF_STRF_ENCODE == 1 /* Write a code point in UTF-16LE */ >>> + if (hs != 0) { /* Surrogate pair? */ >>> + st_word(&pb->buf[i], hs); >>> + i += 2; >>> + nc++; >>> + } >>> + st_word(&pb->buf[i], wc); >>> + i += 2; >>> +#elif FF_STRF_ENCODE == 2 /* Write a code point in UTF-16BE */ >>> + if (hs != 0) { /* Surrogate pair? */ >>> + pb->buf[i++] = (BYTE)(hs >> 8); >>> + pb->buf[i++] = (BYTE)hs; >>> + nc++; >>> + } >>> + pb->buf[i++] = (BYTE)(wc >> 8); >>> + pb->buf[i++] = (BYTE)wc; >>> +#elif FF_STRF_ENCODE == 3 /* Write a code point in UTF-8 */ >>> + if (hs != 0) { /* 4-byte sequence? */ >>> + nc += 3; >>> + hs = (hs & 0x3FF) + 0x40; >>> + pb->buf[i++] = (BYTE)(0xF0 | hs >> 8); >>> + pb->buf[i++] = (BYTE)(0x80 | (hs >> 2 & 0x3F)); >>> + pb->buf[i++] = (BYTE)(0x80 | (hs & 3) << 4 | (wc >> 6 & 0x0F)); >>> + pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F)); >>> + } else { >>> + if (wc < 0x80) { /* Single byte? */ >>> + pb->buf[i++] = (BYTE)wc; >>> + } else { >>> + if (wc < 0x800) { /* 2-byte sequence? */ >>> + nc += 1; >>> + pb->buf[i++] = (BYTE)(0xC0 | wc >> 6); >>> + } else { /* 3-byte sequence */ >>> + nc += 2; >>> + pb->buf[i++] = (BYTE)(0xE0 | wc >> 12); >>> + pb->buf[i++] = (BYTE)(0x80 | (wc >> 6 & 0x3F)); >>> + } >>> + pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F)); >>> + } >>> + } >>> +#else /* Write a code point in ANSI/OEM */ >>> + if (hs != 0) return; >>> + wc = ff_uni2oem(wc, CODEPAGE); /* UTF-16 ==> ANSI/OEM */ >>> + if (wc == 0) return; >>> + if (wc >= 0x100) { >>> + pb->buf[i++] = (BYTE)(wc >> 8); nc++; >>> + } >>> + pb->buf[i++] = (BYTE)wc; >>> +#endif >>> + >>> +#else /* ANSI/OEM input (without >>> re-encoding) */ >>> + pb->buf[i++] = (BYTE)c; >>> +#endif >>> + >>> + if (i >= (int)(sizeof pb->buf) - 4) { /* Write buffered >>> characters to the file */ >>> + f_write(pb->fp, pb->buf, (UINT)i, &n); >>> + i = (n == (UINT)i) ? 0 : -1; >>> + } >>> + pb->idx = i; >>> + pb->nchr = nc + 1; >>> +} >>> + >>> + >>> +/* Flush remaining characters in the buffer */ >>> + >>> +static int putc_flush (putbuff* pb) >>> +{ >>> + UINT nw; >>> + >>> + if ( pb->idx >= 0 /* Flush buffered characters to the file */ >>> + && f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK >>> + && (UINT)pb->idx == nw) return pb->nchr; >>> + return -1; >>> +} >>> + >>> + >>> +/* Initialize write buffer */ >>> + >>> +static void putc_init (putbuff* pb, FIL* fp) >>> +{ >>> + mem_set(pb, 0, sizeof (putbuff)); >>> + pb->fp = fp; >>> +} >>> + >>> + >>> + >>> +int f_putc ( >>> + TCHAR c, /* A character to be output */ >>> + FIL* fp /* Pointer to the file object */ >>> +) >>> +{ >>> + putbuff pb; >>> + >>> + >>> + putc_init(&pb, fp); >>> + putc_bfd(&pb, c); /* Put the character */ >>> + return putc_flush(&pb); >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Put a String to the >>> File */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +int f_puts ( >>> + const TCHAR* str, /* Pointer to the string to be output */ >>> + FIL* fp /* Pointer to the file object */ >>> +) >>> +{ >>> + putbuff pb; >>> + >>> + >>> + putc_init(&pb, fp); >>> + while (*str) putc_bfd(&pb, *str++); /* Put the string */ >>> + return putc_flush(&pb); >>> +} >>> + >>> + >>> + >>> + >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Put a Formatted String to the >>> File */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +int f_printf ( >>> + FIL* fp, /* Pointer to the file object */ >>> + const TCHAR* fmt, /* Pointer to the format string */ >>> + ... /* Optional arguments... */ >>> +) >>> +{ >>> + va_list arp; >>> + putbuff pb; >>> + BYTE f, r; >>> + UINT i, j, w; >>> + DWORD v; >>> + TCHAR c, d, str[32], *p; >>> + >>> + >>> + putc_init(&pb, fp); >>> + >>> + va_start(arp, fmt); >>> + >>> + for (;;) { >>> + c = *fmt++; >>> + if (c == 0) break; /* End of string */ >>> + if (c != '%') { /* Non escape character */ >>> + putc_bfd(&pb, c); >>> + continue; >>> + } >>> + w = f = 0; >>> + c = *fmt++; >>> + if (c == '0') { /* Flag: '0' padding */ >>> + f = 1; c = *fmt++; >>> + } else { >>> + if (c == '-') { /* Flag: left justified */ >>> + f = 2; c = *fmt++; >>> + } >>> + } >>> + if (c == '*') { /* Minimum width by argument */ >>> + w = va_arg(arp, int); >>> + c = *fmt++; >>> + } else { >>> + while (IsDigit(c)) { /* Minimum width */ >>> + w = w * 10 + c - '0'; >>> + c = *fmt++; >>> + } >>> + } >>> + if (c == 'l' || c == 'L') { /* Type prefix: Size is long >>> int */ >>> + f |= 4; c = *fmt++; >>> + } >>> + if (c == 0) break; >>> + d = c; >>> + if (IsLower(d)) d -= 0x20; >>> + switch (d) { /* Atgument type is... */ >>> + case 'S': /* String */ >>> + p = va_arg(arp, TCHAR*); >>> + for (j = 0; p[j]; j++) ; >>> + if (!(f & 2)) { /* Right padded */ >>> + while (j++ < w) putc_bfd(&pb, ' ') ; >>> + } >>> + while (*p) putc_bfd(&pb, *p++) ; /* String body */ >>> + while (j++ < w) putc_bfd(&pb, ' ') ; /* Left padded */ >>> + continue; >>> + >>> + case 'C': /* Character */ >>> + putc_bfd(&pb, (TCHAR)va_arg(arp, int)); >>> + continue; >>> + >>> + case 'B': /* Unsigned binary */ >>> + r = 2; >>> + break; >>> + >>> + case 'O': /* Unsigned octal */ >>> + r = 8; >>> + break; >>> + >>> + case 'D': /* Signed decimal */ >>> + case 'U': /* Unsigned decimal */ >>> + r = 10; >>> + break; >>> + >>> + case 'X': /* Unsigned hexdecimal */ >>> + r = 16; >>> + break; >>> + >>> + default: /* Unknown type (pass-through) */ >>> + putc_bfd(&pb, c); continue; >>> + } >>> + >>> + /* Get an argument and put it in numeral */ >>> + v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? >>> (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int)); >>> + if (d == 'D' && (v & 0x80000000)) { >>> + v = 0 - v; >>> + f |= 8; >>> + } >>> + i = 0; >>> + do { >>> + d = (TCHAR)(v % r); v /= r; >>> + if (d > 9) d += (c == 'x') ? 0x27 : 0x07; >>> + str[i++] = d + '0'; >>> + } while (v && i < sizeof str / sizeof *str); >>> + if (f & 8) str[i++] = '-'; >>> + j = i; d = (f & 1) ? '0' : ' '; >>> + if (!(f & 2)) { >>> + while (j++ < w) putc_bfd(&pb, d); /* Right pad */ >>> + } >>> + do { >>> + putc_bfd(&pb, str[--i]); /* Number body */ >>> + } while (i); >>> + while (j++ < w) putc_bfd(&pb, d); /* Left pad */ >>> + } >>> + >>> + va_end(arp); >>> + >>> + return putc_flush(&pb); >>> +} >>> + >>> +#endif /* !FF_FS_READONLY */ >>> +#endif /* FF_USE_STRFUNC */ >>> + >>> + >>> + >>> +#if FF_CODE_PAGE == 0 >>> +/*-----------------------------------------------------------------------*/ >>> >>> +/* Set Active Codepage for the Path >>> Name */ >>> +/*-----------------------------------------------------------------------*/ >>> >>> + >>> +FRESULT f_setcp ( >>> + WORD cp /* Value to be set as active code page */ >>> +) >>> +{ >>> + static const WORD validcp[] = { 437, 720, 737, 771, >>> 775, 850, 852, 857, 860, 861, 862, 863, 864, 865, >>> 866, 869, 932, 936, 949, 950, 0}; >>> + static const BYTE* const tables[] = {Ct437, Ct720, Ct737, Ct771, >>> Ct775, Ct850, Ct852, Ct857, Ct860, Ct861, Ct862, Ct863, Ct864, Ct865, >>> Ct866, Ct869, Dc932, Dc936, Dc949, Dc950, 0}; >>> + UINT i; >>> + >>> + >>> + for (i = 0; validcp[i] != 0 && validcp[i] != cp; i++) ; /* >>> Find the code page */ >>> + if (validcp[i] != cp) return FR_INVALID_PARAMETER; /* Not >>> found? */ >>> + >>> + CodePage = cp; >>> + if (cp >= 900) { /* DBCS */ >>> + ExCvt = 0; >>> + DbcTbl = tables[i]; >>> + } else { /* SBCS */ >>> + ExCvt = tables[i]; >>> + DbcTbl = 0; >>> + } >>> + return FR_OK; >>> +} >>> +#endif /* FF_CODE_PAGE == 0 */ >>> diff --git a/fatfs/ff.h b/fatfs/ff.h >>> new file mode 100644 >>> index 0000000..ac41fa4 >>> --- /dev/null >>> +++ b/fatfs/ff.h >>> @@ -0,0 +1,425 @@ >>> +/*----------------------------------------------------------------------------/ >>> >>> +/ FatFs - Generic FAT Filesystem module >>> R0.14a / >>> +/-----------------------------------------------------------------------------/ >>> >>> +/ >>> +/ Copyright (C) 2020, ChaN, all right reserved. >>> +/ >>> +/ FatFs module is an open source software. Redistribution and use of >>> FatFs in >>> +/ source and binary forms, with or without modification, are >>> permitted provided >>> +/ that the following condition is met: >>> +/ >>> +/ 1. Redistributions of source code must retain the above copyright >>> notice, >>> +/ this condition and the following disclaimer. >>> +/ >>> +/ This software is provided by the copyright holder and contributors >>> "AS IS" >>> +/ and any warranties related to this software are DISCLAIMED. >>> +/ The copyright owner or contributors be NOT LIABLE for any damages >>> caused >>> +/ by use of this software. >>> +/ >>> +/ SPDX-License-Identifier: BSD-1-Clause >>> +/ >>> +/----------------------------------------------------------------------------*/ >>> >>> + >>> + >>> +#ifndef FF_DEFINED >>> +#define FF_DEFINED 80196 /* Revision ID */ >>> + >>> +#ifdef __cplusplus >>> +extern "C" { >>> +#endif >>> + >>> +#include "ffconf.h" /* FatFs configuration options */ >>> + >>> +#if FF_DEFINED != FFCONF_DEF >>> +#error Wrong configuration file (ffconf.h). >>> +#endif >>> + >>> + >>> +/* Integer types used for FatFs API */ >>> + >>> +#if defined(_WIN32) /* Main development platform */ >>> +#define FF_INTDEF 2 >>> +#include <windows.h> >>> +typedef unsigned __int64 QWORD; >>> +#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || >>> defined(__cplusplus) /* C99 or later */ >>> +#define FF_INTDEF 2 >>> +#include <stdint.h> >>> +typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ >>> +typedef unsigned char BYTE; /* char must be 8-bit */ >>> +typedef uint16_t WORD; /* 16-bit unsigned integer */ >>> +typedef uint32_t DWORD; /* 32-bit unsigned integer */ >>> +typedef uint64_t QWORD; /* 64-bit unsigned integer */ >>> +typedef WORD WCHAR; /* UTF-16 character type */ >>> +#else /* Earlier than C99 */ >>> +#define FF_INTDEF 1 >>> +typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ >>> +typedef unsigned char BYTE; /* char must be 8-bit */ >>> +typedef unsigned short WORD; /* 16-bit unsigned integer */ >>> +typedef unsigned long DWORD; /* 32-bit unsigned integer */ >>> +typedef WORD WCHAR; /* UTF-16 character type */ >>> +#endif >>> + >>> + >>> +/* Definitions of volume management */ >>> + >>> +#if FF_MULTI_PARTITION /* Multiple partition configuration */ >>> +typedef struct { >>> + BYTE pd; /* Physical drive number */ >>> + BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ >>> +} PARTITION; >>> +extern PARTITION VolToPart[]; /* Volume - Partition mapping table */ >>> +#endif >>> + >>> +#if FF_STR_VOLUME_ID >>> +#ifndef FF_VOLUME_STRS >>> +extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume >>> ID */ >>> +#endif >>> +#endif >>> + >>> + >>> + >>> +/* Type of path name strings on FatFs API */ >>> + >>> +#ifndef _INC_TCHAR >>> +#define _INC_TCHAR >>> + >>> +#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 >>> encoding */ >>> +typedef WCHAR TCHAR; >>> +#define _T(x) L ## x >>> +#define _TEXT(x) L ## x >>> +#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 >>> encoding */ >>> +typedef char TCHAR; >>> +#define _T(x) u8 ## x >>> +#define _TEXT(x) u8 ## x >>> +#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 >>> encoding */ >>> +typedef DWORD TCHAR; >>> +#define _T(x) U ## x >>> +#define _TEXT(x) U ## x >>> +#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3) >>> +#error Wrong FF_LFN_UNICODE setting >>> +#else /* ANSI/OEM code in >>> SBCS/DBCS */ >>> +typedef char TCHAR; >>> +#define _T(x) x >>> +#define _TEXT(x) x >>> +#endif >>> + >>> +#endif >>> + >>> + >>> + >>> +/* Type of file size and LBA variables */ >>> + >>> +#if FF_FS_EXFAT >>> +#if FF_INTDEF != 2 >>> +#error exFAT feature wants C99 or later >>> +#endif >>> +typedef QWORD FSIZE_t; >>> +#if FF_LBA64 >>> +typedef QWORD LBA_t; >>> +#else >>> +typedef DWORD LBA_t; >>> +#endif >>> +#else >>> +#if FF_LBA64 >>> +#error exFAT needs to be enabled when enable 64-bit LBA >>> +#endif >>> +typedef DWORD FSIZE_t; >>> +typedef DWORD LBA_t; >>> +#endif >>> + >>> + >>> + >>> +/* Filesystem object structure (FATFS) */ >>> + >>> +typedef struct { >>> + BYTE fs_type; /* Filesystem type (0:not mounted) */ >>> + BYTE pdrv; /* Associated physical drive */ >>> + BYTE n_fats; /* Number of FATs (1 or 2) */ >>> + BYTE wflag; /* win[] flag (b0:dirty) */ >>> + BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ >>> + WORD id; /* Volume mount ID */ >>> + WORD n_rootdir; /* Number of root directory entries >>> (FAT12/16) */ >>> + WORD csize; /* Cluster size [sectors] */ >>> +#if FF_MAX_SS != FF_MIN_SS >>> + WORD ssize; /* Sector size (512, 1024, 2048 or >>> 4096) */ >>> +#endif >>> +#if FF_USE_LFN >>> + WCHAR* lfnbuf; /* LFN working buffer */ >>> +#endif >>> +#if FF_FS_EXFAT >>> + BYTE* dirbuf; /* Directory entry block scratchpad >>> buffer for exFAT */ >>> +#endif >>> +#if FF_FS_REENTRANT >>> + FF_SYNC_t sobj; /* Identifier of sync object */ >>> +#endif >>> +#if !FF_FS_READONLY >>> + DWORD last_clst; /* Last allocated cluster */ >>> + DWORD free_clst; /* Number of free clusters */ >>> +#endif >>> +#if FF_FS_RPATH >>> + DWORD cdir; /* Current directory start cluster >>> (0:root) */ >>> +#if FF_FS_EXFAT >>> + DWORD cdc_scl; /* Containing directory start cluster >>> (invalid when cdir is 0) */ >>> + DWORD cdc_size; /* b31-b8:Size of containing >>> directory, b7-b0: Chain status */ >>> + DWORD cdc_ofs; /* Offset in the containing directory >>> (invalid when cdir is 0) */ >>> +#endif >>> +#endif >>> + DWORD n_fatent; /* Number of FAT entries (number of >>> clusters + 2) */ >>> + DWORD fsize; /* Size of an FAT [sectors] */ >>> + LBA_t volbase; /* Volume base sector */ >>> + LBA_t fatbase; /* FAT base sector */ >>> + LBA_t dirbase; /* Root directory base sector/cluster */ >>> + LBA_t database; /* Data base sector */ >>> +#if FF_FS_EXFAT >>> + LBA_t bitbase; /* Allocation bitmap base sector */ >>> +#endif >>> + LBA_t winsect; /* Current sector appearing in the >>> win[] */ >>> + BYTE win[FF_MAX_SS]; /* Disk access window for Directory, >>> FAT (and file data at tiny cfg) */ >>> +} FATFS; >>> + >>> + >>> + >>> +/* Object ID and allocation information (FFOBJID) */ >>> + >>> +typedef struct { >>> + FATFS* fs; /* Pointer to the hosting volume of >>> this object */ >>> + WORD id; /* Hosting volume mount ID */ >>> + BYTE attr; /* Object attribute */ >>> + BYTE stat; /* Object chain status (b1-0: =0:not >>> contiguous, =2:contiguous, =3:fragmented in this session, >>> b2:sub-directory stretched) */ >>> + DWORD sclust; /* Object data start cluster (0:no >>> cluster or root directory) */ >>> + FSIZE_t objsize; /* Object size (valid when sclust != >>> 0) */ >>> +#if FF_FS_EXFAT >>> + DWORD n_cont; /* Size of first fragment - 1 (valid >>> when stat == 3) */ >>> + DWORD n_frag; /* Size of last fragment needs to be >>> written to FAT (valid when not zero) */ >>> + DWORD c_scl; /* Containing directory start cluster >>> (valid when sclust != 0) */ >>> + DWORD c_size; /* b31-b8:Size of containing >>> directory, b7-b0: Chain status (valid when c_scl != 0) */ >>> + DWORD c_ofs; /* Offset in the containing directory >>> (valid when file object and sclust != 0) */ >>> +#endif >>> +#if FF_FS_LOCK >>> + UINT lockid; /* File lock ID origin from 1 (index >>> of file semaphore table Files[]) */ >>> +#endif >>> +} FFOBJID; >>> + >>> + >>> + >>> +/* File object structure (FIL) */ >>> + >>> +typedef struct { >>> + FFOBJID obj; /* Object identifier (must be the 1st >>> member to detect invalid object pointer) */ >>> + BYTE flag; /* File status flags */ >>> + BYTE err; /* Abort flag (error code) */ >>> + FSIZE_t fptr; /* File read/write pointer (Zeroed >>> on file open) */ >>> + DWORD clust; /* Current cluster of fpter (invalid >>> when fptr is 0) */ >>> + LBA_t sect; /* Sector number appearing in buf[] >>> (0:invalid) */ >>> +#if !FF_FS_READONLY >>> + LBA_t dir_sect; /* Sector number containing the >>> directory entry (not used at exFAT) */ >>> + BYTE* dir_ptr; /* Pointer to the directory entry in >>> the win[] (not used at exFAT) */ >>> +#endif >>> +#if FF_USE_FASTSEEK >>> + DWORD* cltbl; /* Pointer to the cluster link map >>> table (nulled on open, set by application) */ >>> +#endif >>> +#if !FF_FS_TINY >>> + BYTE buf[FF_MAX_SS]; /* File private data read/write >>> window */ >>> +#endif >>> +} FIL; >>> + >>> + >>> + >>> +/* Directory object structure (DIR) */ >>> + >>> +typedef struct { >>> + FFOBJID obj; /* Object identifier */ >>> + DWORD dptr; /* Current read/write offset */ >>> + DWORD clust; /* Current cluster */ >>> + LBA_t sect; /* Current sector (0:Read operation >>> has terminated) */ >>> + BYTE* dir; /* Pointer to the directory item in the >>> win[] */ >>> + BYTE fn[12]; /* SFN (in/out) >>> {body[8],ext[3],status[1]} */ >>> +#if FF_USE_LFN >>> + DWORD blk_ofs; /* Offset of current entry block being >>> processed (0xFFFFFFFF:Invalid) */ >>> +#endif >>> +#if FF_USE_FIND >>> + const TCHAR* pat; /* Pointer to the name matching pattern */ >>> +#endif >>> +} DIR; >>> + >>> + >>> + >>> +/* File information structure (FILINFO) */ >>> + >>> +typedef struct { >>> + FSIZE_t fsize; /* File size */ >>> + WORD fdate; /* Modified date */ >>> + WORD ftime; /* Modified time */ >>> + BYTE fattrib; /* File attribute */ >>> +#if FF_USE_LFN >>> + TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */ >>> + TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */ >>> +#else >>> + TCHAR fname[12 + 1]; /* File name */ >>> +#endif >>> +} FILINFO; >>> + >>> + >>> + >>> +/* Format parameter structure (MKFS_PARM) */ >>> + >>> +typedef struct { >>> + BYTE fmt; /* Format option (FM_FAT, FM_FAT32, >>> FM_EXFAT and FM_SFD) */ >>> + BYTE n_fat; /* Number of FATs */ >>> + UINT align; /* Data area alignment (sector) */ >>> + UINT n_root; /* Number of root directory entries */ >>> + DWORD au_size; /* Cluster size (byte) */ >>> +} MKFS_PARM; >>> + >>> + >>> + >>> +/* File function return code (FRESULT) */ >>> + >>> +typedef enum { >>> + FR_OK = 0, /* (0) Succeeded */ >>> + FR_DISK_ERR, /* (1) A hard error occurred in the low >>> level disk I/O layer */ >>> + FR_INT_ERR, /* (2) Assertion failed */ >>> + FR_NOT_READY, /* (3) The physical drive cannot work */ >>> + FR_NO_FILE, /* (4) Could not find the file */ >>> + FR_NO_PATH, /* (5) Could not find the path */ >>> + FR_INVALID_NAME, /* (6) The path name format is invalid */ >>> + FR_DENIED, /* (7) Access denied due to prohibited >>> access or directory full */ >>> + FR_EXIST, /* (8) Access denied due to prohibited >>> access */ >>> + FR_INVALID_OBJECT, /* (9) The file/directory object is >>> invalid */ >>> + FR_WRITE_PROTECTED, /* (10) The physical drive is write >>> protected */ >>> + FR_INVALID_DRIVE, /* (11) The logical drive number is >>> invalid */ >>> + FR_NOT_ENABLED, /* (12) The volume has no work area */ >>> + FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ >>> + FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any >>> problem */ >>> + FR_TIMEOUT, /* (15) Could not get a grant to >>> access the volume within defined period */ >>> + FR_LOCKED, /* (16) The operation is rejected >>> according to the file sharing policy */ >>> + FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not >>> be allocated */ >>> + FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > >>> FF_FS_LOCK */ >>> + FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ >>> +} FRESULT; >>> + >>> + >>> + >>> +/*--------------------------------------------------------------*/ >>> +/* FatFs module application interface */ >>> + >>> +FRESULT f_open (FIL* fp, const TCHAR* path, BYTE >>> mode); /* Open or create a file */ >>> +FRESULT f_close (FIL* fp); /* Close an open file object */ >>> +FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read >>> data from the file */ >>> +FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* >>> Write data to the file */ >>> +FRESULT f_lseek (FIL* fp, FSIZE_t >>> ofs); /* Move file pointer of the file >>> object */ >>> +FRESULT f_truncate (FIL* fp); /* Truncate the file */ >>> +FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */ >>> +FRESULT f_opendir (DIR* dp, const TCHAR* >>> path); /* Open a directory */ >>> +FRESULT f_closedir (DIR* dp); /* Close an open directory */ >>> +FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */ >>> +FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const >>> TCHAR* pattern); /* Find first file */ >>> +FRESULT f_findnext (DIR* dp, FILINFO* >>> fno); /* Find next file */ >>> +FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */ >>> +FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or >>> directory */ >>> +FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* >>> Rename/Move a file or directory */ >>> +FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */ >>> +FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change >>> attribute of a file/dir */ >>> +FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change >>> timestamp of a file/dir */ >>> +FRESULT f_chdir (const TCHAR* path); /* Change current directory */ >>> +FRESULT f_chdrive (const TCHAR* path); /* Change current drive */ >>> +FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */ >>> +FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** >>> fatfs); /* Get number of free clusters on the drive */ >>> +FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* >>> Get volume label */ >>> +FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ >>> +FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, >>> UINT* bf); /* Forward data to the stream */ >>> +FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a >>> contiguous block to the file */ >>> +FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* >>> Mount/Unmount a logical drive */ >>> +FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, >>> UINT len); /* Create a FAT volume */ >>> +FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* >>> Divide a physical drive into some partitions */ >>> +FRESULT f_setcp (WORD cp); /* Set current code page */ >>> +int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */ >>> +int f_puts (const TCHAR* str, FIL* >>> cp); /* Put a string to the file */ >>> +int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted >>> string to the file */ >>> +TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from >>> the file */ >>> + >>> +#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) >>> +#define f_error(fp) ((fp)->err) >>> +#define f_tell(fp) ((fp)->fptr) >>> +#define f_size(fp) ((fp)->obj.objsize) >>> +#define f_rewind(fp) f_lseek((fp), 0) >>> +#define f_rewinddir(dp) f_readdir((dp), 0) >>> +#define f_rmdir(path) f_unlink(path) >>> +#define f_unmount(path) f_mount(0, path, 0) >>> + >>> + >>> + >>> + >>> +/*--------------------------------------------------------------*/ >>> +/* Additional user defined functions */ >>> + >>> +/* RTC function */ >>> +#if !FF_FS_READONLY && !FF_FS_NORTC >>> +DWORD get_fattime (void); >>> +#endif >>> + >>> +/* LFN support functions */ >>> +#if FF_USE_LFN >= 1 /* Code conversion >>> (defined in unicode.c) */ >>> +WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode >>> conversion */ >>> +WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code >>> conversion */ >>> +DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case >>> conversion */ >>> +#endif >>> +#if FF_USE_LFN == 3 /* Dynamic memory >>> allocation */ >>> +void* ff_memalloc (UINT msize); /* Allocate memory block */ >>> +void ff_memfree (void* mblock); /* Free memory block */ >>> +#endif >>> + >>> +/* Sync functions */ >>> +#if FF_FS_REENTRANT >>> +int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync >>> object */ >>> +int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */ >>> +void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */ >>> +int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */ >>> +#endif >>> + >>> +/* Function for assigning UNIX devices to FatFs library. */ >>> +extern int fatfs_init(char *device); >>> +extern void fatfs_release(void); >>> + >>> +/*--------------------------------------------------------------*/ >>> +/* Flags and offset address */ >>> + >>> + >>> +/* File access mode and open method flags (3rd argument of f_open) */ >>> +#define FA_READ 0x01 >>> +#define FA_WRITE 0x02 >>> +#define FA_OPEN_EXISTING 0x00 >>> +#define FA_CREATE_NEW 0x04 >>> +#define FA_CREATE_ALWAYS 0x08 >>> +#define FA_OPEN_ALWAYS 0x10 >>> +#define FA_OPEN_APPEND 0x30 >>> + >>> +/* Fast seek controls (2nd argument of f_lseek) */ >>> +#define CREATE_LINKMAP ((FSIZE_t)0 - 1) >>> + >>> +/* Format options (2nd argument of f_mkfs) */ >>> +#define FM_FAT 0x01 >>> +#define FM_FAT32 0x02 >>> +#define FM_EXFAT 0x04 >>> +#define FM_ANY 0x07 >>> +#define FM_SFD 0x08 >>> + >>> +/* Filesystem type (FATFS.fs_type) */ >>> +#define FS_FAT12 1 >>> +#define FS_FAT16 2 >>> +#define FS_FAT32 3 >>> +#define FS_EXFAT 4 >>> + >>> +/* File attribute bits for directory entry (FILINFO.fattrib) */ >>> +#define AM_RDO 0x01 /* Read only */ >>> +#define AM_HID 0x02 /* Hidden */ >>> +#define AM_SYS 0x04 /* System */ >>> +#define AM_DIR 0x10 /* Directory */ >>> +#define AM_ARC 0x20 /* Archive */ >>> + >>> + >>> +#ifdef __cplusplus >>> +} >>> +#endif >>> + >>> +#endif /* FF_DEFINED */ >>> diff --git a/fatfs/ffconf.h b/fatfs/ffconf.h >>> new file mode 100644 >>> index 0000000..b85bbb0 >>> --- /dev/null >>> +++ b/fatfs/ffconf.h >>> @@ -0,0 +1,317 @@ >>> +/*---------------------------------------------------------------------------/ >>> >>> +/ FatFs Functional Configurations >>> +/----------------------------------------------------------------------------/ >>> >>> +/ >>> +/ Copyright (C) 2019, ChaN, all right reserved. >>> +/ >>> +/ FatFs module is an open source software. Redistribution and use of >>> FatFs in >>> +/ source and binary forms, with or without modification, are >>> permitted provided >>> +/ that the following condition is met: >>> +/ >>> +/ 1. Redistributions of source code must retain the above copyright >>> notice, >>> +/ this condition and the following disclaimer. >>> +/ >>> +/ This software is provided by the copyright holder and contributors >>> "AS IS" >>> +/ and any warranties related to this software are DISCLAIMED. >>> +/ The copyright owner or contributors be NOT LIABLE for any damages >>> caused >>> +/ by use of this software. >>> +/ >>> +/ SPDX-License-Identifier: BSD-1-Clause >>> +/ >>> +/---------------------------------------------------------------------------*/ >>> >>> + >>> + >>> +#define FFCONF_DEF 80196 /* Revision ID */ >>> + >>> +/*---------------------------------------------------------------------------/ >>> >>> +/ Function Configurations >>> +/---------------------------------------------------------------------------*/ >>> >>> + >>> +#define FF_FS_READONLY 0 >>> +/* This option switches read-only configuration. (0:Read/Write or >>> 1:Read-only) >>> +/ Read-only configuration removes writing API functions, f_write(), >>> f_sync(), >>> +/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), >>> f_getfree() >>> +/ and optional writing functions as well. */ >>> + >>> + >>> +#define FF_FS_MINIMIZE 3 >>> +/* This option defines minimization level to remove some basic API >>> functions. >>> +/ >>> +/ 0: Basic functions are fully enabled. >>> +/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() >>> and f_rename() >>> +/ are removed. >>> +/ 2: f_opendir(), f_readdir() and f_closedir() are removed in >>> addition to 1. >>> +/ 3: f_lseek() function is removed in addition to 2. */ >>> + >>> + >>> +#define FF_USE_STRFUNC 0 >>> +/* This option switches string functions, f_gets(), f_putc(), >>> f_puts() and f_printf(). >>> +/ >>> +/ 0: Disable string functions. >>> +/ 1: Enable without LF-CRLF conversion. >>> +/ 2: Enable with LF-CRLF conversion. */ >>> + >>> + >>> +#define FF_USE_FIND 0 >>> +/* This option switches filtered directory read functions, >>> f_findfirst() and >>> +/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching >>> altname[] too) */ >>> + >>> + >>> +#define FF_USE_MKFS 1 >>> +/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ >>> + >>> + >>> +#define FF_USE_FASTSEEK 0 >>> +/* This option switches fast seek function. (0:Disable or 1:Enable) */ >>> + >>> + >>> +#define FF_USE_EXPAND 0 >>> +/* This option switches f_expand function. (0:Disable or 1:Enable) */ >>> + >>> + >>> +#define FF_USE_CHMOD 0 >>> +/* This option switches attribute manipulation functions, f_chmod() >>> and f_utime(). >>> +/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to >>> enable this option. */ >>> + >>> + >>> +#define FF_USE_LABEL 0 >>> +/* This option switches volume label functions, f_getlabel() and >>> f_setlabel(). >>> +/ (0:Disable or 1:Enable) */ >>> + >>> + >>> +#define FF_USE_FORWARD 0 >>> +/* This option switches f_forward() function. (0:Disable or >>> 1:Enable) */ >>> + >>> + >>> +/*---------------------------------------------------------------------------/ >>> >>> +/ Locale and Namespace Configurations >>> +/---------------------------------------------------------------------------*/ >>> >>> + >>> +#define FF_CODE_PAGE 932 >>> +/* This option specifies the OEM code page to be used on the target >>> system. >>> +/ Incorrect code page setting can cause a file open failure. >>> +/ >>> +/ 437 - U.S. >>> +/ 720 - Arabic >>> +/ 737 - Greek >>> +/ 771 - KBL >>> +/ 775 - Baltic >>> +/ 850 - Latin 1 >>> +/ 852 - Latin 2 >>> +/ 855 - Cyrillic >>> +/ 857 - Turkish >>> +/ 860 - Portuguese >>> +/ 861 - Icelandic >>> +/ 862 - Hebrew >>> +/ 863 - Canadian French >>> +/ 864 - Arabic >>> +/ 865 - Nordic >>> +/ 866 - Russian >>> +/ 869 - Greek 2 >>> +/ 932 - Japanese (DBCS) >>> +/ 936 - Simplified Chinese (DBCS) >>> +/ 949 - Korean (DBCS) >>> +/ 950 - Traditional Chinese (DBCS) >>> +/ 0 - Include all code pages above and configured by f_setcp() >>> +*/ >>> + >>> + >>> +#define FF_USE_LFN 0 >>> +#define FF_MAX_LFN 255 >>> +/* The FF_USE_LFN switches the support for LFN (long file name). >>> +/ >>> +/ 0: Disable LFN. FF_MAX_LFN has no effect. >>> +/ 1: Enable LFN with static working buffer on the BSS. Always NOT >>> thread-safe. >>> +/ 2: Enable LFN with dynamic working buffer on the STACK. >>> +/ 3: Enable LFN with dynamic working buffer on the HEAP. >>> +/ >>> +/ To enable the LFN, ffunicode.c needs to be added to the project. >>> The LFN function >>> +/ requiers certain internal working buffer occupies (FF_MAX_LFN + >>> 1) * 2 bytes and >>> +/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled. >>> +/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code >>> unit and it can >>> +/ be in range of 12 to 255. It is recommended to be set it 255 to >>> fully support LFN >>> +/ specification. >>> +/ When use stack for the working buffer, take care on stack >>> overflow. When use heap >>> +/ memory for the working buffer, memory management functions, >>> ff_memalloc() and >>> +/ ff_memfree() exemplified in ffsystem.c, need to be added to the >>> project. */ >>> + >>> + >>> +#define FF_LFN_UNICODE 0 >>> +/* This option switches the character encoding on the API when LFN >>> is enabled. >>> +/ >>> +/ 0: ANSI/OEM in current CP (TCHAR = char) >>> +/ 1: Unicode in UTF-16 (TCHAR = WCHAR) >>> +/ 2: Unicode in UTF-8 (TCHAR = char) >>> +/ 3: Unicode in UTF-32 (TCHAR = DWORD) >>> +/ >>> +/ Also behavior of string I/O functions will be affected by this >>> option. >>> +/ When LFN is not enabled, this option has no effect. */ >>> + >>> + >>> +#define FF_LFN_BUF 255 >>> +#define FF_SFN_BUF 12 >>> +/* This set of options defines size of file name members in the >>> FILINFO structure >>> +/ which is used to read out directory items. These values should be >>> suffcient for >>> +/ the file names to read. The maximum possible length of the read >>> file name depends >>> +/ on character encoding. When LFN is not enabled, these options >>> have no effect. */ >>> + >>> + >>> +#define FF_STRF_ENCODE 3 >>> +/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, >>> f_gets(), >>> +/ f_putc(), f_puts and f_printf() convert the character encoding in >>> it. >>> +/ This option selects assumption of character encoding ON THE FILE >>> to be >>> +/ read/written via those functions. >>> +/ >>> +/ 0: ANSI/OEM in current CP >>> +/ 1: Unicode in UTF-16LE >>> +/ 2: Unicode in UTF-16BE >>> +/ 3: Unicode in UTF-8 >>> +*/ >>> + >>> + >>> +#define FF_FS_RPATH 0 >>> +/* This option configures support for relative path. >>> +/ >>> +/ 0: Disable relative path and remove related functions. >>> +/ 1: Enable relative path. f_chdir() and f_chdrive() are available. >>> +/ 2: f_getcwd() function is available in addition to 1. >>> +*/ >>> + >>> + >>> +/*---------------------------------------------------------------------------/ >>> >>> +/ Drive/Volume Configurations >>> +/---------------------------------------------------------------------------*/ >>> >>> + >>> +#define FF_VOLUMES 1 >>> +/* Number of volumes (logical drives) to be used. (1-10) */ >>> + >>> + >>> +#define FF_STR_VOLUME_ID 0 >>> +#define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" >>> +/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary >>> strings. >>> +/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be >>> used as drive >>> +/ number in the path name. FF_VOLUME_STRS defines the volume ID >>> strings for each >>> +/ logical drives. Number of items must not be less than FF_VOLUMES. >>> Valid >>> +/ characters for the volume ID strings are A-Z, a-z and 0-9, >>> however, they are >>> +/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and >>> FF_VOLUME_STRS is >>> +/ not defined, a user defined volume string table needs to be >>> defined as: >>> +/ >>> +/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",... >>> +*/ >>> + >>> + >>> +#define FF_MULTI_PARTITION 0 >>> +/* This option switches support for multiple volumes on the physical >>> drive. >>> +/ By default (0), each logical drive number is bound to the same >>> physical drive >>> +/ number and only an FAT volume found on the physical drive will be >>> mounted. >>> +/ When this function is enabled (1), each logical drive number can >>> be bound to >>> +/ arbitrary physical drive and partition listed in the VolToPart[]. >>> Also f_fdisk() >>> +/ funciton will be available. */ >>> + >>> + >>> +#define FF_MIN_SS 512 >>> +#define FF_MAX_SS 512 >>> +/* This set of options configures the range of sector size to be >>> supported. (512, >>> +/ 1024, 2048 or 4096) Always set both 512 for most systems, generic >>> memory card and >>> +/ harddisk. But a larger value may be required for on-board flash >>> memory and some >>> +/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, >>> FatFs is configured >>> +/ for variable sector size mode and disk_ioctl() function needs to >>> implement >>> +/ GET_SECTOR_SIZE command. */ >>> + >>> + >>> +#define FF_LBA64 0 >>> +/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable) >>> +/ To enable the 64-bit LBA, also exFAT needs to be enabled. >>> (FF_FS_EXFAT == 1) */ >>> + >>> + >>> +#define FF_MIN_GPT 0x10000000 >>> +/* Minimum number of sectors to switch GPT as partitioning format in >>> f_mkfs and >>> +/ f_fdisk function. 0x100000000 max. This option has no effect when >>> FF_LBA64 == 0. */ >>> + >>> + >>> +#define FF_USE_TRIM 0 >>> +/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable) >>> +/ To enable Trim function, also CTRL_TRIM command should be >>> implemented to the >>> +/ disk_ioctl() function. */ >>> + >>> + >>> + >>> +/*---------------------------------------------------------------------------/ >>> >>> +/ System Configurations >>> +/---------------------------------------------------------------------------*/ >>> >>> + >>> +#define FF_FS_TINY 0 >>> +/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) >>> +/ At the tiny configuration, size of file object (FIL) is shrinked >>> FF_MAX_SS bytes. >>> +/ Instead of private sector buffer eliminated from the file object, >>> common sector >>> +/ buffer in the filesystem object (FATFS) is used for the file data >>> transfer. */ >>> + >>> + >>> +#define FF_FS_EXFAT 0 >>> +/* This option switches support for exFAT filesystem. (0:Disable or >>> 1:Enable) >>> +/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1) >>> +/ Note that enabling exFAT discards ANSI C (C89) compatibility. */ >>> + >>> + >>> +#define FF_FS_NORTC 0 >>> +#define FF_NORTC_MON 1 >>> +#define FF_NORTC_MDAY 1 >>> +#define FF_NORTC_YEAR 2020 >>> +/* The option FF_FS_NORTC switches timestamp functiton. If the >>> system does not have >>> +/ any RTC function or valid timestamp is not needed, set >>> FF_FS_NORTC = 1 to disable >>> +/ the timestamp function. Every object modified by FatFs will have >>> a fixed timestamp >>> +/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local >>> time. >>> +/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() >>> function need to be >>> +/ added to the project to read current time form real-time clock. >>> FF_NORTC_MON, >>> +/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect. >>> +/ These options have no effect in read-only configuration >>> (FF_FS_READONLY = 1). */ >>> + >>> + >>> +#define FF_FS_NOFSINFO 0 >>> +/* If you need to know correct free space on the FAT32 volume, set >>> bit 0 of this >>> +/ option, and f_getfree() function at first time after volume mount >>> will force >>> +/ a full FAT scan. Bit 1 controls the use of last allocated cluster >>> number. >>> +/ >>> +/ bit0=0: Use free cluster count in the FSINFO if available. >>> +/ bit0=1: Do not trust free cluster count in the FSINFO. >>> +/ bit1=0: Use last allocated cluster number in the FSINFO if >>> available. >>> +/ bit1=1: Do not trust last allocated cluster number in the FSINFO. >>> +*/ >>> + >>> + >>> +#define FF_FS_LOCK 0 >>> +/* The option FF_FS_LOCK switches file lock function to control >>> duplicated file open >>> +/ and illegal operation to open objects. This option must be 0 when >>> FF_FS_READONLY >>> +/ is 1. >>> +/ >>> +/ 0: Disable file lock function. To avoid volume corruption, >>> application program >>> +/ should avoid illegal open, remove and rename to the open >>> objects. >>> +/ >0: Enable file lock function. The value defines how many >>> files/sub-directories >>> +/ can be opened simultaneously under file lock control. Note >>> that the file >>> +/ lock control is independent of re-entrancy. */ >>> + >>> + >>> +/* #include <somertos.h> // O/S definitions */ >>> +#define FF_FS_REENTRANT 0 >>> +#define FF_FS_TIMEOUT 1000 >>> +#define FF_SYNC_t HANDLE >>> +/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) >>> of the FatFs >>> +/ module itself. Note that regardless of this option, file access >>> to different >>> +/ volume is always re-entrant and volume control functions, >>> f_mount(), f_mkfs() >>> +/ and f_fdisk() function, are always not re-entrant. Only >>> file/directory access >>> +/ to the same volume is under control of this function. >>> +/ >>> +/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect. >>> +/ 1: Enable re-entrancy. Also user provided synchronization handlers, >>> +/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and >>> ff_cre_syncobj() >>> +/ function, must be added to the project. Samples are available in >>> +/ option/syscall.c. >>> +/ >>> +/ The FF_FS_TIMEOUT defines timeout period in unit of time tick. >>> +/ The FF_SYNC_t defines O/S dependent sync object type. e.g. >>> HANDLE, ID, OS_EVENT*, >>> +/ SemaphoreHandle_t and etc. A header file for O/S definitions >>> needs to be >>> +/ included somewhere in the scope of ff.h. */ >>> + >>> + >>> + >>> +/*--- End of configuration options ---*/ >>> >> >> > >
On 12.04.21 16:17, Roland Gaudig (OSS) wrote: > Hello Stefano, > > when looking at the code I get the impression, that Coverity Scan might > report a false positive, explanation below. > > On 26.03.21 21:53, Stefano Babic wrote: >> Hi Roland, >> >> On 26.03.21 15:18, Stefano Babic wrote: >>> Hi Roland, >>> >>> I have no specific comments to your series. I merged the series and I >>> ran the coverity scan. I agree that this is imported code, and it >>> does not matter if coding style differs from SWUpdate's. However, >>> coverity discovers some topics in the code that I think they should >>> fixes (with an additional patch). To be short: malloc() is called, no >>> check if the pointer is Null. Can you check it ? >>> >> >> I was too fast to report the coverity's report : most of them are >> unrelated to this series. There is just: >> >>> Here the results: >>> >>> Hi, >>> >>> Please find the latest report on new defect(s) introduced to >>> sbabic/swupdate found with Coverity Scan. >>> >>> 6 new defect(s) introduced to sbabic/swupdate found with Coverity Scan. >>> >>> >>> New defect(s) Reported-by: Coverity Scan >>> Showing 6 of 6 defect(s) >>> >>> >>> ** CID 329979: Control flow issues (DEADCODE) >>> /fatfs/ff.c: 5817 in create_partition() >>> > > In the following is a code snipped from the complained code: > > sz_drv32 is the disk size in number of sectors. > n_hd is the number of heads with should not become bigger than 255. > > 5815: n_sc = N_SEC_TRACK; /* N_SEC_TRACK = 63 */ > 5816: for (n_hd = 8; > n_hd != 0 && sz_drv32 / n_hd / n_sc > 1024; > n_hd *= 2) ; > 5817: if (n_hd == 0) n_hd = 255; > > Assuming a sector size of 512, any disk size greater than about 4 GiB > will result in n_hd being 0 after finishing the loop in line 5816. > In such case the condition in line 5817 will become true for any disk > bigger than 4 GiB. Right - coverity is confused because it does not probably consider that n_hd is a byte, and then it overflows. But it is, of coursse, wrong. It is a false positive. > > Line 5817 being dead code would require, that sz_drv32 is always less > than about 0x007f_ffff. sz_drv32 is set by the disk_ioctl function with > the GET_SECTOR_COUNT command. All variables involved are at least 32 > bits wide. Apart n_hd Best regards, Stefano Babic > >> >> But this can be fixed later in a follow up code. I merge the series >> into master. >> >> Best regards, >> Stefano Babic >> >>> >>> ________________________________________________________________________________________________________ >>> >>> *** CID 329979: Control flow issues (DEADCODE) >>> /fatfs/ff.c: 5817 in create_partition() >>> 5811 } else >>> 5812 #endif >>> 5813 { /* Create partitions in MBR */ >>> 5814 sz_drv32 = (DWORD)sz_drv; >>> 5815 n_sc = N_SEC_TRACK; /* Determine drive CHS >>> without any consideration of the drive geometry */ >>> 5816 for (n_hd = 8; n_hd != 0 && sz_drv32 / n_hd / n_sc > >>> 1024; n_hd *= 2) ; >>> >>> CID 329979: Control flow issues (DEADCODE) >>> >>> Execution cannot reach this statement: "n_hd = 255;". >>> 5817 if (n_hd == 0) n_hd = 255; /* Number of heads >>> needs to be <256 */ >>> 5818 >>> 5819 mem_set(buf, 0, FF_MAX_SS); /* Clear MBR */ >>> 5820 pte = buf + MBR_Table; /* Partition table in the >>> MBR */ >>> 5821 for (i = 0, s_lba32 = n_sc; i < 4 && s_lba32 != 0 && >>> s_lba32 < sz_drv32; i++, s_lba32 += n_lba32) { >>> 5822 n_lba32 = (DWORD)plst[i]; /* Get partition >>> size */ >>> >>> ** CID 314959: Null pointer dereferences (NULL_RETURNS) >>> /mongoose/mongoose.c: 8474 in mg_file_upload_handler() >>> >>> >>> ________________________________________________________________________________________________________ >>> >>> *** CID 314959: Null pointer dereferences (NULL_RETURNS) >>> /mongoose/mongoose.c: 8474 in mg_file_upload_handler() >>> 8468 fus = (struct file_upload_state *) MG_CALLOC(1, >>> sizeof(*fus)); >>> 8469 if (fus == NULL) { >>> 8470 nc->flags |= MG_F_CLOSE_IMMEDIATELY; >>> 8471 return; >>> 8472 } >>> 8473 fus->lfn = (char *) MG_MALLOC(lfn.len + 1); >>> >>> CID 314959: Null pointer dereferences (NULL_RETURNS) >>> >>> Dereferencing a pointer that might be "NULL" "fus->lfn" when >>> calling "memcpy". >>> 8474 memcpy(fus->lfn, lfn.p, lfn.len); >>> 8475 fus->lfn[lfn.len] = '\0'; >>> 8476 if (lfn.p != mp->file_name) MG_FREE((char *) lfn.p); >>> 8477 LOG(LL_DEBUG, >>> 8478 ("%p Receiving file %s -> %s", nc, mp->file_name, >>> fus->lfn)); >>> 8479 fus->fp = mg_fopen(fus->lfn, "wb"); >>> >>> ** CID 314957: Null pointer dereferences (NULL_RETURNS) >>> /mongoose/mongoose.c: 2452 in mg_mgr_init_opt() >>> >>> >>> ________________________________________________________________________________________________________ >>> >>> *** CID 314957: Null pointer dereferences (NULL_RETURNS) >>> /mongoose/mongoose.c: 2452 in mg_mgr_init_opt() >>> 2446 opts.ifaces[MG_MAIN_IFACE] = opts.main_iface; >>> 2447 } >>> 2448 m->num_ifaces = opts.num_ifaces; >>> 2449 m->ifaces = >>> 2450 (struct mg_iface **) MG_MALLOC(sizeof(*m->ifaces) * >>> opts.num_ifaces); >>> 2451 for (i = 0; i < opts.num_ifaces; i++) { >>> >>> CID 314957: Null pointer dereferences (NULL_RETURNS) >>> >>> Dereferencing "m->ifaces", which is known to be "NULL". >>> 2452 m->ifaces[i] = mg_if_create_iface(opts.ifaces[i], m); >>> 2453 m->ifaces[i]->vtable->init(m->ifaces[i]); >>> 2454 } >>> 2455 } >>> 2456 if (opts.nameserver != NULL) { >>> 2457 m->nameserver = strdup(opts.nameserver); >>> >>> ** CID 314956: Null pointer dereferences (NULL_RETURNS) >>> >>> >>> ________________________________________________________________________________________________________ >>> >>> *** CID 314956: Null pointer dereferences (NULL_RETURNS) >>> /mongoose/mongoose.c: 12007 in mg_resolve_async_eh() >>> 12001 req->last_time = now; >>> 12002 req->retries++; >>> 12003 } >>> 12004 break; >>> 12005 case MG_EV_RECV: >>> 12006 msg = (struct mg_dns_message *) MG_MALLOC(sizeof(*msg)); >>> >>> CID 314956: Null pointer dereferences (NULL_RETURNS) >>> >>> Dereferencing a pointer that might be "NULL" "msg" when >>> calling "mg_parse_dns". >>> 12007 if (mg_parse_dns(nc->recv_mbuf.buf, *(int *) data, >>> msg) == 0 && >>> 12008 msg->num_answers > 0) { >>> 12009 req->callback(msg, req->data, MG_RESOLVE_OK); >>> 12010 nc->user_data = NULL; >>> 12011 MG_FREE(req); >>> 12012 } else { >>> >>> ** CID 314955: Null pointer dereferences (NULL_RETURNS) >>> /suricatta/server_hawkbit.c: 1485 in server_send_target_data() >>> >>> >>> ________________________________________________________________________________________________________ >>> >>> *** CID 314955: Null pointer dereferences (NULL_RETURNS) >>> /suricatta/server_hawkbit.c: 1485 in server_send_target_data() >>> 1479 if (!len) { >>> 1480 server_hawkbit.has_to_send_configData = false; >>> 1481 return SERVER_OK; >>> 1482 } >>> 1483 >>> 1484 char *configData = (char *)(malloc(len + 16)); >>> >>> CID 314955: Null pointer dereferences (NULL_RETURNS) >>> >>> Dereferencing a pointer that might be "NULL" "configData" >>> when calling "memset". >>> 1485 memset(configData, 0, len + 16); >>> 1486 >>> 1487 static const char* const config_data = STRINGIFY( >>> 1488 %c"%s": "%s" >>> 1489 ); >>> 1490 >>> >>> ** CID 314954: Null pointer dereferences (NULL_RETURNS) >>> >>> >>> ________________________________________________________________________________________________________ >>> >>> *** CID 314954: Null pointer dereferences (NULL_RETURNS) >>> /mongoose/mongoose.c: 7464 in mg_parse_http_basic_auth() >>> 7458 char fmt[64]; >>> 7459 int res = 0; >>> 7460 >>> 7461 if (mg_strncmp(*hdr, mg_mk_str("Basic "), 6) != 0) return -1; >>> 7462 >>> 7463 buf = (char *) MG_MALLOC(hdr->len); >>> >>> CID 314954: Null pointer dereferences (NULL_RETURNS) >>> >>> Dereferencing a pointer that might be "NULL" "buf" when >>> calling "cs_base64_decode". >>> 7464 cs_base64_decode((unsigned char *) hdr->p + 6, hdr->len, >>> buf, NULL); >>> 7465 >>> 7466 /* e.g. "%123[^:]:%321[^\n]" */ >>> 7467 snprintf(fmt, sizeof(fmt), "%%%" SIZE_T_FMT "[^:]:%%%" >>> SIZE_T_FMT "[^\n]", >>> 7468 user_len - 1, pass_len - 1); >>> 7469 if (sscanf(buf, fmt, user, pass) == 0) { >>> >>> Best regards, >>> Stefano Babic >>> >>> On 25.03.21 16:46, roland.gaudig-oss@weidmueller.com wrote: >>>> From: Roland Gaudig <roland.gaudig@weidmueller.com> >>>> >>>> FatFs is a FAT filesystem implementation for small embedded systems >>>> written >>>> by ChaN. For more information see: >>>> http://elm-chan.org/fsw/ff/00index_e.html >>>> >>>> Signed-off-by: Roland Gaudig <roland.gaudig@weidmueller.com> >>>> --- >>>> fatfs/diskio.c | 174 ++ >>>> fatfs/diskio.h | 96 + >>>> fatfs/ff.c | 6875 >>>> ++++++++++++++++++++++++++++++++++++++++++++++++ >>>> fatfs/ff.h | 425 +++ >>>> fatfs/ffconf.h | 317 +++ >>>> 5 files changed, 7887 insertions(+) >>>> create mode 100644 fatfs/diskio.c >>>> create mode 100644 fatfs/diskio.h >>>> create mode 100644 fatfs/ff.c >>>> create mode 100644 fatfs/ff.h >>>> create mode 100644 fatfs/ffconf.h >>>> >>>> diff --git a/fatfs/diskio.c b/fatfs/diskio.c >>>> new file mode 100644 >>>> index 0000000..1976add >>>> --- /dev/null >>>> +++ b/fatfs/diskio.c >>>> @@ -0,0 +1,174 @@ >>>> +/* >>>> + * Copyright (C) 2021 Weidmueller Interface GmbH & Co. KG >>>> + * Roland Gaudig <roland.gaudig@weidmueller.com> >>>> + * >>>> + * SPDX-License-Identifier: GPL-2.0-or-later >>>> + */ >>>> + >>>> +#include <errno.h> >>>> +#include <fcntl.h> >>>> +#include <time.h> >>>> +#include <unistd.h> >>>> +#include <linux/fs.h> >>>> +#include <sys/ioctl.h> >>>> + >>>> +#include "swupdate.h" >>>> +#include "util.h" >>>> + >>>> +#include "ff.h" >>>> +#include "diskio.h" >>>> + >>>> + >>>> +#define SECTOR_SIZE 512 >>>> + >>>> +static int file_descriptor; >>>> +static char device_name[MAX_VOLNAME]; >>>> +static bool init_status; >>>> + >>>> +int fatfs_init(char *device); >>>> +void fatfs_release(void); >>>> + >>>> + >>>> +int fatfs_init(char *device) >>>> +{ >>>> + if (strnlen(device_name, MAX_VOLNAME)) { >>>> + ERROR("Called fatfs_init second time without fatfs_release"); >>>> + return -1; >>>> + } >>>> + >>>> + strncpy(device_name, device, sizeof(device_name)); >>>> + file_descriptor = open(device_name, O_RDWR); >>>> + >>>> + if (file_descriptor < 0) { >>>> + ERROR("Device %s cannot be opened: %s", device_name, >>>> strerror(errno)); >>>> + return -ENODEV; >>>> + } >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +void fatfs_release(void) >>>> +{ >>>> + (void)close(file_descriptor); >>>> + memset(device_name, 0, MAX_VOLNAME); >>>> + init_status = false; >>>> +} >>>> + >>>> +DSTATUS disk_status(BYTE pdrv) >>>> +{ >>>> + DSTATUS status = 0; >>>> + (void)pdrv; >>>> + >>>> + if (!strnlen(device_name, MAX_VOLNAME)) >>>> + status |= STA_NODISK; >>>> + >>>> + if (!init_status) >>>> + status |= STA_NOINIT; >>>> + >>>> + return status; >>>> +} >>>> + >>>> +DSTATUS disk_initialize(BYTE pdrv) >>>> +{ >>>> + init_status = true; >>>> + >>>> + return disk_status(pdrv); >>>> +} >>>> + >>>> +DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) >>>> +{ >>>> + (void)pdrv; >>>> + >>>> + if (!buff) >>>> + return RES_PARERR; >>>> + >>>> + if (disk_status(pdrv)) >>>> + return RES_NOTRDY; >>>> + >>>> + if (pread(file_descriptor, buff, count * SECTOR_SIZE, sector * >>>> SECTOR_SIZE) != count * SECTOR_SIZE) >>>> + return RES_ERROR; >>>> + >>>> + return RES_OK; >>>> +} >>>> + >>>> +#if FF_FS_READONLY == 0 >>>> +DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT >>>> count) >>>> +{ >>>> + (void)pdrv; >>>> + >>>> + if (!buff) >>>> + return RES_PARERR; >>>> + >>>> + if (disk_status(pdrv)) >>>> + return RES_NOTRDY; >>>> + >>>> + if (pwrite(file_descriptor, buff, count * SECTOR_SIZE, sector * >>>> SECTOR_SIZE) != count * SECTOR_SIZE) >>>> + return RES_ERROR; >>>> + >>>> + return RES_OK; >>>> +} >>>> +#endif >>>> + >>>> +DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) >>>> +{ >>>> + (void) pdrv; >>>> + >>>> + if (disk_status(pdrv)) >>>> + return RES_NOTRDY; >>>> + >>>> + switch (cmd) { >>>> + case CTRL_SYNC: >>>> + if (syncfs(file_descriptor) != 0) >>>> + return RES_ERROR; >>>> + break; >>>> + case GET_SECTOR_COUNT: >>>> + { >>>> + off_t size = lseek(file_descriptor, 0, SEEK_END) / >>>> SECTOR_SIZE; >>>> + >>>> + if (!buff) >>>> + return RES_PARERR; >>>> + >>>> + *(LBA_t*)buff = size; >>>> + break; >>>> + } >>>> + case GET_SECTOR_SIZE: >>>> + if (!buff) >>>> + return RES_PARERR; >>>> + >>>> + *(WORD*)buff = SECTOR_SIZE; >>>> + break; >>>> + case GET_BLOCK_SIZE: >>>> + /* Get erase block size of flash memories, return 1 if not a >>>> + * Flash memory or unknown. >>>> + */ >>>> + if (!buff) >>>> + return RES_PARERR; >>>> + >>>> + *(WORD*)buff = 1; >>>> + break; >>>> + default: >>>> + ERROR("cmd %d not implemented", cmd); >>>> + return RES_PARERR; >>>> + break; >>>> + } >>>> + >>>> + return RES_OK; >>>> +} >>>> + >>>> +DWORD get_fattime(void) >>>> +{ >>>> + time_t unix_time = time(NULL); >>>> + struct tm *t = gmtime(&unix_time); >>>> + >>>> + /* FatFs times are based on year 1980 */ >>>> + DWORD tdos = ((t->tm_year - 80) & 0x3F) << 25; >>>> + /* FatFs months start with 1 */ >>>> + tdos |= (t->tm_mon + 1) << 21; >>>> + tdos |= t->tm_mday << 16; >>>> + tdos |= t->tm_hour << 11; >>>> + tdos |= t->tm_min << 5; >>>> + /* Don't know how FatFs copes with leap seconds, therefore >>>> limit them */ >>>> + tdos |= (t->tm_sec % 60) / 2; >>>> + >>>> + return tdos; >>>> +} >>>> diff --git a/fatfs/diskio.h b/fatfs/diskio.h >>>> new file mode 100644 >>>> index 0000000..1f019a1 >>>> --- /dev/null >>>> +++ b/fatfs/diskio.h >>>> @@ -0,0 +1,96 @@ >>>> +/*-----------------------------------------------------------------------/ >>>> >>>> +/ Low level disk interface modlue include file (C)ChaN, >>>> 2019 / >>>> +/------------------------------------------------------------------------/ >>>> >>>> +/ >>>> +/ Copyright (C) 2019, ChaN, all right reserved. >>>> +/ >>>> +/ FatFs module is an open source software. Redistribution and use >>>> of FatFs in >>>> +/ source and binary forms, with or without modification, are >>>> permitted provided >>>> +/ that the following condition is met: >>>> +/ >>>> +/ 1. Redistributions of source code must retain the above copyright >>>> notice, >>>> +/ this condition and the following disclaimer. >>>> +/ >>>> +/ This software is provided by the copyright holder and >>>> contributors "AS IS" >>>> +/ and any warranties related to this software are DISCLAIMED. >>>> +/ The copyright owner or contributors be NOT LIABLE for any damages >>>> caused >>>> +/ by use of this software. >>>> +/ >>>> +/ SPDX-License-Identifier: BSD-1-Clause >>>> +/ >>>> +/-----------------------------------------------------------------------*/ >>>> >>>> + >>>> + >>>> +#ifndef _DISKIO_DEFINED >>>> +#define _DISKIO_DEFINED >>>> + >>>> +#ifdef __cplusplus >>>> +extern "C" { >>>> +#endif >>>> + >>>> +/* Status of Disk Functions */ >>>> +typedef BYTE DSTATUS; >>>> + >>>> +/* Results of Disk Functions */ >>>> +typedef enum { >>>> + RES_OK = 0, /* 0: Successful */ >>>> + RES_ERROR, /* 1: R/W Error */ >>>> + RES_WRPRT, /* 2: Write Protected */ >>>> + RES_NOTRDY, /* 3: Not Ready */ >>>> + RES_PARERR /* 4: Invalid Parameter */ >>>> +} DRESULT; >>>> + >>>> + >>>> +/*---------------------------------------*/ >>>> +/* Prototypes for disk control functions */ >>>> + >>>> + >>>> +DSTATUS disk_initialize (BYTE pdrv); >>>> +DSTATUS disk_status (BYTE pdrv); >>>> +DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count); >>>> +DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT >>>> count); >>>> +DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); >>>> + >>>> + >>>> +/* Disk Status Bits (DSTATUS) */ >>>> + >>>> +#define STA_NOINIT 0x01 /* Drive not initialized */ >>>> +#define STA_NODISK 0x02 /* No medium in the drive */ >>>> +#define STA_PROTECT 0x04 /* Write protected */ >>>> + >>>> + >>>> +/* Command code for disk_ioctrl fucntion */ >>>> + >>>> +/* Generic command (Used by FatFs) */ >>>> +#define CTRL_SYNC 0 /* Complete pending write process >>>> (needed at FF_FS_READONLY == 0) */ >>>> +#define GET_SECTOR_COUNT 1 /* Get media size (needed at >>>> FF_USE_MKFS == 1) */ >>>> +#define GET_SECTOR_SIZE 2 /* Get sector size (needed at >>>> FF_MAX_SS != FF_MIN_SS) */ >>>> +#define GET_BLOCK_SIZE 3 /* Get erase block size (needed >>>> at FF_USE_MKFS == 1) */ >>>> +#define CTRL_TRIM 4 /* Inform device that the data on >>>> the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */ >>>> + >>>> +/* Generic command (Not used by FatFs) */ >>>> +#define CTRL_POWER 5 /* Get/Set power status */ >>>> +#define CTRL_LOCK 6 /* Lock/Unlock media removal */ >>>> +#define CTRL_EJECT 7 /* Eject media */ >>>> +#define CTRL_FORMAT 8 /* Create physical format on >>>> the media */ >>>> + >>>> +/* MMC/SDC specific ioctl command */ >>>> +#define MMC_GET_TYPE 10 /* Get card type */ >>>> +#define MMC_GET_CSD 11 /* Get CSD */ >>>> +#define MMC_GET_CID 12 /* Get CID */ >>>> +#define MMC_GET_OCR 13 /* Get OCR */ >>>> +#define MMC_GET_SDSTAT 14 /* Get SD status */ >>>> +#define ISDIO_READ 55 /* Read data form SD iSDIO >>>> register */ >>>> +#define ISDIO_WRITE 56 /* Write data to SD iSDIO >>>> register */ >>>> +#define ISDIO_MRITE 57 /* Masked write data to SD >>>> iSDIO register */ >>>> + >>>> +/* ATA/CF specific ioctl command */ >>>> +#define ATA_GET_REV 20 /* Get F/W revision */ >>>> +#define ATA_GET_MODEL 21 /* Get model name */ >>>> +#define ATA_GET_SN 22 /* Get serial number */ >>>> + >>>> +#ifdef __cplusplus >>>> +} >>>> +#endif >>>> + >>>> +#endif >>>> diff --git a/fatfs/ff.c b/fatfs/ff.c >>>> new file mode 100644 >>>> index 0000000..9281ec8 >>>> --- /dev/null >>>> +++ b/fatfs/ff.c >>>> @@ -0,0 +1,6875 @@ >>>> +/*----------------------------------------------------------------------------/ >>>> >>>> +/ FatFs - Generic FAT Filesystem Module >>>> R0.14a / >>>> +/-----------------------------------------------------------------------------/ >>>> >>>> +/ >>>> +/ Copyright (C) 2020, ChaN, all right reserved. >>>> +/ >>>> +/ FatFs module is an open source software. Redistribution and use >>>> of FatFs in >>>> +/ source and binary forms, with or without modification, are >>>> permitted provided >>>> +/ that the following condition is met: >>>> +/ >>>> +/ 1. Redistributions of source code must retain the above copyright >>>> notice, >>>> +/ this condition and the following disclaimer. >>>> +/ >>>> +/ This software is provided by the copyright holder and >>>> contributors "AS IS" >>>> +/ and any warranties related to this software are DISCLAIMED. >>>> +/ The copyright owner or contributors be NOT LIABLE for any damages >>>> caused >>>> +/ by use of this software. >>>> +/ >>>> +/ SPDX-License-Identifier: BSD-1-Clause >>>> +/ >>>> +/----------------------------------------------------------------------------*/ >>>> >>>> + >>>> + >>>> +#include "ff.h" /* Declarations of FatFs API */ >>>> +#include "diskio.h" /* Declarations of device I/O functions */ >>>> + >>>> + >>>> +/*-------------------------------------------------------------------------- >>>> >>>> + >>>> + Module Private Definitions >>>> + >>>> +---------------------------------------------------------------------------*/ >>>> >>>> + >>>> +#if FF_DEFINED != 80196 /* Revision ID */ >>>> +#error Wrong include file (ff.h). >>>> +#endif >>>> + >>>> + >>>> +/* Limits and boundaries */ >>>> +#define MAX_DIR 0x200000 /* Max size of FAT directory */ >>>> +#define MAX_DIR_EX 0x10000000 /* Max size of exFAT >>>> directory */ >>>> +#define MAX_FAT12 0xFF5 /* Max FAT12 clusters >>>> (differs from specs, but right for real DOS/Windows behavior) */ >>>> +#define MAX_FAT16 0xFFF5 /* Max FAT16 clusters >>>> (differs from specs, but right for real DOS/Windows behavior) */ >>>> +#define MAX_FAT32 0x0FFFFFF5 /* Max FAT32 clusters (not >>>> specified, practical limit) */ >>>> +#define MAX_EXFAT 0x7FFFFFFD /* Max exFAT clusters >>>> (differs from specs, implementation limit) */ >>>> + >>>> + >>>> +/* Character code support macros */ >>>> +#define IsUpper(c) ((c) >= 'A' && (c) <= 'Z') >>>> +#define IsLower(c) ((c) >= 'a' && (c) <= 'z') >>>> +#define IsDigit(c) ((c) >= '0' && (c) <= '9') >>>> +#define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF) >>>> +#define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF) >>>> +#define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF) >>>> + >>>> + >>>> +/* Additional file access control and file status flags for >>>> internal use */ >>>> +#define FA_SEEKEND 0x20 /* Seek to end of the file on file >>>> open */ >>>> +#define FA_MODIFIED 0x40 /* File has been modified */ >>>> +#define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */ >>>> + >>>> + >>>> +/* Additional file attribute bits for internal use */ >>>> +#define AM_VOL 0x08 /* Volume label */ >>>> +#define AM_LFN 0x0F /* LFN entry */ >>>> +#define AM_MASK 0x3F /* Mask of defined bits */ >>>> + >>>> + >>>> +/* Name status flags in fn[11] */ >>>> +#define NSFLAG 11 /* Index of the name status byte */ >>>> +#define NS_LOSS 0x01 /* Out of 8.3 format */ >>>> +#define NS_LFN 0x02 /* Force to create LFN entry */ >>>> +#define NS_LAST 0x04 /* Last segment */ >>>> +#define NS_BODY 0x08 /* Lower case flag (body) */ >>>> +#define NS_EXT 0x10 /* Lower case flag (ext) */ >>>> +#define NS_DOT 0x20 /* Dot entry */ >>>> +#define NS_NOLFN 0x40 /* Do not find LFN */ >>>> +#define NS_NONAME 0x80 /* Not followed */ >>>> + >>>> + >>>> +/* exFAT directory entry types */ >>>> +#define ET_BITMAP 0x81 /* Allocation bitmap */ >>>> +#define ET_UPCASE 0x82 /* Up-case table */ >>>> +#define ET_VLABEL 0x83 /* Volume label */ >>>> +#define ET_FILEDIR 0x85 /* File and directory */ >>>> +#define ET_STREAM 0xC0 /* Stream extension */ >>>> +#define ET_FILENAME 0xC1 /* Name extension */ >>>> + >>>> + >>>> +/* FatFs refers the FAT structure as simple byte array instead of >>>> structure member >>>> +/ because the C structure is not binary compatible between >>>> different platforms */ >>>> + >>>> +#define BS_JmpBoot 0 /* x86 jump instruction >>>> (3-byte) */ >>>> +#define BS_OEMName 3 /* OEM name (8-byte) */ >>>> +#define BPB_BytsPerSec 11 /* Sector size [byte] >>>> (WORD) */ >>>> +#define BPB_SecPerClus 13 /* Cluster size [sector] >>>> (BYTE) */ >>>> +#define BPB_RsvdSecCnt 14 /* Size of reserved area >>>> [sector] (WORD) */ >>>> +#define BPB_NumFATs 16 /* Number of FATs (BYTE) */ >>>> +#define BPB_RootEntCnt 17 /* Size of root directory >>>> area for FAT [entry] (WORD) */ >>>> +#define BPB_TotSec16 19 /* Volume size (16-bit) >>>> [sector] (WORD) */ >>>> +#define BPB_Media 21 /* Media descriptor byte >>>> (BYTE) */ >>>> +#define BPB_FATSz16 22 /* FAT size (16-bit) >>>> [sector] (WORD) */ >>>> +#define BPB_SecPerTrk 24 /* Number of sectors per >>>> track for int13h [sector] (WORD) */ >>>> +#define BPB_NumHeads 26 /* Number of heads for int13h >>>> (WORD) */ >>>> +#define BPB_HiddSec 28 /* Volume offset from top >>>> of the drive (DWORD) */ >>>> +#define BPB_TotSec32 32 /* Volume size (32-bit) >>>> [sector] (DWORD) */ >>>> +#define BS_DrvNum 36 /* Physical drive number for >>>> int13h (BYTE) */ >>>> +#define BS_NTres 37 /* WindowsNT error flag >>>> (BYTE) */ >>>> +#define BS_BootSig 38 /* Extended boot signature >>>> (BYTE) */ >>>> +#define BS_VolID 39 /* Volume serial number >>>> (DWORD) */ >>>> +#define BS_VolLab 43 /* Volume label string >>>> (8-byte) */ >>>> +#define BS_FilSysType 54 /* Filesystem type string >>>> (8-byte) */ >>>> +#define BS_BootCode 62 /* Boot code (448-byte) */ >>>> +#define BS_55AA 510 /* Signature word (WORD) */ >>>> + >>>> +#define BPB_FATSz32 36 /* FAT32: FAT size >>>> [sector] (DWORD) */ >>>> +#define BPB_ExtFlags32 40 /* FAT32: Extended flags >>>> (WORD) */ >>>> +#define BPB_FSVer32 42 /* FAT32: Filesystem >>>> version (WORD) */ >>>> +#define BPB_RootClus32 44 /* FAT32: Root directory >>>> cluster (DWORD) */ >>>> +#define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO >>>> sector (WORD) */ >>>> +#define BPB_BkBootSec32 50 /* FAT32: Offset of backup >>>> boot sector (WORD) */ >>>> +#define BS_DrvNum32 64 /* FAT32: Physical drive >>>> number for int13h (BYTE) */ >>>> +#define BS_NTres32 65 /* FAT32: Error flag (BYTE) */ >>>> +#define BS_BootSig32 66 /* FAT32: Extended boot >>>> signature (BYTE) */ >>>> +#define BS_VolID32 67 /* FAT32: Volume serial >>>> number (DWORD) */ >>>> +#define BS_VolLab32 71 /* FAT32: Volume label >>>> string (8-byte) */ >>>> +#define BS_FilSysType32 82 /* FAT32: Filesystem type >>>> string (8-byte) */ >>>> +#define BS_BootCode32 90 /* FAT32: Boot code >>>> (420-byte) */ >>>> + >>>> +#define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */ >>>> +#define BPB_VolOfsEx 64 /* exFAT: Volume offset from >>>> top of the drive [sector] (QWORD) */ >>>> +#define BPB_TotSecEx 72 /* exFAT: Volume size >>>> [sector] (QWORD) */ >>>> +#define BPB_FatOfsEx 80 /* exFAT: FAT offset from top >>>> of the volume [sector] (DWORD) */ >>>> +#define BPB_FatSzEx 84 /* exFAT: FAT size >>>> [sector] (DWORD) */ >>>> +#define BPB_DataOfsEx 88 /* exFAT: Data offset from >>>> top of the volume [sector] (DWORD) */ >>>> +#define BPB_NumClusEx 92 /* exFAT: Number of clusters >>>> (DWORD) */ >>>> +#define BPB_RootClusEx 96 /* exFAT: Root directory >>>> start cluster (DWORD) */ >>>> +#define BPB_VolIDEx 100 /* exFAT: Volume serial >>>> number (DWORD) */ >>>> +#define BPB_FSVerEx 104 /* exFAT: Filesystem >>>> version (WORD) */ >>>> +#define BPB_VolFlagEx 106 /* exFAT: Volume flags >>>> (WORD) */ >>>> +#define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector >>>> size in unit of byte (BYTE) */ >>>> +#define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster >>>> size in unit of sector (BYTE) */ >>>> +#define BPB_NumFATsEx 110 /* exFAT: Number of FATs >>>> (BYTE) */ >>>> +#define BPB_DrvNumEx 111 /* exFAT: Physical drive >>>> number for int13h (BYTE) */ >>>> +#define BPB_PercInUseEx 112 /* exFAT: Percent in use >>>> (BYTE) */ >>>> +#define BPB_RsvdEx 113 /* exFAT: Reserved >>>> (7-byte) */ >>>> +#define BS_BootCodeEx 120 /* exFAT: Boot code >>>> (390-byte) */ >>>> + >>>> +#define DIR_Name 0 /* Short file name (11-byte) */ >>>> +#define DIR_Attr 11 /* Attribute (BYTE) */ >>>> +#define DIR_NTres 12 /* Lower case flag (BYTE) */ >>>> +#define DIR_CrtTime10 13 /* Created time sub-second >>>> (BYTE) */ >>>> +#define DIR_CrtTime 14 /* Created time (DWORD) */ >>>> +#define DIR_LstAccDate 18 /* Last accessed date >>>> (WORD) */ >>>> +#define DIR_FstClusHI 20 /* Higher 16-bit of first >>>> cluster (WORD) */ >>>> +#define DIR_ModTime 22 /* Modified time (DWORD) */ >>>> +#define DIR_FstClusLO 26 /* Lower 16-bit of first >>>> cluster (WORD) */ >>>> +#define DIR_FileSize 28 /* File size (DWORD) */ >>>> +#define LDIR_Ord 0 /* LFN: LFN order and LLE flag >>>> (BYTE) */ >>>> +#define LDIR_Attr 11 /* LFN: LFN attribute (BYTE) */ >>>> +#define LDIR_Type 12 /* LFN: Entry type (BYTE) */ >>>> +#define LDIR_Chksum 13 /* LFN: Checksum of the >>>> SFN (BYTE) */ >>>> +#define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD) */ >>>> +#define XDIR_Type 0 /* exFAT: Type of exFAT >>>> directory entry (BYTE) */ >>>> +#define XDIR_NumLabel 1 /* exFAT: Number of volume >>>> label characters (BYTE) */ >>>> +#define XDIR_Label 2 /* exFAT: Volume label >>>> (11-WORD) */ >>>> +#define XDIR_CaseSum 4 /* exFAT: Sum of case >>>> conversion table (DWORD) */ >>>> +#define XDIR_NumSec 1 /* exFAT: Number of >>>> secondary entries (BYTE) */ >>>> +#define XDIR_SetSum 2 /* exFAT: Sum of the set of >>>> directory entries (WORD) */ >>>> +#define XDIR_Attr 4 /* exFAT: File attribute >>>> (WORD) */ >>>> +#define XDIR_CrtTime 8 /* exFAT: Created time (DWORD) */ >>>> +#define XDIR_ModTime 12 /* exFAT: Modified time >>>> (DWORD) */ >>>> +#define XDIR_AccTime 16 /* exFAT: Last accessed time >>>> (DWORD) */ >>>> +#define XDIR_CrtTime10 20 /* exFAT: Created time >>>> subsecond (BYTE) */ >>>> +#define XDIR_ModTime10 21 /* exFAT: Modified time >>>> subsecond (BYTE) */ >>>> +#define XDIR_CrtTZ 22 /* exFAT: Created timezone >>>> (BYTE) */ >>>> +#define XDIR_ModTZ 23 /* exFAT: Modified timezone >>>> (BYTE) */ >>>> +#define XDIR_AccTZ 24 /* exFAT: Last accessed >>>> timezone (BYTE) */ >>>> +#define XDIR_GenFlags 33 /* exFAT: General secondary >>>> flags (BYTE) */ >>>> +#define XDIR_NumName 35 /* exFAT: Number of file name >>>> characters (BYTE) */ >>>> +#define XDIR_NameHash 36 /* exFAT: Hash of file name >>>> (WORD) */ >>>> +#define XDIR_ValidFileSize 40 /* exFAT: Valid file size >>>> (QWORD) */ >>>> +#define XDIR_FstClus 52 /* exFAT: First cluster of >>>> the file data (DWORD) */ >>>> +#define XDIR_FileSize 56 /* exFAT: File/Directory >>>> size (QWORD) */ >>>> + >>>> +#define SZDIRE 32 /* Size of a directory >>>> entry */ >>>> +#define DDEM 0xE5 /* Deleted directory entry mark >>>> set to DIR_Name[0] */ >>>> +#define RDDEM 0x05 /* Replacement of the >>>> character collides with DDEM */ >>>> +#define LLEF 0x40 /* Last long entry flag in >>>> LDIR_Ord */ >>>> + >>>> +#define FSI_LeadSig 0 /* FAT32 FSI: Leading >>>> signature (DWORD) */ >>>> +#define FSI_StrucSig 484 /* FAT32 FSI: Structure >>>> signature (DWORD) */ >>>> +#define FSI_Free_Count 488 /* FAT32 FSI: Number of >>>> free clusters (DWORD) */ >>>> +#define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated >>>> cluster (DWORD) */ >>>> + >>>> +#define MBR_Table 446 /* MBR: Offset of partition >>>> table in the MBR */ >>>> +#define SZ_PTE 16 /* MBR: Size of a partition >>>> table entry */ >>>> +#define PTE_Boot 0 /* MBR PTE: Boot indicator */ >>>> +#define PTE_StHead 1 /* MBR PTE: Start head */ >>>> +#define PTE_StSec 2 /* MBR PTE: Start sector */ >>>> +#define PTE_StCyl 3 /* MBR PTE: Start cylinder */ >>>> +#define PTE_System 4 /* MBR PTE: System ID */ >>>> +#define PTE_EdHead 5 /* MBR PTE: End head */ >>>> +#define PTE_EdSec 6 /* MBR PTE: End sector */ >>>> +#define PTE_EdCyl 7 /* MBR PTE: End cylinder */ >>>> +#define PTE_StLba 8 /* MBR PTE: Start in LBA */ >>>> +#define PTE_SizLba 12 /* MBR PTE: Size in LBA */ >>>> + >>>> +#define GPTH_Sign 0 /* GPT: Header signature >>>> (8-byte) */ >>>> +#define GPTH_Rev 8 /* GPT: Revision (DWORD) */ >>>> +#define GPTH_Size 12 /* GPT: Header size (DWORD) */ >>>> +#define GPTH_Bcc 16 /* GPT: Header BCC (DWORD) */ >>>> +#define GPTH_CurLba 24 /* GPT: Main header LBA >>>> (QWORD) */ >>>> +#define GPTH_BakLba 32 /* GPT: Backup header LBA >>>> (QWORD) */ >>>> +#define GPTH_FstLba 40 /* GPT: First LBA for >>>> partitions (QWORD) */ >>>> +#define GPTH_LstLba 48 /* GPT: Last LBA for >>>> partitions (QWORD) */ >>>> +#define GPTH_DskGuid 56 /* GPT: Disk GUID (16-byte) */ >>>> +#define GPTH_PtOfs 72 /* GPT: Partation table LBA >>>> (QWORD) */ >>>> +#define GPTH_PtNum 80 /* GPT: Number of table >>>> entries (DWORD) */ >>>> +#define GPTH_PteSize 84 /* GPT: Size of table entry >>>> (DWORD) */ >>>> +#define GPTH_PtBcc 88 /* GPT: Partation table BCC >>>> (DWORD) */ >>>> +#define SZ_GPTE 128 /* GPT: Size of partition >>>> table entry */ >>>> +#define GPTE_PtGuid 0 /* GPT PTE: Partition type >>>> GUID (16-byte) */ >>>> +#define GPTE_UpGuid 16 /* GPT PTE: Partition >>>> unique GUID (16-byte) */ >>>> +#define GPTE_FstLba 32 /* GPT PTE: First LBA >>>> (QWORD) */ >>>> +#define GPTE_LstLba 40 /* GPT PTE: Last LBA >>>> inclusive (QWORD) */ >>>> +#define GPTE_Flags 48 /* GPT PTE: Flags (QWORD) */ >>>> +#define GPTE_Name 56 /* GPT PTE: Name */ >>>> + >>>> + >>>> +/* Post process on fatal error in the file operations */ >>>> +#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, >>>> res); } >>>> + >>>> + >>>> +/* Re-entrancy related */ >>>> +#if FF_FS_REENTRANT >>>> +#if FF_USE_LFN == 1 >>>> +#error Static LFN work area cannot be used at thread-safe >>>> configuration >>>> +#endif >>>> +#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } >>>> +#else >>>> +#define LEAVE_FF(fs, res) return res >>>> +#endif >>>> + >>>> + >>>> +/* Definitions of logical drive - physical location conversion */ >>>> +#if FF_MULTI_PARTITION >>>> +#define LD2PD(vol) VolToPart[vol].pd /* Get physical drive >>>> number */ >>>> +#define LD2PT(vol) VolToPart[vol].pt /* Get partition index */ >>>> +#else >>>> +#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is >>>> associated with the same physical drive number */ >>>> +#define LD2PT(vol) 0 /* Find first valid partition or in >>>> SFD */ >>>> +#endif >>>> + >>>> + >>>> +/* Definitions of sector size */ >>>> +#if (FF_MAX_SS < FF_MIN_SS) || (FF_MAX_SS != 512 && FF_MAX_SS != >>>> 1024 && FF_MAX_SS != 2048 && FF_MAX_SS != 4096) || (FF_MIN_SS != 512 >>>> && FF_MIN_SS != 1024 && FF_MIN_SS != 2048 && FF_MIN_SS != 4096) >>>> +#error Wrong sector size configuration >>>> +#endif >>>> +#if FF_MAX_SS == FF_MIN_SS >>>> +#define SS(fs) ((UINT)FF_MAX_SS) /* Fixed sector size */ >>>> +#else >>>> +#define SS(fs) ((fs)->ssize) /* Variable sector size */ >>>> +#endif >>>> + >>>> + >>>> +/* Timestamp */ >>>> +#if FF_FS_NORTC == 1 >>>> +#if FF_NORTC_YEAR < 1980 || FF_NORTC_YEAR > 2107 || FF_NORTC_MON < >>>> 1 || FF_NORTC_MON > 12 || FF_NORTC_MDAY < 1 || FF_NORTC_MDAY > 31 >>>> +#error Invalid FF_FS_NORTC settings >>>> +#endif >>>> +#define GET_FATTIME() ((DWORD)(FF_NORTC_YEAR - 1980) << 25 | >>>> (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16) >>>> +#else >>>> +#define GET_FATTIME() get_fattime() >>>> +#endif >>>> + >>>> + >>>> +/* File lock controls */ >>>> +#if FF_FS_LOCK != 0 >>>> +#if FF_FS_READONLY >>>> +#error FF_FS_LOCK must be 0 at read-only configuration >>>> +#endif >>>> +typedef struct { >>>> + FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */ >>>> + DWORD clu; /* Object ID 2, containing directory (0:root) */ >>>> + DWORD ofs; /* Object ID 3, offset in the directory */ >>>> + WORD ctr; /* Object open counter, 0:none, >>>> 0x01..0xFF:read mode open count, 0x100:write mode */ >>>> +} FILESEM; >>>> +#endif >>>> + >>>> + >>>> +/* SBCS up-case tables (\x80-\xFF) */ >>>> +#define TBL_CT437 >>>> {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, >>>> \ >>>> + >>>> 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>>> \ >>>> + >>>> 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>>> \ >>>> + >>>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>>> \ >>>> + >>>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>>> \ >>>> + >>>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>>> \ >>>> + >>>> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >>>> \ >>>> + >>>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>>> >>>> +#define TBL_CT720 >>>> {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >>>> \ >>>> + >>>> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>>> \ >>>> + >>>> 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>>> \ >>>> + >>>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>>> \ >>>> + >>>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>>> \ >>>> + >>>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>>> \ >>>> + >>>> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >>>> \ >>>> + >>>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>>> >>>> +#define TBL_CT737 >>>> {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >>>> \ >>>> + >>>> 0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, >>>> \ >>>> + >>>> 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, >>>> \ >>>> + >>>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>>> \ >>>> + >>>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>>> \ >>>> + >>>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>>> \ >>>> + >>>> 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >>>> \ >>>> + >>>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>>> >>>> +#define TBL_CT771 >>>> {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >>>> \ >>>> + >>>> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>>> \ >>>> + >>>> 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >>>> \ >>>> + >>>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>>> \ >>>> + >>>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>>> \ >>>> + >>>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, >>>> \ >>>> + >>>> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>>> \ >>>> + >>>> 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF} >>>> >>>> +#define TBL_CT775 >>>> {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, >>>> \ >>>> + >>>> 0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, >>>> \ >>>> + >>>> 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>>> \ >>>> + >>>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>>> \ >>>> + >>>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>>> \ >>>> + >>>> 0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>>> \ >>>> + >>>> 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, >>>> \ >>>> + >>>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>>> >>>> +#define TBL_CT850 >>>> {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, >>>> \ >>>> + >>>> 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, >>>> \ >>>> + >>>> 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>>> \ >>>> + >>>> 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>>> \ >>>> + >>>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>>> \ >>>> + >>>> 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, >>>> \ >>>> + >>>> 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, >>>> \ >>>> + >>>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>>> >>>> +#define TBL_CT852 >>>> {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, >>>> \ >>>> + >>>> 0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, >>>> \ >>>> + >>>> 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, >>>> \ >>>> + >>>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, >>>> \ >>>> + >>>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>>> \ >>>> + >>>> 0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>>> \ >>>> + >>>> 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, >>>> \ >>>> + >>>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} >>>> >>>> +#define TBL_CT855 >>>> {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, >>>> \ >>>> + >>>> 0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, >>>> \ >>>> + >>>> 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, >>>> \ >>>> + >>>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, >>>> \ >>>> + >>>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>>> \ >>>> + >>>> 0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, >>>> \ >>>> + >>>> 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, >>>> \ >>>> + >>>> 0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} >>>> >>>> +#define TBL_CT857 >>>> {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, >>>> \ >>>> + >>>> 0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, >>>> \ >>>> + >>>> 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>>> \ >>>> + >>>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>>> \ >>>> + >>>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>>> \ >>>> + >>>> 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>>> \ >>>> + >>>> 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, >>>> \ >>>> + >>>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>>> >>>> +#define TBL_CT860 >>>> {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, >>>> \ >>>> + >>>> 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>>> \ >>>> + >>>> 0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>>> \ >>>> + >>>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>>> \ >>>> + >>>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>>> \ >>>> + >>>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>>> \ >>>> + >>>> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >>>> \ >>>> + >>>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>>> >>>> +#define TBL_CT861 >>>> {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, >>>> \ >>>> + >>>> 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, >>>> \ >>>> + >>>> 0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>>> \ >>>> + >>>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>>> \ >>>> + >>>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>>> \ >>>> + >>>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>>> \ >>>> + >>>> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >>>> \ >>>> + >>>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>>> >>>> +#define TBL_CT862 >>>> {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >>>> \ >>>> + >>>> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>>> \ >>>> + >>>> 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>>> \ >>>> + >>>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>>> \ >>>> + >>>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>>> \ >>>> + >>>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>>> \ >>>> + >>>> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >>>> \ >>>> + >>>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>>> >>>> +#define TBL_CT863 >>>> {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, >>>> \ >>>> + >>>> 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, >>>> \ >>>> + >>>> 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>>> \ >>>> + >>>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>>> \ >>>> + >>>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>>> \ >>>> + >>>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>>> \ >>>> + >>>> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >>>> \ >>>> + >>>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>>> >>>> +#define TBL_CT864 >>>> {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, >>>> \ >>>> + >>>> 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>>> \ >>>> + >>>> 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>>> \ >>>> + >>>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>>> \ >>>> + >>>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>>> \ >>>> + >>>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>>> \ >>>> + >>>> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >>>> \ >>>> + >>>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>>> >>>> +#define TBL_CT865 >>>> {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, >>>> \ >>>> + >>>> 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>>> \ >>>> + >>>> 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>>> \ >>>> + >>>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>>> \ >>>> + >>>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>>> \ >>>> + >>>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>>> \ >>>> + >>>> 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, >>>> \ >>>> + >>>> 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>>> >>>> +#define TBL_CT866 >>>> {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >>>> \ >>>> + >>>> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>>> \ >>>> + >>>> 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >>>> \ >>>> + >>>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>>> \ >>>> + >>>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>>> \ >>>> + >>>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, >>>> \ >>>> + >>>> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, >>>> \ >>>> + >>>> 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} >>>> >>>> +#define TBL_CT869 >>>> {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, >>>> \ >>>> + >>>> 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, >>>> \ >>>> + >>>> 0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, >>>> \ >>>> + >>>> 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, >>>> \ >>>> + >>>> 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, >>>> \ >>>> + >>>> 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, >>>> \ >>>> + >>>> 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, >>>> \ >>>> + >>>> 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF} >>>> >>>> + >>>> + >>>> +/* DBCS code range |----- 1st byte -----| |----------- 2nd byte >>>> -----------| */ >>>> +/* <------> <------> <------> <------> >>>> <------> */ >>>> +#define TBL_DC932 {0x81, 0x9F, 0xE0, 0xFC, 0x40, 0x7E, 0x80, 0xFC, >>>> 0x00, 0x00} >>>> +#define TBL_DC936 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0x80, 0xFE, >>>> 0x00, 0x00} >>>> +#define TBL_DC949 {0x81, 0xFE, 0x00, 0x00, 0x41, 0x5A, 0x61, 0x7A, >>>> 0x81, 0xFE} >>>> +#define TBL_DC950 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0xA1, 0xFE, >>>> 0x00, 0x00} >>>> + >>>> + >>>> +/* Macros for table definitions */ >>>> +#define MERGE_2STR(a, b) a ## b >>>> +#define MKCVTBL(hd, cp) MERGE_2STR(hd, cp) >>>> + >>>> + >>>> + >>>> + >>>> +/*-------------------------------------------------------------------------- >>>> >>>> + >>>> + Module Private Work Area >>>> + >>>> +---------------------------------------------------------------------------*/ >>>> >>>> +/* Remark: Variables defined here without initial value shall be >>>> guaranteed >>>> +/ zero/null at start-up. If not, the linker option or start-up >>>> routine is >>>> +/ not compliance with C standard. */ >>>> + >>>> +/*--------------------------------*/ >>>> +/* File/Volume controls */ >>>> +/*--------------------------------*/ >>>> + >>>> +#if FF_VOLUMES < 1 || FF_VOLUMES > 10 >>>> +#error Wrong FF_VOLUMES setting >>>> +#endif >>>> +static FATFS* FatFs[FF_VOLUMES]; /* Pointer to the filesystem >>>> objects (logical drives) */ >>>> +static WORD Fsid; /* Filesystem mount ID */ >>>> + >>>> +#if FF_FS_RPATH != 0 >>>> +static BYTE CurrVol; /* Current drive */ >>>> +#endif >>>> + >>>> +#if FF_FS_LOCK != 0 >>>> +static FILESEM Files[FF_FS_LOCK]; /* Open object lock semaphores */ >>>> +#endif >>>> + >>>> +#if FF_STR_VOLUME_ID >>>> +#ifdef FF_VOLUME_STRS >>>> +static const char* const VolumeStr[FF_VOLUMES] = {FF_VOLUME_STRS}; >>>> /* Pre-defined volume ID */ >>>> +#endif >>>> +#endif >>>> + >>>> +#if FF_LBA64 >>>> +#if FF_MIN_GPT > 0x100000000 >>>> +#error Wrong FF_MIN_GPT setting >>>> +#endif >>>> +static const BYTE GUID_MS_Basic[16] = >>>> {0xA2,0xA0,0xD0,0xEB,0xE5,0xB9,0x33,0x44,0x87,0xC0,0x68,0xB6,0xB7,0x26,0x99,0xC7}; >>>> >>>> +#endif >>>> + >>>> + >>>> + >>>> +/*--------------------------------*/ >>>> +/* LFN/Directory working buffer */ >>>> +/*--------------------------------*/ >>>> + >>>> +#if FF_USE_LFN == 0 /* Non-LFN configuration */ >>>> +#if FF_FS_EXFAT >>>> +#error LFN must be enabled when enable exFAT >>>> +#endif >>>> +#define DEF_NAMBUF >>>> +#define INIT_NAMBUF(fs) >>>> +#define FREE_NAMBUF() >>>> +#define LEAVE_MKFS(res) return res >>>> + >>>> +#else /* LFN configurations */ >>>> +#if FF_MAX_LFN < 12 || FF_MAX_LFN > 255 >>>> +#error Wrong setting of FF_MAX_LFN >>>> +#endif >>>> +#if FF_LFN_BUF < FF_SFN_BUF || FF_SFN_BUF < 12 >>>> +#error Wrong setting of FF_LFN_BUF or FF_SFN_BUF >>>> +#endif >>>> +#if FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3 >>>> +#error Wrong setting of FF_LFN_UNICODE >>>> +#endif >>>> +static const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; >>>> /* FAT: Offset of LFN characters in the directory entry */ >>>> +#define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) /* exFAT: Size >>>> of directory entry block scratchpad buffer needed for the name >>>> length */ >>>> + >>>> +#if FF_USE_LFN == 1 /* LFN enabled with static working >>>> buffer */ >>>> +#if FF_FS_EXFAT >>>> +static BYTE DirBuf[MAXDIRB(FF_MAX_LFN)]; /* Directory entry >>>> block scratchpad buffer */ >>>> +#endif >>>> +static WCHAR LfnBuf[FF_MAX_LFN + 1]; /* LFN working buffer */ >>>> +#define DEF_NAMBUF >>>> +#define INIT_NAMBUF(fs) >>>> +#define FREE_NAMBUF() >>>> +#define LEAVE_MKFS(res) return res >>>> + >>>> +#elif FF_USE_LFN == 2 /* LFN enabled with dynamic working >>>> buffer on the stack */ >>>> +#if FF_FS_EXFAT >>>> +#define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; BYTE >>>> dbuf[MAXDIRB(FF_MAX_LFN)]; /* LFN working buffer and directory >>>> entry block scratchpad buffer */ >>>> +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = >>>> dbuf; } >>>> +#define FREE_NAMBUF() >>>> +#else >>>> +#define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; /* LFN >>>> working buffer */ >>>> +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; } >>>> +#define FREE_NAMBUF() >>>> +#endif >>>> +#define LEAVE_MKFS(res) return res >>>> + >>>> +#elif FF_USE_LFN == 3 /* LFN enabled with dynamic working >>>> buffer on the heap */ >>>> +#if FF_FS_EXFAT >>>> +#define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working >>>> buffer and directory entry block scratchpad buffer */ >>>> +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2 + >>>> MAXDIRB(FF_MAX_LFN)); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); >>>> (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+FF_MAX_LFN+1); } >>>> +#define FREE_NAMBUF() ff_memfree(lfn) >>>> +#else >>>> +#define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working >>>> buffer */ >>>> +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2); >>>> if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; } >>>> +#define FREE_NAMBUF() ff_memfree(lfn) >>>> +#endif >>>> +#define LEAVE_MKFS(res) { if (!work) ff_memfree(buf); return res; } >>>> +#define MAX_MALLOC 0x8000 /* Must be >=FF_MAX_SS */ >>>> + >>>> +#else >>>> +#error Wrong setting of FF_USE_LFN >>>> + >>>> +#endif /* FF_USE_LFN == 1 */ >>>> +#endif /* FF_USE_LFN == 0 */ >>>> + >>>> + >>>> + >>>> +/*--------------------------------*/ >>>> +/* Code conversion tables */ >>>> +/*--------------------------------*/ >>>> + >>>> +#if FF_CODE_PAGE == 0 /* Run-time code page configuration */ >>>> +#define CODEPAGE CodePage >>>> +static WORD CodePage; /* Current code page */ >>>> +static const BYTE *ExCvt, *DbcTbl; /* Pointer to current SBCS >>>> up-case table and DBCS code range table below */ >>>> + >>>> +static const BYTE Ct437[] = TBL_CT437; >>>> +static const BYTE Ct720[] = TBL_CT720; >>>> +static const BYTE Ct737[] = TBL_CT737; >>>> +static const BYTE Ct771[] = TBL_CT771; >>>> +static const BYTE Ct775[] = TBL_CT775; >>>> +static const BYTE Ct850[] = TBL_CT850; >>>> +static const BYTE Ct852[] = TBL_CT852; >>>> +static const BYTE Ct855[] = TBL_CT855; >>>> +static const BYTE Ct857[] = TBL_CT857; >>>> +static const BYTE Ct860[] = TBL_CT860; >>>> +static const BYTE Ct861[] = TBL_CT861; >>>> +static const BYTE Ct862[] = TBL_CT862; >>>> +static const BYTE Ct863[] = TBL_CT863; >>>> +static const BYTE Ct864[] = TBL_CT864; >>>> +static const BYTE Ct865[] = TBL_CT865; >>>> +static const BYTE Ct866[] = TBL_CT866; >>>> +static const BYTE Ct869[] = TBL_CT869; >>>> +static const BYTE Dc932[] = TBL_DC932; >>>> +static const BYTE Dc936[] = TBL_DC936; >>>> +static const BYTE Dc949[] = TBL_DC949; >>>> +static const BYTE Dc950[] = TBL_DC950; >>>> + >>>> +#elif FF_CODE_PAGE < 900 /* Static code page configuration >>>> (SBCS) */ >>>> +#define CODEPAGE FF_CODE_PAGE >>>> +static const BYTE ExCvt[] = MKCVTBL(TBL_CT, FF_CODE_PAGE); >>>> + >>>> +#else /* Static code page configuration (DBCS) */ >>>> +#define CODEPAGE FF_CODE_PAGE >>>> +static const BYTE DbcTbl[] = MKCVTBL(TBL_DC, FF_CODE_PAGE); >>>> + >>>> +#endif >>>> + >>>> + >>>> + >>>> + >>>> +/*-------------------------------------------------------------------------- >>>> >>>> + >>>> + Module Private Functions >>>> + >>>> +---------------------------------------------------------------------------*/ >>>> >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Load/Store multi-byte word in the FAT >>>> structure */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static WORD ld_word (const BYTE* ptr) /* Load a 2-byte >>>> little-endian word */ >>>> +{ >>>> + WORD rv; >>>> + >>>> + rv = ptr[1]; >>>> + rv = rv << 8 | ptr[0]; >>>> + return rv; >>>> +} >>>> + >>>> +static DWORD ld_dword (const BYTE* ptr) /* Load a 4-byte >>>> little-endian word */ >>>> +{ >>>> + DWORD rv; >>>> + >>>> + rv = ptr[3]; >>>> + rv = rv << 8 | ptr[2]; >>>> + rv = rv << 8 | ptr[1]; >>>> + rv = rv << 8 | ptr[0]; >>>> + return rv; >>>> +} >>>> + >>>> +#if FF_FS_EXFAT >>>> +static QWORD ld_qword (const BYTE* ptr) /* Load an 8-byte >>>> little-endian word */ >>>> +{ >>>> + QWORD rv; >>>> + >>>> + rv = ptr[7]; >>>> + rv = rv << 8 | ptr[6]; >>>> + rv = rv << 8 | ptr[5]; >>>> + rv = rv << 8 | ptr[4]; >>>> + rv = rv << 8 | ptr[3]; >>>> + rv = rv << 8 | ptr[2]; >>>> + rv = rv << 8 | ptr[1]; >>>> + rv = rv << 8 | ptr[0]; >>>> + return rv; >>>> +} >>>> +#endif >>>> + >>>> +#if !FF_FS_READONLY >>>> +static void st_word (BYTE* ptr, WORD val) /* Store a 2-byte word >>>> in little-endian */ >>>> +{ >>>> + *ptr++ = (BYTE)val; val >>= 8; >>>> + *ptr++ = (BYTE)val; >>>> +} >>>> + >>>> +static void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte >>>> word in little-endian */ >>>> +{ >>>> + *ptr++ = (BYTE)val; val >>= 8; >>>> + *ptr++ = (BYTE)val; val >>= 8; >>>> + *ptr++ = (BYTE)val; val >>= 8; >>>> + *ptr++ = (BYTE)val; >>>> +} >>>> + >>>> +#if FF_FS_EXFAT >>>> +static void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte >>>> word in little-endian */ >>>> +{ >>>> + *ptr++ = (BYTE)val; val >>= 8; >>>> + *ptr++ = (BYTE)val; val >>= 8; >>>> + *ptr++ = (BYTE)val; val >>= 8; >>>> + *ptr++ = (BYTE)val; val >>= 8; >>>> + *ptr++ = (BYTE)val; val >>= 8; >>>> + *ptr++ = (BYTE)val; val >>= 8; >>>> + *ptr++ = (BYTE)val; val >>= 8; >>>> + *ptr++ = (BYTE)val; >>>> +} >>>> +#endif >>>> +#endif /* !FF_FS_READONLY */ >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* String >>>> functions */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +/* Copy memory to memory */ >>>> +static void mem_cpy (void* dst, const void* src, UINT cnt) >>>> +{ >>>> + BYTE *d = (BYTE*)dst; >>>> + const BYTE *s = (const BYTE*)src; >>>> + >>>> + if (cnt != 0) { >>>> + do { >>>> + *d++ = *s++; >>>> + } while (--cnt); >>>> + } >>>> +} >>>> + >>>> + >>>> +/* Fill memory block */ >>>> +static void mem_set (void* dst, int val, UINT cnt) >>>> +{ >>>> + BYTE *d = (BYTE*)dst; >>>> + >>>> + do { >>>> + *d++ = (BYTE)val; >>>> + } while (--cnt); >>>> +} >>>> + >>>> + >>>> +/* Compare memory block */ >>>> +static int mem_cmp (const void* dst, const void* src, UINT cnt) /* >>>> ZR:same, NZ:different */ >>>> +{ >>>> + const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src; >>>> + int r = 0; >>>> + >>>> + do { >>>> + r = *d++ - *s++; >>>> + } while (--cnt && r == 0); >>>> + >>>> + return r; >>>> +} >>>> + >>>> + >>>> +/* Check if chr is contained in the string */ >>>> +static int chk_chr (const char* str, int chr) /* NZ:contained, >>>> ZR:not contained */ >>>> +{ >>>> + while (*str && *str != chr) str++; >>>> + return *str; >>>> +} >>>> + >>>> + >>>> +/* Test if the byte is DBC 1st byte */ >>>> +static int dbc_1st (BYTE c) >>>> +{ >>>> +#if FF_CODE_PAGE == 0 /* Variable code page */ >>>> + if (DbcTbl && c >= DbcTbl[0]) { >>>> + if (c <= DbcTbl[1]) return 1; /* 1st >>>> byte range 1 */ >>>> + if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1; /* 1st >>>> byte range 2 */ >>>> + } >>>> +#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */ >>>> + if (c >= DbcTbl[0]) { >>>> + if (c <= DbcTbl[1]) return 1; >>>> + if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1; >>>> + } >>>> +#else /* SBCS fixed code page */ >>>> + if (c != 0) return 0; /* Always false */ >>>> +#endif >>>> + return 0; >>>> +} >>>> + >>>> + >>>> +/* Test if the byte is DBC 2nd byte */ >>>> +static int dbc_2nd (BYTE c) >>>> +{ >>>> +#if FF_CODE_PAGE == 0 /* Variable code page */ >>>> + if (DbcTbl && c >= DbcTbl[4]) { >>>> + if (c <= DbcTbl[5]) return 1; /* 2nd >>>> byte range 1 */ >>>> + if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1; /* 2nd >>>> byte range 2 */ >>>> + if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1; /* 2nd >>>> byte range 3 */ >>>> + } >>>> +#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */ >>>> + if (c >= DbcTbl[4]) { >>>> + if (c <= DbcTbl[5]) return 1; >>>> + if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1; >>>> + if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1; >>>> + } >>>> +#else /* SBCS fixed code page */ >>>> + if (c != 0) return 0; /* Always false */ >>>> +#endif >>>> + return 0; >>>> +} >>>> + >>>> + >>>> +#if FF_USE_LFN >>>> + >>>> +/* Get a Unicode code point from the TCHAR string in defined API >>>> encodeing */ >>>> +static DWORD tchar2uni ( /* Returns a character in UTF-16 >>>> encoding (>=0x10000 on surrogate pair, 0xFFFFFFFF on decode error) */ >>>> + const TCHAR** str /* Pointer to pointer to TCHAR string >>>> in configured encoding */ >>>> +) >>>> +{ >>>> + DWORD uc; >>>> + const TCHAR *p = *str; >>>> + >>>> +#if FF_LFN_UNICODE == 1 /* UTF-16 input */ >>>> + WCHAR wc; >>>> + >>>> + uc = *p++; /* Get a unit */ >>>> + if (IsSurrogate(uc)) { /* Surrogate? */ >>>> + wc = *p++; /* Get low surrogate */ >>>> + if (!IsSurrogateH(uc) || !IsSurrogateL(wc)) return >>>> 0xFFFFFFFF; /* Wrong surrogate? */ >>>> + uc = uc << 16 | wc; >>>> + } >>>> + >>>> +#elif FF_LFN_UNICODE == 2 /* UTF-8 input */ >>>> + BYTE b; >>>> + int nf; >>>> + >>>> + uc = (BYTE)*p++; /* Get an encoding unit */ >>>> + if (uc & 0x80) { /* Multiple byte code? */ >>>> + if ((uc & 0xE0) == 0xC0) { /* 2-byte sequence? */ >>>> + uc &= 0x1F; nf = 1; >>>> + } else { >>>> + if ((uc & 0xF0) == 0xE0) { /* 3-byte sequence? */ >>>> + uc &= 0x0F; nf = 2; >>>> + } else { >>>> + if ((uc & 0xF8) == 0xF0) { /* 4-byte sequence? */ >>>> + uc &= 0x07; nf = 3; >>>> + } else { /* Wrong sequence */ >>>> + return 0xFFFFFFFF; >>>> + } >>>> + } >>>> + } >>>> + do { /* Get trailing bytes */ >>>> + b = (BYTE)*p++; >>>> + if ((b & 0xC0) != 0x80) return 0xFFFFFFFF; /* Wrong >>>> sequence? */ >>>> + uc = uc << 6 | (b & 0x3F); >>>> + } while (--nf != 0); >>>> + if (uc < 0x80 || IsSurrogate(uc) || uc >= 0x110000) return >>>> 0xFFFFFFFF; /* Wrong code? */ >>>> + if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 >>>> & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */ >>>> + } >>>> + >>>> +#elif FF_LFN_UNICODE == 3 /* UTF-32 input */ >>>> + uc = (TCHAR)*p++; /* Get a unit */ >>>> + if (uc >= 0x110000 || IsSurrogate(uc)) return 0xFFFFFFFF; /* >>>> Wrong code? */ >>>> + if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & >>>> 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */ >>>> + >>>> +#else /* ANSI/OEM input */ >>>> + BYTE b; >>>> + WCHAR wc; >>>> + >>>> + wc = (BYTE)*p++; /* Get a byte */ >>>> + if (dbc_1st((BYTE)wc)) { /* Is it a DBC 1st byte? */ >>>> + b = (BYTE)*p++; /* Get 2nd byte */ >>>> + if (!dbc_2nd(b)) return 0xFFFFFFFF; /* Invalid code? */ >>>> + wc = (wc << 8) + b; /* Make a DBC */ >>>> + } >>>> + if (wc != 0) { >>>> + wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM ==> Unicode */ >>>> + if (wc == 0) return 0xFFFFFFFF; /* Invalid code? */ >>>> + } >>>> + uc = wc; >>>> + >>>> +#endif >>>> + *str = p; /* Next read pointer */ >>>> + return uc; >>>> +} >>>> + >>>> + >>>> +/* Output a TCHAR string in defined API encoding */ >>>> +static BYTE put_utf ( /* Returns number of encoding units >>>> written (0:buffer overflow or wrong encoding) */ >>>> + DWORD chr, /* UTF-16 encoded character (Surrogate pair if >>>> >=0x10000) */ >>>> + TCHAR* buf, /* Output buffer */ >>>> + UINT szb /* Size of the buffer */ >>>> +) >>>> +{ >>>> +#if FF_LFN_UNICODE == 1 /* UTF-16 output */ >>>> + WCHAR hs, wc; >>>> + >>>> + hs = (WCHAR)(chr >> 16); >>>> + wc = (WCHAR)chr; >>>> + if (hs == 0) { /* Single encoding unit? */ >>>> + if (szb < 1 || IsSurrogate(wc)) return 0; /* Buffer >>>> overflow or wrong code? */ >>>> + *buf = wc; >>>> + return 1; >>>> + } >>>> + if (szb < 2 || !IsSurrogateH(hs) || !IsSurrogateL(wc)) return >>>> 0; /* Buffer overflow or wrong surrogate? */ >>>> + *buf++ = hs; >>>> + *buf++ = wc; >>>> + return 2; >>>> + >>>> +#elif FF_LFN_UNICODE == 2 /* UTF-8 output */ >>>> + DWORD hc; >>>> + >>>> + if (chr < 0x80) { /* Single byte code? */ >>>> + if (szb < 1) return 0; /* Buffer overflow? */ >>>> + *buf = (TCHAR)chr; >>>> + return 1; >>>> + } >>>> + if (chr < 0x800) { /* 2-byte sequence? */ >>>> + if (szb < 2) return 0; /* Buffer overflow? */ >>>> + *buf++ = (TCHAR)(0xC0 | (chr >> 6 & 0x1F)); >>>> + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); >>>> + return 2; >>>> + } >>>> + if (chr < 0x10000) { /* 3-byte sequence? */ >>>> + if (szb < 3 || IsSurrogate(chr)) return 0; /* Buffer >>>> overflow or wrong code? */ >>>> + *buf++ = (TCHAR)(0xE0 | (chr >> 12 & 0x0F)); >>>> + *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F)); >>>> + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); >>>> + return 3; >>>> + } >>>> + /* 4-byte sequence */ >>>> + if (szb < 4) return 0; /* Buffer overflow? */ >>>> + hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high 10 >>>> bits */ >>>> + chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 >>>> bits */ >>>> + if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong >>>> surrogate? */ >>>> + chr = (hc | chr) + 0x10000; >>>> + *buf++ = (TCHAR)(0xF0 | (chr >> 18 & 0x07)); >>>> + *buf++ = (TCHAR)(0x80 | (chr >> 12 & 0x3F)); >>>> + *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F)); >>>> + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); >>>> + return 4; >>>> + >>>> +#elif FF_LFN_UNICODE == 3 /* UTF-32 output */ >>>> + DWORD hc; >>>> + >>>> + if (szb < 1) return 0; /* Buffer overflow? */ >>>> + if (chr >= 0x10000) { /* Out of BMP? */ >>>> + hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high >>>> 10 bits */ >>>> + chr = (chr & 0xFFFF) - 0xDC00; /* Get >>>> low 10 bits */ >>>> + if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong >>>> surrogate? */ >>>> + chr = (hc | chr) + 0x10000; >>>> + } >>>> + *buf++ = (TCHAR)chr; >>>> + return 1; >>>> + >>>> +#else /* ANSI/OEM output */ >>>> + WCHAR wc; >>>> + >>>> + wc = ff_uni2oem(chr, CODEPAGE); >>>> + if (wc >= 0x100) { /* Is this a DBC? */ >>>> + if (szb < 2) return 0; >>>> + *buf++ = (char)(wc >> 8); /* Store DBC 1st byte */ >>>> + *buf++ = (TCHAR)wc; /* Store DBC 2nd byte */ >>>> + return 2; >>>> + } >>>> + if (wc == 0 || szb < 1) return 0; /* Invalid char or buffer >>>> overflow? */ >>>> + *buf++ = (TCHAR)wc; /* Store the character */ >>>> + return 1; >>>> +#endif >>>> +} >>>> +#endif /* FF_USE_LFN */ >>>> + >>>> + >>>> +#if FF_FS_REENTRANT >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Request/Release grant to access the >>>> volume */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +static int lock_fs ( /* 1:Ok, 0:timeout */ >>>> + FATFS* fs /* Filesystem object */ >>>> +) >>>> +{ >>>> + return ff_req_grant(fs->sobj); >>>> +} >>>> + >>>> + >>>> +static void unlock_fs ( >>>> + FATFS* fs, /* Filesystem object */ >>>> + FRESULT res /* Result code to be returned */ >>>> +) >>>> +{ >>>> + if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && >>>> res != FR_TIMEOUT) { >>>> + ff_rel_grant(fs->sobj); >>>> + } >>>> +} >>>> + >>>> +#endif >>>> + >>>> + >>>> + >>>> +#if FF_FS_LOCK != 0 >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* File lock control >>>> functions */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static FRESULT chk_lock ( /* Check if the file can be accessed */ >>>> + DIR* dp, /* Directory object pointing the file to be >>>> checked */ >>>> + int acc /* Desired access type (0:Read mode open, >>>> 1:Write mode open, 2:Delete or rename) */ >>>> +) >>>> +{ >>>> + UINT i, be; >>>> + >>>> + /* Search open object table for the object */ >>>> + be = 0; >>>> + for (i = 0; i < FF_FS_LOCK; i++) { >>>> + if (Files[i].fs) { /* Existing entry */ >>>> + if (Files[i].fs == dp->obj.fs && /* Check if >>>> the object matches with an open object */ >>>> + Files[i].clu == dp->obj.sclust && >>>> + Files[i].ofs == dp->dptr) break; >>>> + } else { /* Blank entry */ >>>> + be = 1; >>>> + } >>>> + } >>>> + if (i == FF_FS_LOCK) { /* The object has not been opened */ >>>> + return (!be && acc != 2) ? FR_TOO_MANY_OPEN_FILES : FR_OK; >>>> /* Is there a blank entry for new object? */ >>>> + } >>>> + >>>> + /* The object was opened. Reject any open against writing file >>>> and all write mode open */ >>>> + return (acc != 0 || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; >>>> +} >>>> + >>>> + >>>> +static int enq_lock (void) /* Check if an entry is available for >>>> a new object */ >>>> +{ >>>> + UINT i; >>>> + >>>> + for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ; >>>> + return (i == FF_FS_LOCK) ? 0 : 1; >>>> +} >>>> + >>>> + >>>> +static UINT inc_lock ( /* Increment object open counter and >>>> returns its index (0:Internal error) */ >>>> + DIR* dp, /* Directory object pointing the file to register >>>> or increment */ >>>> + int acc /* Desired access (0:Read, 1:Write, >>>> 2:Delete/Rename) */ >>>> +) >>>> +{ >>>> + UINT i; >>>> + >>>> + >>>> + for (i = 0; i < FF_FS_LOCK; i++) { /* Find the object */ >>>> + if (Files[i].fs == dp->obj.fs >>>> + && Files[i].clu == dp->obj.sclust >>>> + && Files[i].ofs == dp->dptr) break; >>>> + } >>>> + >>>> + if (i == FF_FS_LOCK) { /* Not opened. Register >>>> it as new. */ >>>> + for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ; >>>> + if (i == FF_FS_LOCK) return 0; /* No free entry to >>>> register (int err) */ >>>> + Files[i].fs = dp->obj.fs; >>>> + Files[i].clu = dp->obj.sclust; >>>> + Files[i].ofs = dp->dptr; >>>> + Files[i].ctr = 0; >>>> + } >>>> + >>>> + if (acc >= 1 && Files[i].ctr) return 0; /* Access violation >>>> (int err) */ >>>> + >>>> + Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set >>>> semaphore value */ >>>> + >>>> + return i + 1; /* Index number origin from 1 */ >>>> +} >>>> + >>>> + >>>> +static FRESULT dec_lock ( /* Decrement object open counter */ >>>> + UINT i /* Semaphore index (1..) */ >>>> +) >>>> +{ >>>> + WORD n; >>>> + FRESULT res; >>>> + >>>> + >>>> + if (--i < FF_FS_LOCK) { /* Index number origin from 0 */ >>>> + n = Files[i].ctr; >>>> + if (n == 0x100) n = 0; /* If write mode open, delete >>>> the entry */ >>>> + if (n > 0) n--; /* Decrement read mode open >>>> count */ >>>> + Files[i].ctr = n; >>>> + if (n == 0) Files[i].fs = 0; /* Delete the entry if open >>>> count gets zero */ >>>> + res = FR_OK; >>>> + } else { >>>> + res = FR_INT_ERR; /* Invalid index nunber */ >>>> + } >>>> + return res; >>>> +} >>>> + >>>> + >>>> +static void clear_lock ( /* Clear lock entries of the volume */ >>>> + FATFS *fs >>>> +) >>>> +{ >>>> + UINT i; >>>> + >>>> + for (i = 0; i < FF_FS_LOCK; i++) { >>>> + if (Files[i].fs == fs) Files[i].fs = 0; >>>> + } >>>> +} >>>> + >>>> +#endif /* FF_FS_LOCK != 0 */ >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Move/Flush disk access window in the filesystem >>>> object */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +#if !FF_FS_READONLY >>>> +static FRESULT sync_window ( /* Returns FR_OK or FR_DISK_ERR */ >>>> + FATFS* fs /* Filesystem object */ >>>> +) >>>> +{ >>>> + FRESULT res = FR_OK; >>>> + >>>> + >>>> + if (fs->wflag) { /* Is the disk access window dirty? */ >>>> + if (disk_write(fs->pdrv, fs->win, fs->winsect, 1) == >>>> RES_OK) { /* Write it back into the volume */ >>>> + fs->wflag = 0; /* Clear window dirty flag */ >>>> + if (fs->winsect - fs->fatbase < fs->fsize) { /* Is >>>> it in the 1st FAT? */ >>>> + if (fs->n_fats == 2) disk_write(fs->pdrv, fs->win, >>>> fs->winsect + fs->fsize, 1); /* Reflect it to 2nd FAT if needed */ >>>> + } >>>> + } else { >>>> + res = FR_DISK_ERR; >>>> + } >>>> + } >>>> + return res; >>>> +} >>>> +#endif >>>> + >>>> + >>>> +static FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERR */ >>>> + FATFS* fs, /* Filesystem object */ >>>> + LBA_t sect /* Sector LBA to make appearance in the >>>> fs->win[] */ >>>> +) >>>> +{ >>>> + FRESULT res = FR_OK; >>>> + >>>> + >>>> + if (sect != fs->winsect) { /* Window offset changed? */ >>>> +#if !FF_FS_READONLY >>>> + res = sync_window(fs); /* Flush the window */ >>>> +#endif >>>> + if (res == FR_OK) { /* Fill sector window with >>>> new data */ >>>> + if (disk_read(fs->pdrv, fs->win, sect, 1) != RES_OK) { >>>> + sect = (LBA_t)0 - 1; /* Invalidate window if >>>> read data is not valid */ >>>> + res = FR_DISK_ERR; >>>> + } >>>> + fs->winsect = sect; >>>> + } >>>> + } >>>> + return res; >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +#if !FF_FS_READONLY >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Synchronize filesystem and data on the >>>> storage */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */ >>>> + FATFS* fs /* Filesystem object */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + >>>> + >>>> + res = sync_window(fs); >>>> + if (res == FR_OK) { >>>> + if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { /* >>>> FAT32: Update FSInfo sector if needed */ >>>> + /* Create FSInfo structure */ >>>> + mem_set(fs->win, 0, sizeof fs->win); >>>> + st_word(fs->win + BS_55AA, 0xAA55); /* Boot signature */ >>>> + st_dword(fs->win + FSI_LeadSig, 0x41615252); /* >>>> Leading signature */ >>>> + st_dword(fs->win + FSI_StrucSig, 0x61417272); /* >>>> Structure signature */ >>>> + st_dword(fs->win + FSI_Free_Count, fs->free_clst); >>>> /* Number of free clusters */ >>>> + st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); /* >>>> Last allocated culuster */ >>>> + fs->winsect = fs->volbase + 1; >>>> /* Write it into the FSInfo sector (Next to VBR) */ >>>> + disk_write(fs->pdrv, fs->win, fs->winsect, 1); >>>> + fs->fsi_flag = 0; >>>> + } >>>> + /* Make sure that no pending write process in the lower >>>> layer */ >>>> + if (disk_ioctl(fs->pdrv, CTRL_SYNC, 0) != RES_OK) res = >>>> FR_DISK_ERR; >>>> + } >>>> + >>>> + return res; >>>> +} >>>> + >>>> +#endif >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Get physical sector number from cluster >>>> number */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static LBA_t clst2sect ( /* !=0:Sector number, 0:Failed (invalid >>>> cluster#) */ >>>> + FATFS* fs, /* Filesystem object */ >>>> + DWORD clst /* Cluster# to be converted */ >>>> +) >>>> +{ >>>> + clst -= 2; /* Cluster number is origin from 2 */ >>>> + if (clst >= fs->n_fatent - 2) return 0; /* Is it invalid >>>> cluster number? */ >>>> + return fs->database + (LBA_t)fs->csize * clst; /* Start >>>> sector number of the cluster */ >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* FAT access - Read value of a FAT >>>> entry */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal >>>> error, 2..0x7FFFFFFF:Cluster status */ >>>> + FFOBJID* obj, /* Corresponding object */ >>>> + DWORD clst /* Cluster number to get the value */ >>>> +) >>>> +{ >>>> + UINT wc, bc; >>>> + DWORD val; >>>> + FATFS *fs = obj->fs; >>>> + >>>> + >>>> + if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid >>>> range */ >>>> + val = 1; /* Internal error */ >>>> + >>>> + } else { >>>> + val = 0xFFFFFFFF; /* Default value falls on disk error */ >>>> + >>>> + switch (fs->fs_type) { >>>> + case FS_FAT12 : >>>> + bc = (UINT)clst; bc += bc / 2; >>>> + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != >>>> FR_OK) break; >>>> + wc = fs->win[bc++ % SS(fs)]; /* Get 1st byte of >>>> the entry */ >>>> + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != >>>> FR_OK) break; >>>> + wc |= fs->win[bc % SS(fs)] << 8; /* Merge 2nd byte >>>> of the entry */ >>>> + val = (clst & 1) ? (wc >> 4) : (wc & 0xFFF); /* >>>> Adjust bit position */ >>>> + break; >>>> + >>>> + case FS_FAT16 : >>>> + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / >>>> 2))) != FR_OK) break; >>>> + val = ld_word(fs->win + clst * 2 % SS(fs)); /* >>>> Simple WORD array */ >>>> + break; >>>> + >>>> + case FS_FAT32 : >>>> + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / >>>> 4))) != FR_OK) break; >>>> + val = ld_dword(fs->win + clst * 4 % SS(fs)) & >>>> 0x0FFFFFFF; /* Simple DWORD array but mask out upper 4 bits */ >>>> + break; >>>> +#if FF_FS_EXFAT >>>> + case FS_EXFAT : >>>> + if ((obj->objsize != 0 && obj->sclust != 0) || >>>> obj->stat == 0) { /* Object except root dir must have valid data >>>> length */ >>>> + DWORD cofs = clst - obj->sclust; /* Offset from >>>> start cluster */ >>>> + DWORD clen = (DWORD)((LBA_t)((obj->objsize - 1) / >>>> SS(fs)) / fs->csize); /* Number of clusters - 1 */ >>>> + >>>> + if (obj->stat == 2 && cofs <= clen) { /* Is it a >>>> contiguous chain? */ >>>> + val = (cofs == clen) ? 0x7FFFFFFF : clst + 1; >>>> /* No data on the FAT, generate the value */ >>>> + break; >>>> + } >>>> + if (obj->stat == 3 && cofs < obj->n_cont) { /* >>>> Is it in the 1st fragment? */ >>>> + val = clst + 1; /* Generate the value */ >>>> + break; >>>> + } >>>> + if (obj->stat != 2) { /* Get value from FAT if >>>> FAT chain is valid */ >>>> + if (obj->n_frag != 0) { /* Is it on the >>>> growing edge? */ >>>> + val = 0x7FFFFFFF; /* Generate EOC */ >>>> + } else { >>>> + if (move_window(fs, fs->fatbase + (clst / >>>> (SS(fs) / 4))) != FR_OK) break; >>>> + val = ld_dword(fs->win + clst * 4 % SS(fs)) >>>> & 0x7FFFFFFF; >>>> + } >>>> + break; >>>> + } >>>> + } >>>> + val = 1; /* Internal error */ >>>> + break; >>>> +#endif >>>> + default: >>>> + val = 1; /* Internal error */ >>>> + } >>>> + } >>>> + >>>> + return val; >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +#if !FF_FS_READONLY >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* FAT access - Change value of a FAT >>>> entry */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ >>>> + FATFS* fs, /* Corresponding filesystem object */ >>>> + DWORD clst, /* FAT index number (cluster number) to be >>>> changed */ >>>> + DWORD val /* New value to be set to the entry */ >>>> +) >>>> +{ >>>> + UINT bc; >>>> + BYTE *p; >>>> + FRESULT res = FR_INT_ERR; >>>> + >>>> + >>>> + if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid >>>> range */ >>>> + switch (fs->fs_type) { >>>> + case FS_FAT12: >>>> + bc = (UINT)clst; bc += bc / 2; /* bc: byte offset of >>>> the entry */ >>>> + res = move_window(fs, fs->fatbase + (bc / SS(fs))); >>>> + if (res != FR_OK) break; >>>> + p = fs->win + bc++ % SS(fs); >>>> + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : >>>> (BYTE)val; /* Update 1st byte */ >>>> + fs->wflag = 1; >>>> + res = move_window(fs, fs->fatbase + (bc / SS(fs))); >>>> + if (res != FR_OK) break; >>>> + p = fs->win + bc % SS(fs); >>>> + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | >>>> ((BYTE)(val >> 8) & 0x0F)); /* Update 2nd byte */ >>>> + fs->wflag = 1; >>>> + break; >>>> + >>>> + case FS_FAT16: >>>> + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / >>>> 2))); >>>> + if (res != FR_OK) break; >>>> + st_word(fs->win + clst * 2 % SS(fs), (WORD)val); /* >>>> Simple WORD array */ >>>> + fs->wflag = 1; >>>> + break; >>>> + >>>> + case FS_FAT32: >>>> +#if FF_FS_EXFAT >>>> + case FS_EXFAT: >>>> +#endif >>>> + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / >>>> 4))); >>>> + if (res != FR_OK) break; >>>> + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { >>>> + val = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst >>>> * 4 % SS(fs)) & 0xF0000000); >>>> + } >>>> + st_dword(fs->win + clst * 4 % SS(fs), val); >>>> + fs->wflag = 1; >>>> + break; >>>> + } >>>> + } >>>> + return res; >>>> +} >>>> + >>>> +#endif /* !FF_FS_READONLY */ >>>> + >>>> + >>>> + >>>> + >>>> +#if FF_FS_EXFAT && !FF_FS_READONLY >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* exFAT: Accessing FAT and Allocation >>>> Bitmap */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +/*--------------------------------------*/ >>>> +/* Find a contiguous free cluster block */ >>>> +/*--------------------------------------*/ >>>> + >>>> +static DWORD find_bitmap ( /* 0:Not found, 2..:Cluster block >>>> found, 0xFFFFFFFF:Disk error */ >>>> + FATFS* fs, /* Filesystem object */ >>>> + DWORD clst, /* Cluster number to scan from */ >>>> + DWORD ncl /* Number of contiguous clusters to find (1..) */ >>>> +) >>>> +{ >>>> + BYTE bm, bv; >>>> + UINT i; >>>> + DWORD val, scl, ctr; >>>> + >>>> + >>>> + clst -= 2; /* The first bit in the bitmap corresponds to >>>> cluster #2 */ >>>> + if (clst >= fs->n_fatent - 2) clst = 0; >>>> + scl = val = clst; ctr = 0; >>>> + for (;;) { >>>> + if (move_window(fs, fs->bitbase + val / 8 / SS(fs)) != >>>> FR_OK) return 0xFFFFFFFF; >>>> + i = val / 8 % SS(fs); bm = 1 << (val % 8); >>>> + do { >>>> + do { >>>> + bv = fs->win[i] & bm; bm <<= 1; /* Get bit >>>> value */ >>>> + if (++val >= fs->n_fatent - 2) { /* Next cluster >>>> (with wrap-around) */ >>>> + val = 0; bm = 0; i = SS(fs); >>>> + } >>>> + if (bv == 0) { /* Is it a free cluster? */ >>>> + if (++ctr == ncl) return scl + 2; /* Check >>>> if run length is sufficient for required */ >>>> + } else { >>>> + scl = val; ctr = 0; /* Encountered a >>>> cluster in-use, restart to scan */ >>>> + } >>>> + if (val == clst) return 0; /* All cluster >>>> scanned? */ >>>> + } while (bm != 0); >>>> + bm = 1; >>>> + } while (++i < SS(fs)); >>>> + } >>>> +} >>>> + >>>> + >>>> +/*----------------------------------------*/ >>>> +/* Set/Clear a block of allocation bitmap */ >>>> +/*----------------------------------------*/ >>>> + >>>> +static FRESULT change_bitmap ( >>>> + FATFS* fs, /* Filesystem object */ >>>> + DWORD clst, /* Cluster number to change from */ >>>> + DWORD ncl, /* Number of clusters to be changed */ >>>> + int bv /* bit value to be set (0 or 1) */ >>>> +) >>>> +{ >>>> + BYTE bm; >>>> + UINT i; >>>> + LBA_t sect; >>>> + >>>> + >>>> + clst -= 2; /* The first bit corresponds to cluster #2 */ >>>> + sect = fs->bitbase + clst / 8 / SS(fs); /* Sector address */ >>>> + i = clst / 8 % SS(fs); /* Byte offset in the >>>> sector */ >>>> + bm = 1 << (clst % 8); /* Bit mask in the >>>> byte */ >>>> + for (;;) { >>>> + if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR; >>>> + do { >>>> + do { >>>> + if (bv == (int)((fs->win[i] & bm) != 0)) return >>>> FR_INT_ERR; /* Is the bit expected value? */ >>>> + fs->win[i] ^= bm; /* Flip the bit */ >>>> + fs->wflag = 1; >>>> + if (--ncl == 0) return FR_OK; /* All bits >>>> processed? */ >>>> + } while (bm <<= 1); /* Next bit */ >>>> + bm = 1; >>>> + } while (++i < SS(fs)); /* Next byte */ >>>> + i = 0; >>>> + } >>>> +} >>>> + >>>> + >>>> +/*---------------------------------------------*/ >>>> +/* Fill the first fragment of the FAT chain */ >>>> +/*---------------------------------------------*/ >>>> + >>>> +static FRESULT fill_first_frag ( >>>> + FFOBJID* obj /* Pointer to the corresponding object */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + DWORD cl, n; >>>> + >>>> + >>>> + if (obj->stat == 3) { /* Has the object been changed >>>> 'fragmented' in this session? */ >>>> + for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) { /* >>>> Create cluster chain on the FAT */ >>>> + res = put_fat(obj->fs, cl, cl + 1); >>>> + if (res != FR_OK) return res; >>>> + } >>>> + obj->stat = 0; /* Change status 'FAT chain is valid' */ >>>> + } >>>> + return FR_OK; >>>> +} >>>> + >>>> + >>>> +/*---------------------------------------------*/ >>>> +/* Fill the last fragment of the FAT chain */ >>>> +/*---------------------------------------------*/ >>>> + >>>> +static FRESULT fill_last_frag ( >>>> + FFOBJID* obj, /* Pointer to the corresponding object */ >>>> + DWORD lcl, /* Last cluster of the fragment */ >>>> + DWORD term /* Value to set the last FAT entry */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + >>>> + >>>> + while (obj->n_frag > 0) { /* Create the chain of last >>>> fragment */ >>>> + res = put_fat(obj->fs, lcl - obj->n_frag + 1, (obj->n_frag >>>> > 1) ? lcl - obj->n_frag + 2 : term); >>>> + if (res != FR_OK) return res; >>>> + obj->n_frag--; >>>> + } >>>> + return FR_OK; >>>> +} >>>> + >>>> +#endif /* FF_FS_EXFAT && !FF_FS_READONLY */ >>>> + >>>> + >>>> + >>>> +#if !FF_FS_READONLY >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* FAT handling - Remove a cluster >>>> chain */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ >>>> + FFOBJID* obj, /* Corresponding object */ >>>> + DWORD clst, /* Cluster to remove a chain from */ >>>> + DWORD pclst /* Previous cluster of clst (0 if entire >>>> chain) */ >>>> +) >>>> +{ >>>> + FRESULT res = FR_OK; >>>> + DWORD nxt; >>>> + FATFS *fs = obj->fs; >>>> +#if FF_FS_EXFAT || FF_USE_TRIM >>>> + DWORD scl = clst, ecl = clst; >>>> +#endif >>>> +#if FF_USE_TRIM >>>> + LBA_t rt[2]; >>>> +#endif >>>> + >>>> + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* >>>> Check if in valid range */ >>>> + >>>> + /* Mark the previous cluster 'EOC' on the FAT if it exists */ >>>> + if (pclst != 0 && (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT || >>>> obj->stat != 2)) { >>>> + res = put_fat(fs, pclst, 0xFFFFFFFF); >>>> + if (res != FR_OK) return res; >>>> + } >>>> + >>>> + /* Remove the chain */ >>>> + do { >>>> + nxt = get_fat(obj, clst); /* Get cluster status */ >>>> + if (nxt == 0) break; /* Empty cluster? */ >>>> + if (nxt == 1) return FR_INT_ERR; /* Internal error? */ >>>> + if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk >>>> error? */ >>>> + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { >>>> + res = put_fat(fs, clst, 0); /* Mark the cluster >>>> 'free' on the FAT */ >>>> + if (res != FR_OK) return res; >>>> + } >>>> + if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */ >>>> + fs->free_clst++; >>>> + fs->fsi_flag |= 1; >>>> + } >>>> +#if FF_FS_EXFAT || FF_USE_TRIM >>>> + if (ecl + 1 == nxt) { /* Is next cluster contiguous? */ >>>> + ecl = nxt; >>>> + } else { /* End of contiguous cluster block */ >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { >>>> + res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* >>>> Mark the cluster block 'free' on the bitmap */ >>>> + if (res != FR_OK) return res; >>>> + } >>>> +#endif >>>> +#if FF_USE_TRIM >>>> + rt[0] = clst2sect(fs, scl); /* Start >>>> of data area to be freed */ >>>> + rt[1] = clst2sect(fs, ecl) + fs->csize - 1; /* End >>>> of data area to be freed */ >>>> + disk_ioctl(fs->pdrv, CTRL_TRIM, rt); /* Inform >>>> storage device that the data in the block may be erased */ >>>> +#endif >>>> + scl = ecl = nxt; >>>> + } >>>> +#endif >>>> + clst = nxt; /* Next cluster */ >>>> + } while (clst < fs->n_fatent); /* Repeat while not the last >>>> link */ >>>> + >>>> +#if FF_FS_EXFAT >>>> + /* Some post processes for chain status */ >>>> + if (fs->fs_type == FS_EXFAT) { >>>> + if (pclst == 0) { /* Has the entire chain been removed? */ >>>> + obj->stat = 0; /* Change the chain status >>>> 'initial' */ >>>> + } else { >>>> + if (obj->stat == 0) { /* Is it a fragmented chain >>>> from the beginning of this session? */ >>>> + clst = obj->sclust; /* Follow the chain to >>>> check if it gets contiguous */ >>>> + while (clst != pclst) { >>>> + nxt = get_fat(obj, clst); >>>> + if (nxt < 2) return FR_INT_ERR; >>>> + if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; >>>> + if (nxt != clst + 1) break; /* Not >>>> contiguous? */ >>>> + clst++; >>>> + } >>>> + if (clst == pclst) { /* Has the chain got >>>> contiguous again? */ >>>> + obj->stat = 2; /* Change the chain >>>> status 'contiguous' */ >>>> + } >>>> + } else { >>>> + if (obj->stat == 3 && pclst >= obj->sclust && pclst >>>> <= obj->sclust + obj->n_cont) { /* Was the chain fragmented in >>>> this session and got contiguous again? */ >>>> + obj->stat = 2; /* Change the chain status >>>> 'contiguous' */ >>>> + } >>>> + } >>>> + } >>>> + } >>>> +#endif >>>> + return FR_OK; >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* FAT handling - Stretch a chain or Create a new >>>> chain */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static DWORD create_chain ( /* 0:No free cluster, 1:Internal >>>> error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ >>>> + FFOBJID* obj, /* Corresponding object */ >>>> + DWORD clst /* Cluster# to stretch, 0:Create a new >>>> chain */ >>>> +) >>>> +{ >>>> + DWORD cs, ncl, scl; >>>> + FRESULT res; >>>> + FATFS *fs = obj->fs; >>>> + >>>> + >>>> + if (clst == 0) { /* Create a new chain */ >>>> + scl = fs->last_clst; /* Suggested cluster to >>>> start to find */ >>>> + if (scl == 0 || scl >= fs->n_fatent) scl = 1; >>>> + } >>>> + else { /* Stretch a chain */ >>>> + cs = get_fat(obj, clst); /* Check the cluster >>>> status */ >>>> + if (cs < 2) return 1; /* Test for insanity */ >>>> + if (cs == 0xFFFFFFFF) return cs; /* Test for disk error */ >>>> + if (cs < fs->n_fatent) return cs; /* It is already >>>> followed by next cluster */ >>>> + scl = clst; /* Cluster to start >>>> to find */ >>>> + } >>>> + if (fs->free_clst == 0) return 0; /* No free cluster */ >>>> + >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ >>>> + ncl = find_bitmap(fs, scl, 1); /* Find a >>>> free cluster */ >>>> + if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl; /* No >>>> free cluster or hard error? */ >>>> + res = change_bitmap(fs, ncl, 1, 1); /* Mark the >>>> cluster 'in use' */ >>>> + if (res == FR_INT_ERR) return 1; >>>> + if (res == FR_DISK_ERR) return 0xFFFFFFFF; >>>> + if (clst == 0) { /* Is it a new >>>> chain? */ >>>> + obj->stat = 2; /* Set status >>>> 'contiguous' */ >>>> + } else { /* It is a >>>> stretched chain */ >>>> + if (obj->stat == 2 && ncl != scl + 1) { /* Is the >>>> chain got fragmented? */ >>>> + obj->n_cont = scl - obj->sclust; /* Set size of >>>> the contiguous part */ >>>> + obj->stat = 3; /* Change >>>> status 'just fragmented' */ >>>> + } >>>> + } >>>> + if (obj->stat != 2) { /* Is the file non-contiguous? */ >>>> + if (ncl == clst + 1) { /* Is the cluster next to >>>> previous one? */ >>>> + obj->n_frag = obj->n_frag ? obj->n_frag + 1 : 2; /* >>>> Increment size of last framgent */ >>>> + } else { /* New fragment */ >>>> + if (obj->n_frag == 0) obj->n_frag = 1; >>>> + res = fill_last_frag(obj, clst, ncl); /* Fill >>>> last fragment on the FAT and link it to new one */ >>>> + if (res == FR_OK) obj->n_frag = 1; >>>> + } >>>> + } >>>> + } else >>>> +#endif >>>> + { /* On the FAT/FAT32 volume */ >>>> + ncl = 0; >>>> + if (scl == clst) { /* Stretching an >>>> existing chain? */ >>>> + ncl = scl + 1; /* Test if next >>>> cluster is free */ >>>> + if (ncl >= fs->n_fatent) ncl = 2; >>>> + cs = get_fat(obj, ncl); /* Get next >>>> cluster status */ >>>> + if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* Test >>>> for error */ >>>> + if (cs != 0) { /* Not free? */ >>>> + cs = fs->last_clst; /* Start at >>>> suggested cluster if it is valid */ >>>> + if (cs >= 2 && cs < fs->n_fatent) scl = cs; >>>> + ncl = 0; >>>> + } >>>> + } >>>> + if (ncl == 0) { /* The new cluster cannot be contiguous >>>> and find another fragment */ >>>> + ncl = scl; /* Start cluster */ >>>> + for (;;) { >>>> + ncl++; /* Next cluster */ >>>> + if (ncl >= fs->n_fatent) { /* Check >>>> wrap-around */ >>>> + ncl = 2; >>>> + if (ncl > scl) return 0; /* No free cluster >>>> found? */ >>>> + } >>>> + cs = get_fat(obj, ncl); /* Get the >>>> cluster status */ >>>> + if (cs == 0) break; /* Found a free >>>> cluster? */ >>>> + if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* >>>> Test for error */ >>>> + if (ncl == scl) return 0; /* No free cluster >>>> found? */ >>>> + } >>>> + } >>>> + res = put_fat(fs, ncl, 0xFFFFFFFF); /* Mark the new >>>> cluster 'EOC' */ >>>> + if (res == FR_OK && clst != 0) { >>>> + res = put_fat(fs, clst, ncl); /* Link it from >>>> the previous one if needed */ >>>> + } >>>> + } >>>> + >>>> + if (res == FR_OK) { /* Update FSINFO if function >>>> succeeded. */ >>>> + fs->last_clst = ncl; >>>> + if (fs->free_clst <= fs->n_fatent - 2) fs->free_clst--; >>>> + fs->fsi_flag |= 1; >>>> + } else { >>>> + ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; /* Failed. >>>> Generate error status */ >>>> + } >>>> + >>>> + return ncl; /* Return new cluster number or error status */ >>>> +} >>>> + >>>> +#endif /* !FF_FS_READONLY */ >>>> + >>>> + >>>> + >>>> + >>>> +#if FF_USE_FASTSEEK >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* FAT handling - Convert offset into cluster with link map >>>> table */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ >>>> + FIL* fp, /* Pointer to the file object */ >>>> + FSIZE_t ofs /* File offset to be converted to cluster# */ >>>> +) >>>> +{ >>>> + DWORD cl, ncl, *tbl; >>>> + FATFS *fs = fp->obj.fs; >>>> + >>>> + >>>> + tbl = fp->cltbl + 1; /* Top of CLMT */ >>>> + cl = (DWORD)(ofs / SS(fs) / fs->csize); /* Cluster order >>>> from top of the file */ >>>> + for (;;) { >>>> + ncl = *tbl++; /* Number of cluters in the >>>> fragment */ >>>> + if (ncl == 0) return 0; /* End of table? (error) */ >>>> + if (cl < ncl) break; /* In this fragment? */ >>>> + cl -= ncl; tbl++; /* Next fragment */ >>>> + } >>>> + return cl + *tbl; /* Return the cluster number */ >>>> +} >>>> + >>>> +#endif /* FF_USE_FASTSEEK */ >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Directory handling - Fill a cluster with >>>> zeros */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +#if !FF_FS_READONLY >>>> +static FRESULT dir_clear ( /* Returns FR_OK or FR_DISK_ERR */ >>>> + FATFS *fs, /* Filesystem object */ >>>> + DWORD clst /* Directory table to clear */ >>>> +) >>>> +{ >>>> + LBA_t sect; >>>> + UINT n, szb; >>>> + BYTE *ibuf; >>>> + >>>> + >>>> + if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush >>>> disk access window */ >>>> + sect = clst2sect(fs, clst); /* Top of the cluster */ >>>> + fs->winsect = sect; /* Set window to top of the >>>> cluster */ >>>> + mem_set(fs->win, 0, sizeof fs->win); /* Clear window buffer */ >>>> +#if FF_USE_LFN == 3 /* Quick table clear by using >>>> multi-secter write */ >>>> + /* Allocate a temporary buffer */ >>>> + for (szb = ((DWORD)fs->csize * SS(fs) >= MAX_MALLOC) ? >>>> MAX_MALLOC : fs->csize * SS(fs), ibuf = 0; szb > SS(fs) && (ibuf = >>>> ff_memalloc(szb)) == 0; szb /= 2) ; >>>> + if (szb > SS(fs)) { /* Buffer allocated? */ >>>> + mem_set(ibuf, 0, szb); >>>> + szb /= SS(fs); /* Bytes -> Sectors */ >>>> + for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, >>>> sect + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */ >>>> + ff_memfree(ibuf); >>>> + } else >>>> +#endif >>>> + { >>>> + ibuf = fs->win; szb = 1; /* Use window buffer (many >>>> single-sector writes may take a time) */ >>>> + for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, >>>> sect + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */ >>>> + } >>>> + return (n == fs->csize) ? FR_OK : FR_DISK_ERR; >>>> +} >>>> +#endif /* !FF_FS_READONLY */ >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Directory handling - Set directory >>>> index */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */ >>>> + DIR* dp, /* Pointer to directory object */ >>>> + DWORD ofs /* Offset of directory table */ >>>> +) >>>> +{ >>>> + DWORD csz, clst; >>>> + FATFS *fs = dp->obj.fs; >>>> + >>>> + >>>> + if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? >>>> MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) { /* Check range of offset >>>> and alignment */ >>>> + return FR_INT_ERR; >>>> + } >>>> + dp->dptr = ofs; /* Set current offset */ >>>> + clst = dp->obj.sclust; /* Table start cluster (0:root) */ >>>> + if (clst == 0 && fs->fs_type >= FS_FAT32) { /* Replace >>>> cluster# 0 with root cluster# */ >>>> + clst = (DWORD)fs->dirbase; >>>> + if (FF_FS_EXFAT) dp->obj.stat = 0; /* exFAT: Root dir >>>> has an FAT chain */ >>>> + } >>>> + >>>> + if (clst == 0) { /* Static table (root-directory on the FAT >>>> volume) */ >>>> + if (ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR; /* >>>> Is index out of range? */ >>>> + dp->sect = fs->dirbase; >>>> + >>>> + } else { /* Dynamic table (sub-directory or >>>> root-directory on the FAT32/exFAT volume) */ >>>> + csz = (DWORD)fs->csize * SS(fs); /* Bytes per cluster */ >>>> + while (ofs >= csz) { /* Follow cluster chain */ >>>> + clst = get_fat(&dp->obj, clst); /* Get >>>> next cluster */ >>>> + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk >>>> error */ >>>> + if (clst < 2 || clst >= fs->n_fatent) return >>>> FR_INT_ERR; /* Reached to end of table or internal error */ >>>> + ofs -= csz; >>>> + } >>>> + dp->sect = clst2sect(fs, clst); >>>> + } >>>> + dp->clust = clst; /* Current cluster# */ >>>> + if (dp->sect == 0) return FR_INT_ERR; >>>> + dp->sect += ofs / SS(fs); /* Sector# of the >>>> directory entry */ >>>> + dp->dir = fs->win + (ofs % SS(fs)); /* Pointer to the entry >>>> in the win[] */ >>>> + >>>> + return FR_OK; >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Directory handling - Move directory table index >>>> next */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End >>>> of table, FR_DENIED:Could not stretch */ >>>> + DIR* dp, /* Pointer to the directory object */ >>>> + int stretch /* 0: Do not stretch table, 1: >>>> Stretch table if needed */ >>>> +) >>>> +{ >>>> + DWORD ofs, clst; >>>> + FATFS *fs = dp->obj.fs; >>>> + >>>> + >>>> + ofs = dp->dptr + SZDIRE; /* Next entry */ >>>> + if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? >>>> MAX_DIR_EX : MAX_DIR)) dp->sect = 0; /* Disable it if the offset >>>> reached the max value */ >>>> + if (dp->sect == 0) return FR_NO_FILE; /* Report EOT if it >>>> has been disabled */ >>>> + >>>> + if (ofs % SS(fs) == 0) { /* Sector changed? */ >>>> + dp->sect++; /* Next sector */ >>>> + >>>> + if (dp->clust == 0) { /* Static table */ >>>> + if (ofs / SZDIRE >= fs->n_rootdir) { /* Report EOT >>>> if it reached end of static table */ >>>> + dp->sect = 0; return FR_NO_FILE; >>>> + } >>>> + } >>>> + else { /* Dynamic table */ >>>> + if ((ofs / SS(fs) & (fs->csize - 1)) == 0) { /* >>>> Cluster changed? */ >>>> + clst = get_fat(&dp->obj, dp->clust); /* Get >>>> next cluster */ >>>> + if (clst <= 1) return FR_INT_ERR; /* >>>> Internal error */ >>>> + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* >>>> Disk error */ >>>> + if (clst >= fs->n_fatent) { /* >>>> It reached end of dynamic table */ >>>> +#if !FF_FS_READONLY >>>> + if (!stretch) { /* If no stretch, report EOT */ >>>> + dp->sect = 0; return FR_NO_FILE; >>>> + } >>>> + clst = create_chain(&dp->obj, dp->clust); /* >>>> Allocate a cluster */ >>>> + if (clst == 0) return FR_DENIED; /* >>>> No free cluster */ >>>> + if (clst == 1) return FR_INT_ERR; /* >>>> Internal error */ >>>> + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* >>>> Disk error */ >>>> + if (dir_clear(fs, clst) != FR_OK) return >>>> FR_DISK_ERR; /* Clean up the stretched table */ >>>> + if (FF_FS_EXFAT) dp->obj.stat |= 4; /* exFAT: >>>> The directory has been stretched */ >>>> +#else >>>> + if (!stretch) dp->sect = 0; /* (this line is to >>>> suppress compiler warning) */ >>>> + dp->sect = 0; return FR_NO_FILE; /* >>>> Report EOT */ >>>> +#endif >>>> + } >>>> + dp->clust = clst; /* Initialize data for new >>>> cluster */ >>>> + dp->sect = clst2sect(fs, clst); >>>> + } >>>> + } >>>> + } >>>> + dp->dptr = ofs; /* Current entry */ >>>> + dp->dir = fs->win + ofs % SS(fs); /* Pointer to the entry in >>>> the win[] */ >>>> + >>>> + return FR_OK; >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +#if !FF_FS_READONLY >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Directory handling - Reserve a block of directory >>>> entries */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ >>>> + DIR* dp, /* Pointer to the directory object */ >>>> + UINT n_ent /* Number of contiguous entries to >>>> allocate */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + UINT n; >>>> + FATFS *fs = dp->obj.fs; >>>> + >>>> + >>>> + res = dir_sdi(dp, 0); >>>> + if (res == FR_OK) { >>>> + n = 0; >>>> + do { >>>> + res = move_window(fs, dp->sect); >>>> + if (res != FR_OK) break; >>>> +#if FF_FS_EXFAT >>>> + if ((fs->fs_type == FS_EXFAT) ? >>>> (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == >>>> DDEM || dp->dir[DIR_Name] == 0)) { /* Is the entry free? */ >>>> +#else >>>> + if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == >>>> 0) { /* Is the entry free? */ >>>> +#endif >>>> + if (++n == n_ent) break; /* Is a block of >>>> contiguous free entries found? */ >>>> + } else { >>>> + n = 0; /* Not a free entry, restart >>>> to search */ >>>> + } >>>> + res = dir_next(dp, 1); /* Next entry with table >>>> stretch enabled */ >>>> + } while (res == FR_OK); >>>> + } >>>> + >>>> + if (res == FR_NO_FILE) res = FR_DENIED; /* No directory >>>> entry to allocate */ >>>> + return res; >>>> +} >>>> + >>>> +#endif /* !FF_FS_READONLY */ >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* FAT: Directory handling - Load/Store start cluster >>>> number */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static DWORD ld_clust ( /* Returns the top cluster value of the >>>> SFN entry */ >>>> + FATFS* fs, /* Pointer to the fs object */ >>>> + const BYTE* dir /* Pointer to the key entry */ >>>> +) >>>> +{ >>>> + DWORD cl; >>>> + >>>> + cl = ld_word(dir + DIR_FstClusLO); >>>> + if (fs->fs_type == FS_FAT32) { >>>> + cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16; >>>> + } >>>> + >>>> + return cl; >>>> +} >>>> + >>>> + >>>> +#if !FF_FS_READONLY >>>> +static void st_clust ( >>>> + FATFS* fs, /* Pointer to the fs object */ >>>> + BYTE* dir, /* Pointer to the key entry */ >>>> + DWORD cl /* Value to be set */ >>>> +) >>>> +{ >>>> + st_word(dir + DIR_FstClusLO, (WORD)cl); >>>> + if (fs->fs_type == FS_FAT32) { >>>> + st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16)); >>>> + } >>>> +} >>>> +#endif >>>> + >>>> + >>>> + >>>> +#if FF_USE_LFN >>>> +/*--------------------------------------------------------*/ >>>> +/* FAT-LFN: Compare a part of file name with an LFN entry */ >>>> +/*--------------------------------------------------------*/ >>>> + >>>> +static int cmp_lfn ( /* 1:matched, 0:not matched */ >>>> + const WCHAR* lfnbuf, /* Pointer to the LFN working buffer to >>>> be compared */ >>>> + BYTE* dir /* Pointer to the directory entry >>>> containing the part of LFN */ >>>> +) >>>> +{ >>>> + UINT i, s; >>>> + WCHAR wc, uc; >>>> + >>>> + >>>> + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check >>>> LDIR_FstClusLO */ >>>> + >>>> + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN >>>> buffer */ >>>> + >>>> + for (wc = 1, s = 0; s < 13; s++) { /* Process all >>>> characters in the entry */ >>>> + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN >>>> character */ >>>> + if (wc != 0) { >>>> + if (i >= FF_MAX_LFN + 1 || ff_wtoupper(uc) != >>>> ff_wtoupper(lfnbuf[i++])) { /* Compare it */ >>>> + return 0; /* Not matched */ >>>> + } >>>> + wc = uc; >>>> + } else { >>>> + if (uc != 0xFFFF) return 0; /* Check filler */ >>>> + } >>>> + } >>>> + >>>> + if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) return 0; /* >>>> Last segment matched but different length */ >>>> + >>>> + return 1; /* The part of LFN matched */ >>>> +} >>>> + >>>> + >>>> +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || >>>> FF_FS_EXFAT >>>> +/*-----------------------------------------------------*/ >>>> +/* FAT-LFN: Pick a part of file name from an LFN entry */ >>>> +/*-----------------------------------------------------*/ >>>> + >>>> +static int pick_lfn ( /* 1:succeeded, 0:buffer overflow or >>>> invalid LFN entry */ >>>> + WCHAR* lfnbuf, /* Pointer to the LFN working buffer */ >>>> + BYTE* dir /* Pointer to the LFN entry */ >>>> +) >>>> +{ >>>> + UINT i, s; >>>> + WCHAR wc, uc; >>>> + >>>> + >>>> + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check >>>> LDIR_FstClusLO is 0 */ >>>> + >>>> + i = ((dir[LDIR_Ord] & ~LLEF) - 1) * 13; /* Offset in the LFN >>>> buffer */ >>>> + >>>> + for (wc = 1, s = 0; s < 13; s++) { /* Process all >>>> characters in the entry */ >>>> + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN >>>> character */ >>>> + if (wc != 0) { >>>> + if (i >= FF_MAX_LFN + 1) return 0; /* Buffer >>>> overflow? */ >>>> + lfnbuf[i++] = wc = uc; /* Store it */ >>>> + } else { >>>> + if (uc != 0xFFFF) return 0; /* Check filler */ >>>> + } >>>> + } >>>> + >>>> + if (dir[LDIR_Ord] & LLEF && wc != 0) { /* Put terminator if >>>> it is the last LFN part and not terminated */ >>>> + if (i >= FF_MAX_LFN + 1) return 0; /* Buffer overflow? */ >>>> + lfnbuf[i] = 0; >>>> + } >>>> + >>>> + return 1; /* The part of LFN is valid */ >>>> +} >>>> +#endif >>>> + >>>> + >>>> +#if !FF_FS_READONLY >>>> +/*-----------------------------------------*/ >>>> +/* FAT-LFN: Create an entry of LFN entries */ >>>> +/*-----------------------------------------*/ >>>> + >>>> +static void put_lfn ( >>>> + const WCHAR* lfn, /* Pointer to the LFN */ >>>> + BYTE* dir, /* Pointer to the LFN entry to be created */ >>>> + BYTE ord, /* LFN order (1-20) */ >>>> + BYTE sum /* Checksum of the corresponding SFN */ >>>> +) >>>> +{ >>>> + UINT i, s; >>>> + WCHAR wc; >>>> + >>>> + >>>> + dir[LDIR_Chksum] = sum; /* Set checksum */ >>>> + dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ >>>> + dir[LDIR_Type] = 0; >>>> + st_word(dir + LDIR_FstClusLO, 0); >>>> + >>>> + i = (ord - 1) * 13; /* Get offset in the LFN >>>> working buffer */ >>>> + s = wc = 0; >>>> + do { >>>> + if (wc != 0xFFFF) wc = lfn[i++]; /* Get an effective >>>> character */ >>>> + st_word(dir + LfnOfs[s], wc); /* Put it */ >>>> + if (wc == 0) wc = 0xFFFF; /* Padding characters for >>>> following items */ >>>> + } while (++s < 13); >>>> + if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part >>>> is the start of LFN sequence */ >>>> + dir[LDIR_Ord] = ord; /* Set the LFN order */ >>>> +} >>>> + >>>> +#endif /* !FF_FS_READONLY */ >>>> +#endif /* FF_USE_LFN */ >>>> + >>>> + >>>> + >>>> +#if FF_USE_LFN && !FF_FS_READONLY >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* FAT-LFN: Create a Numbered >>>> SFN */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static void gen_numname ( >>>> + BYTE* dst, /* Pointer to the buffer to store >>>> numbered SFN */ >>>> + const BYTE* src, /* Pointer to SFN */ >>>> + const WCHAR* lfn, /* Pointer to LFN */ >>>> + UINT seq /* Sequence number */ >>>> +) >>>> +{ >>>> + BYTE ns[8], c; >>>> + UINT i, j; >>>> + WCHAR wc; >>>> + DWORD sreg; >>>> + >>>> + >>>> + mem_cpy(dst, src, 11); >>>> + >>>> + if (seq > 5) { /* In case of many collisions, generate a >>>> hash number instead of sequential number */ >>>> + sreg = seq; >>>> + while (*lfn) { /* Create a CRC as hash value */ >>>> + wc = *lfn++; >>>> + for (i = 0; i < 16; i++) { >>>> + sreg = (sreg << 1) + (wc & 1); >>>> + wc >>= 1; >>>> + if (sreg & 0x10000) sreg ^= 0x11021; >>>> + } >>>> + } >>>> + seq = (UINT)sreg; >>>> + } >>>> + >>>> + /* itoa (hexdecimal) */ >>>> + i = 7; >>>> + do { >>>> + c = (BYTE)((seq % 16) + '0'); >>>> + if (c > '9') c += 7; >>>> + ns[i--] = c; >>>> + seq /= 16; >>>> + } while (seq); >>>> + ns[i] = '~'; >>>> + >>>> + /* Append the number to the SFN body */ >>>> + for (j = 0; j < i && dst[j] != ' '; j++) { >>>> + if (dbc_1st(dst[j])) { >>>> + if (j == i - 1) break; >>>> + j++; >>>> + } >>>> + } >>>> + do { >>>> + dst[j++] = (i < 8) ? ns[i++] : ' '; >>>> + } while (j < 8); >>>> +} >>>> +#endif /* FF_USE_LFN && !FF_FS_READONLY */ >>>> + >>>> + >>>> + >>>> +#if FF_USE_LFN >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* FAT-LFN: Calculate checksum of an SFN >>>> entry */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static BYTE sum_sfn ( >>>> + const BYTE* dir /* Pointer to the SFN entry */ >>>> +) >>>> +{ >>>> + BYTE sum = 0; >>>> + UINT n = 11; >>>> + >>>> + do { >>>> + sum = (sum >> 1) + (sum << 7) + *dir++; >>>> + } while (--n); >>>> + return sum; >>>> +} >>>> + >>>> +#endif /* FF_USE_LFN */ >>>> + >>>> + >>>> + >>>> +#if FF_FS_EXFAT >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* exFAT: >>>> Checksum */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static WORD xdir_sum ( /* Get checksum of the directoly entry >>>> block */ >>>> + const BYTE* dir /* Directory entry block to be >>>> calculated */ >>>> +) >>>> +{ >>>> + UINT i, szblk; >>>> + WORD sum; >>>> + >>>> + >>>> + szblk = (dir[XDIR_NumSec] + 1) * SZDIRE; /* Number of bytes >>>> of the entry block */ >>>> + for (i = sum = 0; i < szblk; i++) { >>>> + if (i == XDIR_SetSum) { /* Skip 2-byte sum field */ >>>> + i++; >>>> + } else { >>>> + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i]; >>>> + } >>>> + } >>>> + return sum; >>>> +} >>>> + >>>> + >>>> + >>>> +static WORD xname_sum ( /* Get check sum (to be used as hash) of >>>> the file name */ >>>> + const WCHAR* name /* File name to be calculated */ >>>> +) >>>> +{ >>>> + WCHAR chr; >>>> + WORD sum = 0; >>>> + >>>> + >>>> + while ((chr = *name++) != 0) { >>>> + chr = (WCHAR)ff_wtoupper(chr); /* File name needs to >>>> be up-case converted */ >>>> + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF); >>>> + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8); >>>> + } >>>> + return sum; >>>> +} >>>> + >>>> + >>>> +#if !FF_FS_READONLY && FF_USE_MKFS >>>> +static DWORD xsum32 ( /* Returns 32-bit checksum */ >>>> + BYTE dat, /* Byte to be calculated (byte-by-byte >>>> processing) */ >>>> + DWORD sum /* Previous sum value */ >>>> +) >>>> +{ >>>> + sum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat; >>>> + return sum; >>>> +} >>>> +#endif >>>> + >>>> + >>>> +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 >>>> +/*------------------------------------------------------*/ >>>> +/* exFAT: Get object information from a directory block */ >>>> +/*------------------------------------------------------*/ >>>> + >>>> +static void get_xfileinfo ( >>>> + BYTE* dirb, /* Pointer to the direcotry entry block >>>> 85+C0+C1s */ >>>> + FILINFO* fno /* Buffer to store the extracted file >>>> information */ >>>> +) >>>> +{ >>>> + WCHAR wc, hs; >>>> + UINT di, si, nc; >>>> + >>>> + /* Get file name from the entry block */ >>>> + si = SZDIRE * 2; /* 1st C1 entry */ >>>> + nc = 0; hs = 0; di = 0; >>>> + while (nc < dirb[XDIR_NumName]) { >>>> + if (si >= MAXDIRB(FF_MAX_LFN)) { di = 0; break; } /* >>>> Truncated directory block? */ >>>> + if ((si % SZDIRE) == 0) si += 2; /* Skip entry type >>>> field */ >>>> + wc = ld_word(dirb + si); si += 2; nc++; /* Get a >>>> character */ >>>> + if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ >>>> + hs = wc; continue; /* Get low surrogate */ >>>> + } >>>> + wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], >>>> FF_LFN_BUF - di); /* Store it in API encoding */ >>>> + if (wc == 0) { di = 0; break; } /* Buffer overflow or >>>> wrong encoding? */ >>>> + di += wc; >>>> + hs = 0; >>>> + } >>>> + if (hs != 0) di = 0; /* Broken surrogate >>>> pair? */ >>>> + if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object >>>> name? */ >>>> + fno->fname[di] = 0; /* Terminate the >>>> name */ >>>> + fno->altname[0] = 0; /* exFAT does not >>>> support SFN */ >>>> + >>>> + fno->fattrib = dirb[XDIR_Attr]; /* Attribute */ >>>> + fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + >>>> XDIR_FileSize); /* Size */ >>>> + fno->ftime = ld_word(dirb + XDIR_ModTime + 0); /* Time */ >>>> + fno->fdate = ld_word(dirb + XDIR_ModTime + 2); /* Date */ >>>> +} >>>> + >>>> +#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */ >>>> + >>>> + >>>> +/*-----------------------------------*/ >>>> +/* exFAT: Get a directry entry block */ >>>> +/*-----------------------------------*/ >>>> + >>>> +static FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ >>>> + DIR* dp /* Reading direcotry object pointing >>>> top of the entry block to load */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + UINT i, sz_ent; >>>> + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory >>>> direcotry entry block 85+C0+C1s */ >>>> + >>>> + >>>> + /* Load file-directory entry */ >>>> + res = move_window(dp->obj.fs, dp->sect); >>>> + if (res != FR_OK) return res; >>>> + if (dp->dir[XDIR_Type] != ET_FILEDIR) return FR_INT_ERR; /* >>>> Invalid order */ >>>> + mem_cpy(dirb + 0 * SZDIRE, dp->dir, SZDIRE); >>>> + sz_ent = (dirb[XDIR_NumSec] + 1) * SZDIRE; >>>> + if (sz_ent < 3 * SZDIRE || sz_ent > 19 * SZDIRE) return >>>> FR_INT_ERR; >>>> + >>>> + /* Load stream-extension entry */ >>>> + res = dir_next(dp, 0); >>>> + if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */ >>>> + if (res != FR_OK) return res; >>>> + res = move_window(dp->obj.fs, dp->sect); >>>> + if (res != FR_OK) return res; >>>> + if (dp->dir[XDIR_Type] != ET_STREAM) return FR_INT_ERR; /* >>>> Invalid order */ >>>> + mem_cpy(dirb + 1 * SZDIRE, dp->dir, SZDIRE); >>>> + if (MAXDIRB(dirb[XDIR_NumName]) > sz_ent) return FR_INT_ERR; >>>> + >>>> + /* Load file-name entries */ >>>> + i = 2 * SZDIRE; /* Name offset to load */ >>>> + do { >>>> + res = dir_next(dp, 0); >>>> + if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */ >>>> + if (res != FR_OK) return res; >>>> + res = move_window(dp->obj.fs, dp->sect); >>>> + if (res != FR_OK) return res; >>>> + if (dp->dir[XDIR_Type] != ET_FILENAME) return FR_INT_ERR; >>>> /* Invalid order */ >>>> + if (i < MAXDIRB(FF_MAX_LFN)) mem_cpy(dirb + i, dp->dir, >>>> SZDIRE); >>>> + } while ((i += SZDIRE) < sz_ent); >>>> + >>>> + /* Sanity check (do it for only accessible object) */ >>>> + if (i <= MAXDIRB(FF_MAX_LFN)) { >>>> + if (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return >>>> FR_INT_ERR; >>>> + } >>>> + return FR_OK; >>>> +} >>>> + >>>> + >>>> +/*------------------------------------------------------------------*/ >>>> +/* exFAT: Initialize object allocation info with loaded entry block */ >>>> +/*------------------------------------------------------------------*/ >>>> + >>>> +static void init_alloc_info ( >>>> + FATFS* fs, /* Filesystem object */ >>>> + FFOBJID* obj /* Object allocation information to be >>>> initialized */ >>>> +) >>>> +{ >>>> + obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* >>>> Start cluster */ >>>> + obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); /* Size */ >>>> + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; /* >>>> Allocation status */ >>>> + obj->n_frag = 0; /* No >>>> last fragment info */ >>>> +} >>>> + >>>> + >>>> + >>>> +#if !FF_FS_READONLY || FF_FS_RPATH != 0 >>>> +/*------------------------------------------------*/ >>>> +/* exFAT: Load the object's directory entry block */ >>>> +/*------------------------------------------------*/ >>>> + >>>> +static FRESULT load_obj_xdir ( >>>> + DIR* dp, /* Blank directory object to be used to >>>> access containing direcotry */ >>>> + const FFOBJID* obj /* Object with its containing directory >>>> information */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + >>>> + /* Open object containing directory */ >>>> + dp->obj.fs = obj->fs; >>>> + dp->obj.sclust = obj->c_scl; >>>> + dp->obj.stat = (BYTE)obj->c_size; >>>> + dp->obj.objsize = obj->c_size & 0xFFFFFF00; >>>> + dp->obj.n_frag = 0; >>>> + dp->blk_ofs = obj->c_ofs; >>>> + >>>> + res = dir_sdi(dp, dp->blk_ofs); /* Goto object's entry block */ >>>> + if (res == FR_OK) { >>>> + res = load_xdir(dp); /* Load the object's entry >>>> block */ >>>> + } >>>> + return res; >>>> +} >>>> +#endif >>>> + >>>> + >>>> +#if !FF_FS_READONLY >>>> +/*----------------------------------------*/ >>>> +/* exFAT: Store the directory entry block */ >>>> +/*----------------------------------------*/ >>>> + >>>> +static FRESULT store_xdir ( >>>> + DIR* dp /* Pointer to the direcotry object */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + UINT nent; >>>> + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry >>>> entry block 85+C0+C1s */ >>>> + >>>> + /* Create set sum */ >>>> + st_word(dirb + XDIR_SetSum, xdir_sum(dirb)); >>>> + nent = dirb[XDIR_NumSec] + 1; >>>> + >>>> + /* Store the direcotry entry block to the directory */ >>>> + res = dir_sdi(dp, dp->blk_ofs); >>>> + while (res == FR_OK) { >>>> + res = move_window(dp->obj.fs, dp->sect); >>>> + if (res != FR_OK) break; >>>> + mem_cpy(dp->dir, dirb, SZDIRE); >>>> + dp->obj.fs->wflag = 1; >>>> + if (--nent == 0) break; >>>> + dirb += SZDIRE; >>>> + res = dir_next(dp, 0); >>>> + } >>>> + return (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR; >>>> +} >>>> + >>>> + >>>> + >>>> +/*-------------------------------------------*/ >>>> +/* exFAT: Create a new directory enrty block */ >>>> +/*-------------------------------------------*/ >>>> + >>>> +static void create_xdir ( >>>> + BYTE* dirb, /* Pointer to the direcotry entry block >>>> buffer */ >>>> + const WCHAR* lfn /* Pointer to the object name */ >>>> +) >>>> +{ >>>> + UINT i; >>>> + BYTE nc1, nlen; >>>> + WCHAR wc; >>>> + >>>> + >>>> + /* Create file-directory and stream-extension entry */ >>>> + mem_set(dirb, 0, 2 * SZDIRE); >>>> + dirb[0 * SZDIRE + XDIR_Type] = ET_FILEDIR; >>>> + dirb[1 * SZDIRE + XDIR_Type] = ET_STREAM; >>>> + >>>> + /* Create file-name entries */ >>>> + i = SZDIRE * 2; /* Top of file_name entries */ >>>> + nlen = nc1 = 0; wc = 1; >>>> + do { >>>> + dirb[i++] = ET_FILENAME; dirb[i++] = 0; >>>> + do { /* Fill name field */ >>>> + if (wc != 0 && (wc = lfn[nlen]) != 0) nlen++; /* Get >>>> a character if exist */ >>>> + st_word(dirb + i, wc); /* Store it */ >>>> + i += 2; >>>> + } while (i % SZDIRE != 0); >>>> + nc1++; >>>> + } while (lfn[nlen]); /* Fill next entry if any char follows */ >>>> + >>>> + dirb[XDIR_NumName] = nlen; /* Set name length */ >>>> + dirb[XDIR_NumSec] = 1 + nc1; /* Set secondary count (C0 + >>>> C1s) */ >>>> + st_word(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name >>>> hash */ >>>> +} >>>> + >>>> +#endif /* !FF_FS_READONLY */ >>>> +#endif /* FF_FS_EXFAT */ >>>> + >>>> + >>>> + >>>> +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || >>>> FF_FS_EXFAT >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Read an object from the >>>> directory */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +#define DIR_READ_FILE(dp) dir_read(dp, 0) >>>> +#define DIR_READ_LABEL(dp) dir_read(dp, 1) >>>> + >>>> +static FRESULT dir_read ( >>>> + DIR* dp, /* Pointer to the directory object */ >>>> + int vol /* Filtered by 0:file/directory or 1:volume >>>> label */ >>>> +) >>>> +{ >>>> + FRESULT res = FR_NO_FILE; >>>> + FATFS *fs = dp->obj.fs; >>>> + BYTE attr, b; >>>> +#if FF_USE_LFN >>>> + BYTE ord = 0xFF, sum = 0xFF; >>>> +#endif >>>> + >>>> + while (dp->sect) { >>>> + res = move_window(fs, dp->sect); >>>> + if (res != FR_OK) break; >>>> + b = dp->dir[DIR_Name]; /* Test for the entry type */ >>>> + if (b == 0) { >>>> + res = FR_NO_FILE; break; /* Reached to end of the >>>> directory */ >>>> + } >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ >>>> + if (FF_USE_LABEL && vol) { >>>> + if (b == ET_VLABEL) break; /* Volume label >>>> entry? */ >>>> + } else { >>>> + if (b == ET_FILEDIR) { /* Start of the file >>>> entry block? */ >>>> + dp->blk_ofs = dp->dptr; /* Get location of >>>> the block */ >>>> + res = load_xdir(dp); /* Load the entry block */ >>>> + if (res == FR_OK) { >>>> + dp->obj.attr = fs->dirbuf[XDIR_Attr] & >>>> AM_MASK; /* Get attribute */ >>>> + } >>>> + break; >>>> + } >>>> + } >>>> + } else >>>> +#endif >>>> + { /* On the FAT/FAT32 volume */ >>>> + dp->obj.attr = attr = dp->dir[DIR_Attr] & AM_MASK; >>>> /* Get attribute */ >>>> +#if FF_USE_LFN /* LFN configuration */ >>>> + if (b == DDEM || b == '.' || (int)((attr & ~AM_ARC) == >>>> AM_VOL) != vol) { /* An entry without valid data */ >>>> + ord = 0xFF; >>>> + } else { >>>> + if (attr == AM_LFN) { /* An LFN entry is >>>> found */ >>>> + if (b & LLEF) { /* Is it start of an >>>> LFN sequence? */ >>>> + sum = dp->dir[LDIR_Chksum]; >>>> + b &= (BYTE)~LLEF; ord = b; >>>> + dp->blk_ofs = dp->dptr; >>>> + } >>>> + /* Check LFN validity and capture it */ >>>> + ord = (b == ord && sum == dp->dir[LDIR_Chksum] >>>> && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; >>>> + } else { /* An SFN entry is >>>> found */ >>>> + if (ord != 0 || sum != sum_sfn(dp->dir)) { >>>> /* Is there a valid LFN? */ >>>> + dp->blk_ofs = 0xFFFFFFFF; /* It >>>> has no LFN. */ >>>> + } >>>> + break; >>>> + } >>>> + } >>>> +#else /* Non LFN configuration */ >>>> + if (b != DDEM && b != '.' && attr != AM_LFN && >>>> (int)((attr & ~AM_ARC) == AM_VOL) == vol) { /* Is it a valid >>>> entry? */ >>>> + break; >>>> + } >>>> +#endif >>>> + } >>>> + res = dir_next(dp, 0); /* Next entry */ >>>> + if (res != FR_OK) break; >>>> + } >>>> + >>>> + if (res != FR_OK) dp->sect = 0; /* Terminate the read >>>> operation on error or EOT */ >>>> + return res; >>>> +} >>>> + >>>> +#endif /* FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_RPATH >= >>>> 2 */ >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Directory handling - Find an object in the >>>> directory */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ >>>> + DIR* dp /* Pointer to the directory object >>>> with the file name */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + FATFS *fs = dp->obj.fs; >>>> + BYTE c; >>>> +#if FF_USE_LFN >>>> + BYTE a, ord, sum; >>>> +#endif >>>> + >>>> + res = dir_sdi(dp, 0); /* Rewind directory object */ >>>> + if (res != FR_OK) return res; >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ >>>> + BYTE nc; >>>> + UINT di, ni; >>>> + WORD hash = xname_sum(fs->lfnbuf); /* Hash value of >>>> the name to find */ >>>> + >>>> + while ((res = DIR_READ_FILE(dp)) == FR_OK) { /* Read an >>>> item */ >>>> +#if FF_MAX_LFN < 255 >>>> + if (fs->dirbuf[XDIR_NumName] > FF_MAX_LFN) >>>> continue; /* Skip comparison if inaccessible object name */ >>>> +#endif >>>> + if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) >>>> continue; /* Skip comparison if hash mismatched */ >>>> + for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni >>>> = 0; nc; nc--, di += 2, ni++) { /* Compare the name */ >>>> + if ((di % SZDIRE) == 0) di += 2; >>>> + if (ff_wtoupper(ld_word(fs->dirbuf + di)) != >>>> ff_wtoupper(fs->lfnbuf[ni])) break; >>>> + } >>>> + if (nc == 0 && !fs->lfnbuf[ni]) break; /* Name >>>> matched? */ >>>> + } >>>> + return res; >>>> + } >>>> +#endif >>>> + /* On the FAT/FAT32 volume */ >>>> +#if FF_USE_LFN >>>> + ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN >>>> sequence */ >>>> +#endif >>>> + do { >>>> + res = move_window(fs, dp->sect); >>>> + if (res != FR_OK) break; >>>> + c = dp->dir[DIR_Name]; >>>> + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to >>>> end of table */ >>>> +#if FF_USE_LFN /* LFN configuration */ >>>> + dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; >>>> + if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An >>>> entry without valid data */ >>>> + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN >>>> sequence */ >>>> + } else { >>>> + if (a == AM_LFN) { /* An LFN entry is found */ >>>> + if (!(dp->fn[NSFLAG] & NS_NOLFN)) { >>>> + if (c & LLEF) { /* Is it start of LFN >>>> sequence? */ >>>> + sum = dp->dir[LDIR_Chksum]; >>>> + c &= (BYTE)~LLEF; ord = c; /* LFN start >>>> order */ >>>> + dp->blk_ofs = dp->dptr; /* Start offset >>>> of LFN */ >>>> + } >>>> + /* Check validity of the LFN entry and compare >>>> it with given name */ >>>> + ord = (c == ord && sum == dp->dir[LDIR_Chksum] >>>> && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; >>>> + } >>>> + } else { /* An SFN entry is found */ >>>> + if (ord == 0 && sum == sum_sfn(dp->dir)) break; /* >>>> LFN matched? */ >>>> + if (!(dp->fn[NSFLAG] & NS_LOSS) && >>>> !mem_cmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */ >>>> + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset >>>> LFN sequence */ >>>> + } >>>> + } >>>> +#else /* Non LFN configuration */ >>>> + dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK; >>>> + if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, >>>> dp->fn, 11)) break; /* Is it a valid entry? */ >>>> +#endif >>>> + res = dir_next(dp, 0); /* Next entry */ >>>> + } while (res == FR_OK); >>>> + >>>> + return res; >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +#if !FF_FS_READONLY >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Register an object to the >>>> directory */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no >>>> free entry or too many SFN collision, FR_DISK_ERR:disk error */ >>>> + DIR* dp /* Target directory with object >>>> name to be created */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + FATFS *fs = dp->obj.fs; >>>> +#if FF_USE_LFN /* LFN configuration */ >>>> + UINT n, len, n_ent; >>>> + BYTE sn[12], sum; >>>> + >>>> + >>>> + if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return >>>> FR_INVALID_NAME; /* Check name validity */ >>>> + for (len = 0; fs->lfnbuf[len]; len++) ; /* Get lfn length */ >>>> + >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ >>>> + n_ent = (len + 14) / 15 + 2; /* Number of entries to >>>> allocate (85+C0+C1s) */ >>>> + res = dir_alloc(dp, n_ent); /* Allocate directory >>>> entries */ >>>> + if (res != FR_OK) return res; >>>> + dp->blk_ofs = dp->dptr - SZDIRE * (n_ent - 1); /* Set >>>> the allocated entry block offset */ >>>> + >>>> + if (dp->obj.stat & 4) { /* Has the directory >>>> been stretched by new allocation? */ >>>> + dp->obj.stat &= ~4; >>>> + res = fill_first_frag(&dp->obj); /* Fill the first >>>> fragment on the FAT if needed */ >>>> + if (res != FR_OK) return res; >>>> + res = fill_last_frag(&dp->obj, dp->clust, 0xFFFFFFFF); >>>> /* Fill the last fragment on the FAT if needed */ >>>> + if (res != FR_OK) return res; >>>> + if (dp->obj.sclust != 0) { /* Is it a >>>> sub-directory? */ >>>> + DIR dj; >>>> + >>>> + res = load_obj_xdir(&dj, &dp->obj); /* Load the >>>> object status */ >>>> + if (res != FR_OK) return res; >>>> + dp->obj.objsize += (DWORD)fs->csize * >>>> SS(fs); /* Increase the directory size by cluster size */ >>>> + st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize); >>>> + st_qword(fs->dirbuf + XDIR_ValidFileSize, >>>> dp->obj.objsize); >>>> + fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | >>>> 1; /* Update the allocation status */ >>>> + res = store_xdir(&dj); /* Store the >>>> object status */ >>>> + if (res != FR_OK) return res; >>>> + } >>>> + } >>>> + >>>> + create_xdir(fs->dirbuf, fs->lfnbuf); /* Create on-memory >>>> directory block to be written later */ >>>> + return FR_OK; >>>> + } >>>> +#endif >>>> + /* On the FAT/FAT32 volume */ >>>> + mem_cpy(sn, dp->fn, 12); >>>> + if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of >>>> 8.3 format, generate a numbered name */ >>>> + dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */ >>>> + for (n = 1; n < 100; n++) { >>>> + gen_numname(dp->fn, sn, fs->lfnbuf, n); /* Generate >>>> a numbered name */ >>>> + res = dir_find(dp); /* Check if the name >>>> collides with existing SFN */ >>>> + if (res != FR_OK) break; >>>> + } >>>> + if (n == 100) return FR_DENIED; /* Abort if too many >>>> collisions */ >>>> + if (res != FR_NO_FILE) return res; /* Abort if the >>>> result is other than 'not collided' */ >>>> + dp->fn[NSFLAG] = sn[NSFLAG]; >>>> + } >>>> + >>>> + /* Create an SFN with/without LFNs. */ >>>> + n_ent = (sn[NSFLAG] & NS_LFN) ? (len + 12) / 13 + 1 : 1; /* >>>> Number of entries to allocate */ >>>> + res = dir_alloc(dp, n_ent); /* Allocate entries */ >>>> + if (res == FR_OK && --n_ent) { /* Set LFN entry if needed */ >>>> + res = dir_sdi(dp, dp->dptr - n_ent * SZDIRE); >>>> + if (res == FR_OK) { >>>> + sum = sum_sfn(dp->fn); /* Checksum value of the SFN >>>> tied to the LFN */ >>>> + do { /* Store LFN entries in bottom >>>> first */ >>>> + res = move_window(fs, dp->sect); >>>> + if (res != FR_OK) break; >>>> + put_lfn(fs->lfnbuf, dp->dir, (BYTE)n_ent, sum); >>>> + fs->wflag = 1; >>>> + res = dir_next(dp, 0); /* Next entry */ >>>> + } while (res == FR_OK && --n_ent); >>>> + } >>>> + } >>>> + >>>> +#else /* Non LFN configuration */ >>>> + res = dir_alloc(dp, 1); /* Allocate an entry for SFN */ >>>> + >>>> +#endif >>>> + >>>> + /* Set SFN entry */ >>>> + if (res == FR_OK) { >>>> + res = move_window(fs, dp->sect); >>>> + if (res == FR_OK) { >>>> + mem_set(dp->dir, 0, SZDIRE); /* Clean the entry */ >>>> + mem_cpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */ >>>> +#if FF_USE_LFN >>>> + dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | >>>> NS_EXT); /* Put NT flag */ >>>> +#endif >>>> + fs->wflag = 1; >>>> + } >>>> + } >>>> + >>>> + return res; >>>> +} >>>> + >>>> +#endif /* !FF_FS_READONLY */ >>>> + >>>> + >>>> + >>>> +#if !FF_FS_READONLY && FF_FS_MINIMIZE == 0 >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Remove an object from the >>>> directory */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A >>>> disk error */ >>>> + DIR* dp /* Directory object pointing the >>>> entry to be removed */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + FATFS *fs = dp->obj.fs; >>>> +#if FF_USE_LFN /* LFN configuration */ >>>> + DWORD last = dp->dptr; >>>> + >>>> + res = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, >>>> dp->blk_ofs); /* Goto top of the entry block if LFN is exist */ >>>> + if (res == FR_OK) { >>>> + do { >>>> + res = move_window(fs, dp->sect); >>>> + if (res != FR_OK) break; >>>> + if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On >>>> the exFAT volume */ >>>> + dp->dir[XDIR_Type] &= 0x7F; /* Clear the entry >>>> InUse flag. */ >>>> + } else { /* On the >>>> FAT/FAT32 volume */ >>>> + dp->dir[DIR_Name] = DDEM; /* Mark the entry >>>> 'deleted'. */ >>>> + } >>>> + fs->wflag = 1; >>>> + if (dp->dptr >= last) break; /* If reached last >>>> entry then all entries of the object has been deleted. */ >>>> + res = dir_next(dp, 0); /* Next entry */ >>>> + } while (res == FR_OK); >>>> + if (res == FR_NO_FILE) res = FR_INT_ERR; >>>> + } >>>> +#else /* Non LFN configuration */ >>>> + >>>> + res = move_window(fs, dp->sect); >>>> + if (res == FR_OK) { >>>> + dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'.*/ >>>> + fs->wflag = 1; >>>> + } >>>> +#endif >>>> + >>>> + return res; >>>> +} >>>> + >>>> +#endif /* !FF_FS_READONLY && FF_FS_MINIMIZE == 0 */ >>>> + >>>> + >>>> + >>>> +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Get file information from directory >>>> entry */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static void get_fileinfo ( >>>> + DIR* dp, /* Pointer to the directory object */ >>>> + FILINFO* fno /* Pointer to the file information to be >>>> filled */ >>>> +) >>>> +{ >>>> + UINT si, di; >>>> +#if FF_USE_LFN >>>> + BYTE lcf; >>>> + WCHAR wc, hs; >>>> + FATFS *fs = dp->obj.fs; >>>> +#else >>>> + TCHAR c; >>>> +#endif >>>> + >>>> + >>>> + fno->fname[0] = 0; /* Invaidate file info */ >>>> + if (dp->sect == 0) return; /* Exit if read pointer has >>>> reached end of directory */ >>>> + >>>> +#if FF_USE_LFN /* LFN configuration */ >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ >>>> + get_xfileinfo(fs->dirbuf, fno); >>>> + return; >>>> + } else >>>> +#endif >>>> + { /* On the FAT/FAT32 volume */ >>>> + if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */ >>>> + si = di = hs = 0; >>>> + while (fs->lfnbuf[si] != 0) { >>>> + wc = fs->lfnbuf[si++]; /* Get an LFN >>>> character (UTF-16) */ >>>> + if (hs == 0 && IsSurrogate(wc)) { /* Is it a >>>> surrogate? */ >>>> + hs = wc; continue; /* Get low surrogate */ >>>> + } >>>> + wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], >>>> FF_LFN_BUF - di); /* Store it in UTF-16 or UTF-8 encoding */ >>>> + if (wc == 0) { di = 0; break; } /* Invalid char >>>> or buffer overflow? */ >>>> + di += wc; >>>> + hs = 0; >>>> + } >>>> + if (hs != 0) di = 0; /* Broken surrogate pair? */ >>>> + fno->fname[di] = 0; /* Terminate the LFN (null >>>> string means LFN is invalid) */ >>>> + } >>>> + } >>>> + >>>> + si = di = 0; >>>> + while (si < 11) { /* Get SFN from SFN entry */ >>>> + wc = dp->dir[si++]; /* Get a char */ >>>> + if (wc == ' ') continue; /* Skip padding spaces */ >>>> + if (wc == RDDEM) wc = DDEM; /* Restore replaced DDEM >>>> character */ >>>> + if (si == 9 && di < FF_SFN_BUF) fno->altname[di++] = '.'; >>>> /* Insert a . if extension is exist */ >>>> +#if FF_LFN_UNICODE >= 1 /* Unicode output */ >>>> + if (dbc_1st((BYTE)wc) && si != 8 && si != 11 && >>>> dbc_2nd(dp->dir[si])) { /* Make a DBC if needed */ >>>> + wc = wc << 8 | dp->dir[si++]; >>>> + } >>>> + wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM -> >>>> Unicode */ >>>> + if (wc == 0) { di = 0; break; } /* Wrong char in the >>>> current code page? */ >>>> + wc = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di); /* >>>> Store it in Unicode */ >>>> + if (wc == 0) { di = 0; break; } /* Buffer overflow? */ >>>> + di += wc; >>>> +#else /* ANSI/OEM output */ >>>> + fno->altname[di++] = (TCHAR)wc; /* Store it without any >>>> conversion */ >>>> +#endif >>>> + } >>>> + fno->altname[di] = 0; /* Terminate the SFN (null string >>>> means SFN is invalid) */ >>>> + >>>> + if (fno->fname[0] == 0) { /* If LFN is invalid, altname[] >>>> needs to be copied to fname[] */ >>>> + if (di == 0) { /* If LFN and SFN both are invalid, this >>>> object is inaccesible */ >>>> + fno->fname[di++] = '?'; >>>> + } else { >>>> + for (si = di = 0, lcf = NS_BODY; fno->altname[si]; >>>> si++, di++) { /* Copy altname[] to fname[] with case information */ >>>> + wc = (WCHAR)fno->altname[si]; >>>> + if (wc == '.') lcf = NS_EXT; >>>> + if (IsUpper(wc) && (dp->dir[DIR_NTres] & lcf)) wc >>>> += 0x20; >>>> + fno->fname[di] = (TCHAR)wc; >>>> + } >>>> + } >>>> + fno->fname[di] = 0; /* Terminate the LFN */ >>>> + if (!dp->dir[DIR_NTres]) fno->altname[0] = 0; /* Altname >>>> is not needed if neither LFN nor case info is exist. */ >>>> + } >>>> + >>>> +#else /* Non-LFN configuration */ >>>> + si = di = 0; >>>> + while (si < 11) { /* Copy name body and extension */ >>>> + c = (TCHAR)dp->dir[si++]; >>>> + if (c == ' ') continue; /* Skip padding spaces */ >>>> + if (c == RDDEM) c = DDEM; /* Restore replaced DDEM >>>> character */ >>>> + if (si == 9) fno->fname[di++] = '.';/* Insert a . if >>>> extension is exist */ >>>> + fno->fname[di++] = c; >>>> + } >>>> + fno->fname[di] = 0; >>>> +#endif >>>> + >>>> + fno->fattrib = dp->dir[DIR_Attr]; /* >>>> Attribute */ >>>> + fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ >>>> + fno->ftime = ld_word(dp->dir + DIR_ModTime + 0); /* Time */ >>>> + fno->fdate = ld_word(dp->dir + DIR_ModTime + 2); /* Date */ >>>> +} >>>> + >>>> +#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */ >>>> + >>>> + >>>> + >>>> +#if FF_USE_FIND && FF_FS_MINIMIZE <= 1 >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Pattern >>>> matching */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +#define FIND_RECURS 4 /* Maximum number of wildcard terms in >>>> the pattern to limit recursion */ >>>> + >>>> + >>>> +static DWORD get_achar ( /* Get a character and advance ptr */ >>>> + const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM >>>> or Unicode string */ >>>> +) >>>> +{ >>>> + DWORD chr; >>>> + >>>> + >>>> +#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode input */ >>>> + chr = tchar2uni(ptr); >>>> + if (chr == 0xFFFFFFFF) chr = 0; /* Wrong UTF encoding is >>>> recognized as end of the string */ >>>> + chr = ff_wtoupper(chr); >>>> + >>>> +#else /* ANSI/OEM input */ >>>> + chr = (BYTE)*(*ptr)++; /* Get a byte */ >>>> + if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */ >>>> +#if FF_CODE_PAGE == 0 >>>> + if (ExCvt && chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To >>>> upper SBCS extended char */ >>>> +#elif FF_CODE_PAGE < 900 >>>> + if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS >>>> extended char */ >>>> +#endif >>>> +#if FF_CODE_PAGE == 0 || FF_CODE_PAGE >= 900 >>>> + if (dbc_1st((BYTE)chr)) { /* Get DBC 2nd byte if needed */ >>>> + chr = dbc_2nd((BYTE)**ptr) ? chr << 8 | (BYTE)*(*ptr)++ : 0; >>>> + } >>>> +#endif >>>> + >>>> +#endif >>>> + return chr; >>>> +} >>>> + >>>> + >>>> +static int pattern_match ( /* 0:mismatched, 1:matched */ >>>> + const TCHAR* pat, /* Matching pattern */ >>>> + const TCHAR* nam, /* String to be tested */ >>>> + UINT skip, /* Number of pre-skip chars (number of >>>> ?s, b8:infinite (* specified)) */ >>>> + UINT recur /* Recursion count */ >>>> +) >>>> +{ >>>> + const TCHAR *pptr, *nptr; >>>> + DWORD pchr, nchr; >>>> + UINT sk; >>>> + >>>> + >>>> + while ((skip & 0xFF) != 0) { /* Pre-skip name chars */ >>>> + if (!get_achar(&nam)) return 0; /* Branch mismatched if >>>> less name chars */ >>>> + skip--; >>>> + } >>>> + if (*pat == 0 && skip) return 1; /* Matched? (short circuit) */ >>>> + >>>> + do { >>>> + pptr = pat; nptr = nam; /* Top of pattern and >>>> name to match */ >>>> + for (;;) { >>>> + if (*pptr == '?' || *pptr == '*') { /* Wildcard >>>> term? */ >>>> + if (recur == 0) return 0; /* Too many wildcard >>>> terms? */ >>>> + sk = 0; >>>> + do { /* Analyze the wildcard term */ >>>> + if (*pptr++ == '?') sk++; else sk |= 0x100; >>>> + } while (*pptr == '?' || *pptr == '*'); >>>> + if (pattern_match(pptr, nptr, sk, recur - 1)) >>>> return 1; /* Test new branch (recursive call) */ >>>> + nchr = *nptr; break; /* Branch mismatched */ >>>> + } >>>> + pchr = get_achar(&pptr); /* Get a pattern char */ >>>> + nchr = get_achar(&nptr); /* Get a name char */ >>>> + if (pchr != nchr) break; /* Branch mismatched? */ >>>> + if (pchr == 0) return 1; /* Branch matched? (matched >>>> at end of both strings) */ >>>> + } >>>> + get_achar(&nam); /* nam++ */ >>>> + } while (skip && nchr); /* Retry until end of name if >>>> infinite search is specified */ >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +#endif /* FF_USE_FIND && FF_FS_MINIMIZE <= 1 */ >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Pick a top segment and create the object name in directory >>>> form */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static FRESULT create_name ( /* FR_OK: successful, >>>> FR_INVALID_NAME: could not create */ >>>> + DIR* dp, /* Pointer to the directory object */ >>>> + const TCHAR** path /* Pointer to pointer to the >>>> segment in the path string */ >>>> +) >>>> +{ >>>> +#if FF_USE_LFN /* LFN configuration */ >>>> + BYTE b, cf; >>>> + WCHAR wc, *lfn; >>>> + DWORD uc; >>>> + UINT i, ni, si, di; >>>> + const TCHAR *p; >>>> + >>>> + >>>> + /* Create LFN into LFN working buffer */ >>>> + p = *path; lfn = dp->obj.fs->lfnbuf; di = 0; >>>> + for (;;) { >>>> + uc = tchar2uni(&p); /* Get a character */ >>>> + if (uc == 0xFFFFFFFF) return FR_INVALID_NAME; /* >>>> Invalid code or UTF decode error */ >>>> + if (uc >= 0x10000) lfn[di++] = (WCHAR)(uc >> 16); /* >>>> Store high surrogate if needed */ >>>> + wc = (WCHAR)uc; >>>> + if (wc < ' ' || wc == '/' || wc == '\\') break; /* Break >>>> if end of the path or a separator is found */ >>>> + if (wc < 0x80 && chk_chr("\"*:<>\?|\x7F", wc)) return >>>> FR_INVALID_NAME; /* Reject illegal characters for LFN */ >>>> + if (di >= FF_MAX_LFN) return FR_INVALID_NAME; /* Reject >>>> too long name */ >>>> + lfn[di++] = wc; /* Store the Unicode >>>> character */ >>>> + } >>>> + if (wc < ' ') { /* End of path? */ >>>> + cf = NS_LAST; /* Set last segment flag */ >>>> + } else { >>>> + cf = 0; /* Next segment follows */ >>>> + while (*p == '/' || *p == '\\') p++; /* Skip duplicated >>>> separators if exist */ >>>> + } >>>> + *path = p; /* Return pointer to the next >>>> segment */ >>>> + >>>> +#if FF_FS_RPATH != 0 >>>> + if ((di == 1 && lfn[di - 1] == '.') || >>>> + (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* >>>> Is this segment a dot name? */ >>>> + lfn[di] = 0; >>>> + for (i = 0; i < 11; i++) { /* Create dot name for >>>> SFN entry */ >>>> + dp->fn[i] = (i < di) ? '.' : ' '; >>>> + } >>>> + dp->fn[i] = cf | NS_DOT; /* This is a dot entry */ >>>> + return FR_OK; >>>> + } >>>> +#endif >>>> + while (di) { /* Snip off trailing spaces >>>> and dots if exist */ >>>> + wc = lfn[di - 1]; >>>> + if (wc != ' ' && wc != '.') break; >>>> + di--; >>>> + } >>>> + lfn[di] = 0; /* LFN is created into >>>> the working buffer */ >>>> + if (di == 0) return FR_INVALID_NAME; /* Reject null name */ >>>> + >>>> + /* Create SFN in directory form */ >>>> + for (si = 0; lfn[si] == ' '; si++) ; /* Remove leading >>>> spaces */ >>>> + if (si > 0 || lfn[si] == '.') cf |= NS_LOSS | NS_LFN; /* Is >>>> there any leading space or dot? */ >>>> + while (di > 0 && lfn[di - 1] != '.') di--; /* Find last dot >>>> (di<=si: no extension) */ >>>> + >>>> + mem_set(dp->fn, ' ', 11); >>>> + i = b = 0; ni = 8; >>>> + for (;;) { >>>> + wc = lfn[si++]; /* Get an LFN character */ >>>> + if (wc == 0) break; /* Break on end of the >>>> LFN */ >>>> + if (wc == ' ' || (wc == '.' && si != di)) { /* Remove >>>> embedded spaces and dots */ >>>> + cf |= NS_LOSS | NS_LFN; >>>> + continue; >>>> + } >>>> + >>>> + if (i >= ni || si == di) { /* End of field? */ >>>> + if (ni == 11) { /* Name extension >>>> overflow? */ >>>> + cf |= NS_LOSS | NS_LFN; >>>> + break; >>>> + } >>>> + if (si != di) cf |= NS_LOSS | NS_LFN; /* Name body >>>> overflow? */ >>>> + if (si > di) break; /* No name >>>> extension? */ >>>> + si = di; i = 8; ni = 11; b <<= 2; /* Enter name >>>> extension */ >>>> + continue; >>>> + } >>>> + >>>> + if (wc >= 0x80) { /* Is this a non-ASCII character? */ >>>> + cf |= NS_LFN; /* LFN entry needs to be created */ >>>> +#if FF_CODE_PAGE == 0 >>>> + if (ExCvt) { /* At SBCS */ >>>> + wc = ff_uni2oem(wc, CODEPAGE); /* >>>> Unicode ==> ANSI/OEM code */ >>>> + if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert >>>> extended character to upper (SBCS) */ >>>> + } else { /* At DBCS */ >>>> + wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* >>>> Unicode ==> Upper convert ==> ANSI/OEM code */ >>>> + } >>>> +#elif FF_CODE_PAGE < 900 /* SBCS cfg */ >>>> + wc = ff_uni2oem(wc, CODEPAGE); /* Unicode >>>> ==> ANSI/OEM code */ >>>> + if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert >>>> extended character to upper (SBCS) */ >>>> +#else /* DBCS cfg */ >>>> + wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* >>>> Unicode ==> Upper convert ==> ANSI/OEM code */ >>>> +#endif >>>> + } >>>> + >>>> + if (wc >= 0x100) { /* Is this a DBC? */ >>>> + if (i >= ni - 1) { /* Field overflow? */ >>>> + cf |= NS_LOSS | NS_LFN; >>>> + i = ni; continue; /* Next field */ >>>> + } >>>> + dp->fn[i++] = (BYTE)(wc >> 8); /* Put 1st byte */ >>>> + } else { /* SBC */ >>>> + if (wc == 0 || chk_chr("+,;=[]", wc)) { /* Replace >>>> illegal characters for SFN if needed */ >>>> + wc = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy >>>> conversion */ >>>> + } else { >>>> + if (IsUpper(wc)) { /* ASCII upper case? */ >>>> + b |= 2; >>>> + } >>>> + if (IsLower(wc)) { /* ASCII lower case? */ >>>> + b |= 1; wc -= 0x20; >>>> + } >>>> + } >>>> + } >>>> + dp->fn[i++] = (BYTE)wc; >>>> + } >>>> + >>>> + if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first >>>> character collides with DDEM, replace it with RDDEM */ >>>> + >>>> + if (ni == 8) b <<= 2; /* Shift capital flags if >>>> no extension */ >>>> + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; /* >>>> LFN entry needs to be created if composite capitals */ >>>> + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 >>>> format without extended character, NT flags are created */ >>>> + if (b & 0x01) cf |= NS_EXT; /* NT flag (Extension >>>> has small capital letters only) */ >>>> + if (b & 0x04) cf |= NS_BODY; /* NT flag (Body has small >>>> capital letters only) */ >>>> + } >>>> + >>>> + dp->fn[NSFLAG] = cf; /* SFN is created into dp->fn[] */ >>>> + >>>> + return FR_OK; >>>> + >>>> + >>>> +#else /* FF_USE_LFN : Non-LFN configuration */ >>>> + BYTE c, d, *sfn; >>>> + UINT ni, si, i; >>>> + const char *p; >>>> + >>>> + /* Create file name in directory form */ >>>> + p = *path; sfn = dp->fn; >>>> + mem_set(sfn, ' ', 11); >>>> + si = i = 0; ni = 8; >>>> +#if FF_FS_RPATH != 0 >>>> + if (p[si] == '.') { /* Is this a dot entry? */ >>>> + for (;;) { >>>> + c = (BYTE)p[si++]; >>>> + if (c != '.' || si >= 3) break; >>>> + sfn[i++] = c; >>>> + } >>>> + if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; >>>> + *path = p + si; /* Return >>>> pointer to the next segment */ >>>> + sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* >>>> Set last segment flag if end of the path */ >>>> + return FR_OK; >>>> + } >>>> +#endif >>>> + for (;;) { >>>> + c = (BYTE)p[si++]; /* Get a byte */ >>>> + if (c <= ' ') break; /* Break if end of the >>>> path name */ >>>> + if (c == '/' || c == '\\') { /* Break if a separator is >>>> found */ >>>> + while (p[si] == '/' || p[si] == '\\') si++; /* Skip >>>> duplicated separator if exist */ >>>> + break; >>>> + } >>>> + if (c == '.' || i >= ni) { /* End of body or field >>>> overflow? */ >>>> + if (ni == 11 || c != '.') return FR_INVALID_NAME; /* >>>> Field overflow or invalid dot? */ >>>> + i = 8; ni = 11; /* Enter file extension >>>> field */ >>>> + continue; >>>> + } >>>> +#if FF_CODE_PAGE == 0 >>>> + if (ExCvt && c >= 0x80) { /* Is SBC extended >>>> character? */ >>>> + c = ExCvt[c & 0x7F]; /* To upper SBC extended >>>> character */ >>>> + } >>>> +#elif FF_CODE_PAGE < 900 >>>> + if (c >= 0x80) { /* Is SBC extended >>>> character? */ >>>> + c = ExCvt[c & 0x7F]; /* To upper SBC extended >>>> character */ >>>> + } >>>> +#endif >>>> + if (dbc_1st(c)) { /* Check if it is a DBC >>>> 1st byte */ >>>> + d = (BYTE)p[si++]; /* Get 2nd byte */ >>>> + if (!dbc_2nd(d) || i >= ni - 1) return >>>> FR_INVALID_NAME; /* Reject invalid DBC */ >>>> + sfn[i++] = c; >>>> + sfn[i++] = d; >>>> + } else { /* SBC */ >>>> + if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return >>>> FR_INVALID_NAME; /* Reject illegal chrs for SFN */ >>>> + if (IsLower(c)) c -= 0x20; /* To upper */ >>>> + sfn[i++] = c; >>>> + } >>>> + } >>>> + *path = p + si; /* Return pointer to the >>>> next segment */ >>>> + if (i == 0) return FR_INVALID_NAME; /* Reject nul string */ >>>> + >>>> + if (sfn[0] == DDEM) sfn[0] = RDDEM; /* If the first >>>> character collides with DDEM, replace it with RDDEM */ >>>> + sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0; /* Set last >>>> segment flag if end of the path */ >>>> + >>>> + return FR_OK; >>>> +#endif /* FF_USE_LFN */ >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Follow a file >>>> path */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error >>>> code */ >>>> + DIR* dp, /* Directory object to return last >>>> directory and found object */ >>>> + const TCHAR* path /* Full-path string to find a file >>>> or directory */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + BYTE ns; >>>> + FATFS *fs = dp->obj.fs; >>>> + >>>> + >>>> +#if FF_FS_RPATH != 0 >>>> + if (*path != '/' && *path != '\\') { /* Without heading >>>> separator */ >>>> + dp->obj.sclust = fs->cdir; /* Start from >>>> current directory */ >>>> + } else >>>> +#endif >>>> + { /* With heading >>>> separator */ >>>> + while (*path == '/' || *path == '\\') path++; /* Strip >>>> heading separator */ >>>> + dp->obj.sclust = 0; /* Start from root >>>> directory */ >>>> + } >>>> +#if FF_FS_EXFAT >>>> + dp->obj.n_frag = 0; /* Invalidate last fragment counter of >>>> the object */ >>>> +#if FF_FS_RPATH != 0 >>>> + if (fs->fs_type == FS_EXFAT && dp->obj.sclust) { /* exFAT: >>>> Retrieve the sub-directory's status */ >>>> + DIR dj; >>>> + >>>> + dp->obj.c_scl = fs->cdc_scl; >>>> + dp->obj.c_size = fs->cdc_size; >>>> + dp->obj.c_ofs = fs->cdc_ofs; >>>> + res = load_obj_xdir(&dj, &dp->obj); >>>> + if (res != FR_OK) return res; >>>> + dp->obj.objsize = ld_dword(fs->dirbuf + XDIR_FileSize); >>>> + dp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; >>>> + } >>>> +#endif >>>> +#endif >>>> + >>>> + if ((UINT)*path < ' ') { /* Null path name is >>>> the origin directory itself */ >>>> + dp->fn[NSFLAG] = NS_NONAME; >>>> + res = dir_sdi(dp, 0); >>>> + >>>> + } else { /* Follow path */ >>>> + for (;;) { >>>> + res = create_name(dp, &path); /* Get a segment name >>>> of the path */ >>>> + if (res != FR_OK) break; >>>> + res = dir_find(dp); /* Find an object >>>> with the segment name */ >>>> + ns = dp->fn[NSFLAG]; >>>> + if (res != FR_OK) { /* Failed to find >>>> the object */ >>>> + if (res == FR_NO_FILE) { /* Object is not found */ >>>> + if (FF_FS_RPATH && (ns & NS_DOT)) { /* If >>>> dot entry is not exist, stay there */ >>>> + if (!(ns & NS_LAST)) continue; /* >>>> Continue to follow if not last segment */ >>>> + dp->fn[NSFLAG] = NS_NONAME; >>>> + res = FR_OK; >>>> + } else { /* Could >>>> not find the object */ >>>> + if (!(ns & NS_LAST)) res = FR_NO_PATH; >>>> /* Adjust error code if not last segment */ >>>> + } >>>> + } >>>> + break; >>>> + } >>>> + if (ns & NS_LAST) break; /* Last segment >>>> matched. Function completed. */ >>>> + /* Get into the sub-directory */ >>>> + if (!(dp->obj.attr & AM_DIR)) { /* It is not a >>>> sub-directory and cannot follow */ >>>> + res = FR_NO_PATH; break; >>>> + } >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { /* Save >>>> containing directory information for next dir */ >>>> + dp->obj.c_scl = dp->obj.sclust; >>>> + dp->obj.c_size = ((DWORD)dp->obj.objsize & >>>> 0xFFFFFF00) | dp->obj.stat; >>>> + dp->obj.c_ofs = dp->blk_ofs; >>>> + init_alloc_info(fs, &dp->obj); /* Open next >>>> directory */ >>>> + } else >>>> +#endif >>>> + { >>>> + dp->obj.sclust = ld_clust(fs, fs->win + dp->dptr % >>>> SS(fs)); /* Open next directory */ >>>> + } >>>> + } >>>> + } >>>> + >>>> + return res; >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Get logical drive number from path >>>> name */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static int get_ldnumber ( /* Returns logical drive number >>>> (-1:invalid drive number or null pointer) */ >>>> + const TCHAR** path /* Pointer to pointer to the path >>>> name */ >>>> +) >>>> +{ >>>> + const TCHAR *tp, *tt; >>>> + TCHAR tc; >>>> + int i, vol = -1; >>>> +#if FF_STR_VOLUME_ID /* Find string volume ID */ >>>> + const char *sp; >>>> + char c; >>>> +#endif >>>> + >>>> + tt = tp = *path; >>>> + if (!tp) return vol; /* Invalid path name? */ >>>> + do tc = *tt++; while ((UINT)tc >= (FF_USE_LFN ? ' ' : '!') && >>>> tc != ':'); /* Find a colon in the path */ >>>> + >>>> + if (tc == ':') { /* DOS/Windows style volume ID? */ >>>> + i = FF_VOLUMES; >>>> + if (IsDigit(*tp) && tp + 2 == tt) { /* Is there a >>>> numeric volume ID + colon? */ >>>> + i = (int)*tp - '0'; /* Get the LD number */ >>>> + } >>>> +#if FF_STR_VOLUME_ID == 1 /* Arbitrary string is enabled */ >>>> + else { >>>> + i = 0; >>>> + do { >>>> + sp = VolumeStr[i]; tp = *path; /* This string >>>> volume ID and path name */ >>>> + do { /* Compare the volume ID with path name */ >>>> + c = *sp++; tc = *tp++; >>>> + if (IsLower(c)) c -= 0x20; >>>> + if (IsLower(tc)) tc -= 0x20; >>>> + } while (c && (TCHAR)c == tc); >>>> + } while ((c || tp != tt) && ++i < FF_VOLUMES); /* >>>> Repeat for each id until pattern match */ >>>> + } >>>> +#endif >>>> + if (i < FF_VOLUMES) { /* If a volume ID is found, get >>>> the drive number and strip it */ >>>> + vol = i; /* Drive number */ >>>> + *path = tt; /* Snip the drive prefix off */ >>>> + } >>>> + return vol; >>>> + } >>>> +#if FF_STR_VOLUME_ID == 2 /* Unix style volume ID is enabled */ >>>> + if (*tp == '/') { >>>> + i = 0; >>>> + do { >>>> + sp = VolumeStr[i]; tp = *path; /* This string volume >>>> ID and path name */ >>>> + do { /* Compare the volume ID with path name */ >>>> + c = *sp++; tc = *(++tp); >>>> + if (IsLower(c)) c -= 0x20; >>>> + if (IsLower(tc)) tc -= 0x20; >>>> + } while (c && (TCHAR)c == tc); >>>> + } while ((c || (tc != '/' && (UINT)tc >= (FF_USE_LFN ? ' ' >>>> : '!'))) && ++i < FF_VOLUMES); /* Repeat for each ID until >>>> pattern match */ >>>> + if (i < FF_VOLUMES) { /* If a volume ID is found, get >>>> the drive number and strip it */ >>>> + vol = i; /* Drive number */ >>>> + *path = tp; /* Snip the drive prefix off */ >>>> + return vol; >>>> + } >>>> + } >>>> +#endif >>>> + /* No drive prefix is found */ >>>> +#if FF_FS_RPATH != 0 >>>> + vol = CurrVol; /* Default drive is current drive */ >>>> +#else >>>> + vol = 0; /* Default drive is 0 */ >>>> +#endif >>>> + return vol; /* Return the default drive */ >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* GPT support >>>> functions */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +#if FF_LBA64 >>>> + >>>> +/* Calculate CRC32 in byte-by-byte */ >>>> + >>>> +static DWORD crc32 ( /* Returns next CRC value */ >>>> + DWORD crc, /* Current CRC value */ >>>> + BYTE d /* A byte to be processed */ >>>> +) >>>> +{ >>>> + BYTE b; >>>> + >>>> + >>>> + for (b = 1; b; b <<= 1) { >>>> + crc ^= (d & b) ? 1 : 0; >>>> + crc = (crc & 1) ? crc >> 1 ^ 0xEDB88320 : crc >> 1; >>>> + } >>>> + return crc; >>>> +} >>>> + >>>> + >>>> +/* Check validity of GPT header */ >>>> + >>>> +static int test_gpt_header ( /* 0:Invalid, 1:Valid */ >>>> + const BYTE* gpth /* Pointer to the GPT header */ >>>> +) >>>> +{ >>>> + UINT i; >>>> + DWORD bcc; >>>> + >>>> + >>>> + if (mem_cmp(gpth + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", >>>> 16)) return 0; /* Check sign, version (1.0) and length (92) */ >>>> + for (i = 0, bcc = 0xFFFFFFFF; i < 92; i++) { /* Check >>>> header BCC */ >>>> + bcc = crc32(bcc, i - GPTH_Bcc < 4 ? 0 : gpth[i]); >>>> + } >>>> + if (~bcc != ld_dword(gpth + GPTH_Bcc)) return 0; >>>> + if (ld_dword(gpth + GPTH_PteSize) != SZ_GPTE) return 0; /* >>>> Table entry size (must be SZ_GPTE bytes) */ >>>> + if (ld_dword(gpth + GPTH_PtNum) > 128) return 0; /* Table >>>> size (must be 128 entries or less) */ >>>> + >>>> + return 1; >>>> +} >>>> + >>>> +#if !FF_FS_READONLY && FF_USE_MKFS >>>> + >>>> +/* Generate random value */ >>>> +static DWORD make_rand ( >>>> + DWORD seed, /* Seed value */ >>>> + BYTE* buff, /* Output buffer */ >>>> + UINT n /* Data length */ >>>> +) >>>> +{ >>>> + UINT r; >>>> + >>>> + >>>> + if (seed == 0) seed = 1; >>>> + do { >>>> + for (r = 0; r < 8; r++) seed = seed & 1 ? seed >> 1 ^ >>>> 0xA3000000 : seed >> 1; /* Shift 8 bits the 32-bit LFSR */ >>>> + *buff++ = (BYTE)seed; >>>> + } while (--n); >>>> + return seed; >>>> +} >>>> + >>>> +#endif >>>> +#endif >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Load a sector and check if it is an FAT >>>> VBR */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +/* Check what the sector is */ >>>> + >>>> +static UINT check_fs ( /* 0:FAT VBR, 1:exFAT VBR, 2:Not FAT and >>>> valid BS, 3:Not FAT and invalid BS, 4:Disk error */ >>>> + FATFS* fs, /* Filesystem object */ >>>> + LBA_t sect /* Sector to load and check if it is an >>>> FAT-VBR or not */ >>>> +) >>>> +{ >>>> + WORD w, sign; >>>> + BYTE b; >>>> + >>>> + >>>> + fs->wflag = 0; fs->winsect = (LBA_t)0 - 1; /* Invaidate >>>> window */ >>>> + if (move_window(fs, sect) != FR_OK) return 4; /* Load the >>>> boot sector */ >>>> + sign = ld_word(fs->win + BS_55AA); >>>> +#if FF_FS_EXFAT >>>> + if (sign == 0xAA55 && !mem_cmp(fs->win + BS_JmpBoot, >>>> "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* It is an exFAT VBR */ >>>> +#endif >>>> + b = fs->win[BS_JmpBoot]; >>>> + if (b == 0xEB || b == 0xE9 || b == 0xE8) { /* Valid JumpBoot >>>> code? (short jump, near jump or near call) */ >>>> + if (sign == 0xAA55 && !mem_cmp(fs->win + BS_FilSysType32, >>>> "FAT32 ", 8)) return 0; /* It is an FAT32 VBR */ >>>> + /* FAT volumes formatted with early MS-DOS lack boot >>>> signature and FAT string, so that we need to identify the FAT VBR >>>> without them. */ >>>> + w = ld_word(fs->win + BPB_BytsPerSec); >>>> + if ((w & (w - 1)) == 0 && w >= FF_MIN_SS && w <= FF_MAX_SS) >>>> { /* Properness of sector size */ >>>> + b = fs->win[BPB_SecPerClus]; >>>> + if (b != 0 && (b & (b - 1)) == 0 /* Properness of >>>> cluster size */ >>>> + && (fs->win[BPB_NumFATs] == 1 || fs->win[BPB_NumFATs] >>>> == 2) /* Properness of number of FATs */ >>>> + && ld_word(fs->win + BPB_RootEntCnt) != 0 /* Properness >>>> of root entry count */ >>>> + && ld_word(fs->win + BPB_FATSz16) != 0) { /* Properness >>>> of FAT size */ >>>> + return 0; /* Sector can be presumed an FAT VBR */ >>>> + } >>>> + } >>>> + } >>>> + return sign == 0xAA55 ? 2 : 3; /* Not an FAT VBR (valid or >>>> invalid BS) */ >>>> +} >>>> + >>>> + >>>> +/* Find an FAT volume */ >>>> +/* (It supports only generic partitioning rules, MBR, GPT and SFD) */ >>>> + >>>> +static UINT find_volume ( /* Returns BS status found in the >>>> hosting drive */ >>>> + FATFS* fs, /* Filesystem object */ >>>> + UINT part /* Partition to fined = 0:auto, 1..:forced */ >>>> +) >>>> +{ >>>> + UINT fmt, i; >>>> + DWORD mbr_pt[4]; >>>> + >>>> + >>>> + fmt = check_fs(fs, 0); /* Load sector 0 and >>>> check if it is an FAT VBR as SFD */ >>>> + if (fmt != 2 && (fmt >= 3 || part == 0)) return fmt; /* >>>> Returns if it is a FAT VBR as auto scan, not a BS or disk error */ >>>> + >>>> + /* Sector 0 is not an FAT VBR or forced partition number wants >>>> a partition */ >>>> + >>>> +#if FF_LBA64 >>>> + if (fs->win[MBR_Table + PTE_System] == 0xEE) { /* GPT >>>> protective MBR? */ >>>> + DWORD n_ent, v_ent, ofs; >>>> + QWORD pt_lba; >>>> + >>>> + if (move_window(fs, 1) != FR_OK) return 4; /* Load GPT >>>> header sector (next to MBR) */ >>>> + if (!test_gpt_header(fs->win)) return 3; /* Check if GPT >>>> header is valid */ >>>> + n_ent = ld_dword(fs->win + GPTH_PtNum); /* Number of >>>> entries */ >>>> + pt_lba = ld_qword(fs->win + GPTH_PtOfs); /* Table >>>> location */ >>>> + for (v_ent = i = 0; i < n_ent; i++) { /* Find FAT >>>> partition */ >>>> + if (move_window(fs, pt_lba + i * SZ_GPTE / SS(fs)) != >>>> FR_OK) return 4; /* PT sector */ >>>> + ofs = i * SZ_GPTE % >>>> SS(fs); /* Offset in >>>> the sector */ >>>> + if (!mem_cmp(fs->win + ofs + GPTE_PtGuid, >>>> GUID_MS_Basic, 16)) { /* MS basic data partition? */ >>>> + v_ent++; >>>> + fmt = check_fs(fs, ld_qword(fs->win + ofs + >>>> GPTE_FstLba)); /* Load VBR and check status */ >>>> + if (part == 0 && fmt <= 1) return fmt; >>>> /* Auto search (valid FAT volume found first) */ >>>> + if (part != 0 && v_ent == part) return fmt; /* >>>> Forced partition order (regardless of it is valid or not) */ >>>> + } >>>> + } >>>> + return 3; /* Not found */ >>>> + } >>>> +#endif >>>> + if (FF_MULTI_PARTITION && part > 4) return 3; /* MBR has 4 >>>> partitions max */ >>>> + for (i = 0; i < 4; i++) { /* Load partition offset in >>>> the MBR */ >>>> + mbr_pt[i] = ld_dword(fs->win + MBR_Table + i * SZ_PTE + >>>> PTE_StLba); >>>> + } >>>> + i = part ? part - 1 : 0; /* Table index to find first */ >>>> + do { /* Find an FAT volume */ >>>> + fmt = mbr_pt[i] ? check_fs(fs, mbr_pt[i]) : 3; /* Check >>>> if the partition is FAT */ >>>> + } while (part == 0 && fmt >= 2 && ++i < 4); >>>> + return fmt; >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Determine logical drive number and mount the volume if >>>> needed */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an >>>> error occurred */ >>>> + const TCHAR** path, /* Pointer to pointer to the >>>> path name (drive number) */ >>>> + FATFS** rfs, /* Pointer to pointer to the found >>>> filesystem object */ >>>> + BYTE mode /* !=0: Check write protection for >>>> write access */ >>>> +) >>>> +{ >>>> + int vol; >>>> + DSTATUS stat; >>>> + LBA_t bsect; >>>> + DWORD tsect, sysect, fasize, nclst, szbfat; >>>> + WORD nrsv; >>>> + FATFS *fs; >>>> + UINT fmt; >>>> + >>>> + >>>> + /* Get logical drive number */ >>>> + *rfs = 0; >>>> + vol = get_ldnumber(path); >>>> + if (vol < 0) return FR_INVALID_DRIVE; >>>> + >>>> + /* Check if the filesystem object is valid or not */ >>>> + fs = FatFs[vol]; /* Get pointer to the >>>> filesystem object */ >>>> + if (!fs) return FR_NOT_ENABLED; /* Is the filesystem >>>> object available? */ >>>> +#if FF_FS_REENTRANT >>>> + if (!lock_fs(fs)) return FR_TIMEOUT; /* Lock the volume */ >>>> +#endif >>>> + *rfs = fs; /* Return pointer to the >>>> filesystem object */ >>>> + >>>> + mode &= (BYTE)~FA_READ; /* Desired access mode, >>>> write access or not */ >>>> + if (fs->fs_type != 0) { /* If the volume has >>>> been mounted */ >>>> + stat = disk_status(fs->pdrv); >>>> + if (!(stat & STA_NOINIT)) { /* and the physical >>>> drive is kept initialized */ >>>> + if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { >>>> /* Check write protection if needed */ >>>> + return FR_WRITE_PROTECTED; >>>> + } >>>> + return FR_OK; /* The filesystem object >>>> is already valid */ >>>> + } >>>> + } >>>> + >>>> + /* The filesystem object is not valid. */ >>>> + /* Following code attempts to mount the volume. (find a FAT >>>> volume, analyze the BPB and initialize the filesystem object) */ >>>> + >>>> + fs->fs_type = 0; /* Clear the filesystem >>>> object */ >>>> + fs->pdrv = LD2PD(vol); /* Volume hosting >>>> physical drive */ >>>> + stat = disk_initialize(fs->pdrv); /* Initialize the physical >>>> drive */ >>>> + if (stat & STA_NOINIT) { /* Check if the >>>> initialization succeeded */ >>>> + return FR_NOT_READY; /* Failed to initialize due >>>> to no medium or hard error */ >>>> + } >>>> + if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check >>>> disk write protection if needed */ >>>> + return FR_WRITE_PROTECTED; >>>> + } >>>> +#if FF_MAX_SS != FF_MIN_SS /* Get sector size >>>> (multiple sector size cfg only) */ >>>> + if (disk_ioctl(fs->pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) >>>> return FR_DISK_ERR; >>>> + if (SS(fs) > FF_MAX_SS || SS(fs) < FF_MIN_SS || (SS(fs) & >>>> (SS(fs) - 1))) return FR_DISK_ERR; >>>> +#endif >>>> + >>>> + /* Find an FAT volume on the drive */ >>>> + fmt = find_volume(fs, LD2PT(vol)); >>>> + if (fmt == 4) return FR_DISK_ERR; /* An error occured in >>>> the disk I/O layer */ >>>> + if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is >>>> found */ >>>> + bsect = fs->winsect; /* Volume location */ >>>> + >>>> + /* An FAT volume is found (bsect). Following code initializes >>>> the filesystem object */ >>>> + >>>> +#if FF_FS_EXFAT >>>> + if (fmt == 1) { >>>> + QWORD maxlba; >>>> + DWORD so, cv, bcl, i; >>>> + >>>> + for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] >>>> == 0; i++) ; /* Check zero filler */ >>>> + if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM; >>>> + >>>> + if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return >>>> FR_NO_FILESYSTEM; /* Check exFAT version (must be version 1.0) */ >>>> + >>>> + if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) { /* >>>> (BPB_BytsPerSecEx must be equal to the physical sector size) */ >>>> + return FR_NO_FILESYSTEM; >>>> + } >>>> + >>>> + maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* >>>> Last LBA + 1 of the volume */ >>>> + if (!FF_LBA64 && maxlba >= 0x100000000) return >>>> FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */ >>>> + >>>> + fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number >>>> of sectors per FAT */ >>>> + >>>> + fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number >>>> of FATs */ >>>> + if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* >>>> (Supports only 1 FAT) */ >>>> + >>>> + fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* >>>> Cluster size */ >>>> + if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must >>>> be 1..32768) */ >>>> + >>>> + nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number >>>> of clusters */ >>>> + if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too >>>> many clusters) */ >>>> + fs->n_fatent = nclst + 2; >>>> + >>>> + /* Boundaries and Limits */ >>>> + fs->volbase = bsect; >>>> + fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx); >>>> + fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx); >>>> + if (maxlba < (QWORD)fs->database + nclst * fs->csize) >>>> return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than >>>> the size requiered) */ >>>> + fs->dirbase = ld_dword(fs->win + BPB_RootClusEx); >>>> + >>>> + /* Get bitmap location and check if it is contiguous >>>> (implementation assumption) */ >>>> + so = i = 0; >>>> + for (;;) { /* Find the bitmap entry in the root >>>> directory (in only first cluster) */ >>>> + if (i == 0) { >>>> + if (so >= fs->csize) return FR_NO_FILESYSTEM; /* >>>> Not found? */ >>>> + if (move_window(fs, clst2sect(fs, >>>> (DWORD)fs->dirbase) + so) != FR_OK) return FR_DISK_ERR; >>>> + so++; >>>> + } >>>> + if (fs->win[i] == ET_BITMAP) break; /* >>>> Is it a bitmap entry? */ >>>> + i = (i + SZDIRE) % SS(fs); /* Next entry */ >>>> + } >>>> + bcl = ld_dword(fs->win + i + 20); /* >>>> Bitmap cluster */ >>>> + if (bcl < 2 || bcl >= fs->n_fatent) return FR_NO_FILESYSTEM; >>>> + fs->bitbase = fs->database + fs->csize * (bcl - 2); /* >>>> Bitmap sector */ >>>> + for (;;) { /* Check if bitmap is contiguous */ >>>> + if (move_window(fs, fs->fatbase + bcl / (SS(fs) / 4)) >>>> != FR_OK) return FR_DISK_ERR; >>>> + cv = ld_dword(fs->win + bcl % (SS(fs) / 4) * 4); >>>> + if (cv == 0xFFFFFFFF) break; /* Last >>>> link? */ >>>> + if (cv != ++bcl) return FR_NO_FILESYSTEM; /* >>>> Fragmented? */ >>>> + } >>>> + >>>> +#if !FF_FS_READONLY >>>> + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* >>>> Initialize cluster allocation information */ >>>> +#endif >>>> + fmt = FS_EXFAT; /* FAT sub-type */ >>>> + } else >>>> +#endif /* FF_FS_EXFAT */ >>>> + { >>>> + if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return >>>> FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the >>>> physical sector size) */ >>>> + >>>> + fasize = ld_word(fs->win + BPB_FATSz16); /* Number >>>> of sectors per FAT */ >>>> + if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32); >>>> + fs->fsize = fasize; >>>> + >>>> + fs->n_fats = fs->win[BPB_NumFATs]; /* Number >>>> of FATs */ >>>> + if (fs->n_fats != 1 && fs->n_fats != 2) return >>>> FR_NO_FILESYSTEM; /* (Must be 1 or 2) */ >>>> + fasize *= fs->n_fats; /* Number >>>> of sectors for FAT area */ >>>> + >>>> + fs->csize = fs->win[BPB_SecPerClus]; /* Cluster >>>> size */ >>>> + if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return >>>> FR_NO_FILESYSTEM; /* (Must be power of 2) */ >>>> + >>>> + fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* >>>> Number of root directory entries */ >>>> + if (fs->n_rootdir % (SS(fs) / SZDIRE)) return >>>> FR_NO_FILESYSTEM; /* (Must be sector aligned) */ >>>> + >>>> + tsect = ld_word(fs->win + BPB_TotSec16); /* Number >>>> of sectors on the volume */ >>>> + if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32); >>>> + >>>> + nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number >>>> of reserved sectors */ >>>> + if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must >>>> not be 0) */ >>>> + >>>> + /* Determine the FAT sub type */ >>>> + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / >>>> SZDIRE); /* RSV + FAT + DIR */ >>>> + if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid >>>> volume size) */ >>>> + nclst = (tsect - sysect) / fs->csize; /* Number >>>> of clusters */ >>>> + if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid >>>> volume size) */ >>>> + fmt = 0; >>>> + if (nclst <= MAX_FAT32) fmt = FS_FAT32; >>>> + if (nclst <= MAX_FAT16) fmt = FS_FAT16; >>>> + if (nclst <= MAX_FAT12) fmt = FS_FAT12; >>>> + if (fmt == 0) return FR_NO_FILESYSTEM; >>>> + >>>> + /* Boundaries and Limits */ >>>> + fs->n_fatent = nclst + 2; /* Number >>>> of FAT entries */ >>>> + fs->volbase = bsect; /* Volume >>>> start sector */ >>>> + fs->fatbase = bsect + nrsv; /* FAT >>>> start sector */ >>>> + fs->database = bsect + sysect; /* Data >>>> start sector */ >>>> + if (fmt == FS_FAT32) { >>>> + if (ld_word(fs->win + BPB_FSVer32) != 0) return >>>> FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */ >>>> + if (fs->n_rootdir != 0) return FR_NO_FILESYSTEM; /* >>>> (BPB_RootEntCnt must be 0) */ >>>> + fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* >>>> Root directory start cluster */ >>>> + szbfat = fs->n_fatent * 4; /* >>>> (Needed FAT size) */ >>>> + } else { >>>> + if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM; /* >>>> (BPB_RootEntCnt must not be 0) */ >>>> + fs->dirbase = fs->fatbase + fasize; /* Root >>>> directory start sector */ >>>> + szbfat = (fmt == FS_FAT16) ? /* (Needed >>>> FAT size) */ >>>> + fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + >>>> (fs->n_fatent & 1); >>>> + } >>>> + if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return >>>> FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size >>>> needed) */ >>>> + >>>> +#if !FF_FS_READONLY >>>> + /* Get FSInfo if available */ >>>> + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* >>>> Initialize cluster allocation information */ >>>> + fs->fsi_flag = 0x80; >>>> +#if (FF_FS_NOFSINFO & 3) != 3 >>>> + if (fmt == FS_FAT32 /* Allow to update >>>> FSInfo only if BPB_FSInfo32 == 1 */ >>>> + && ld_word(fs->win + BPB_FSInfo32) == 1 >>>> + && move_window(fs, bsect + 1) == FR_OK) >>>> + { >>>> + fs->fsi_flag = 0; >>>> + if (ld_word(fs->win + BS_55AA) == 0xAA55 /* Load >>>> FSInfo data if available */ >>>> + && ld_dword(fs->win + FSI_LeadSig) == 0x41615252 >>>> + && ld_dword(fs->win + FSI_StrucSig) == 0x61417272) >>>> + { >>>> +#if (FF_FS_NOFSINFO & 1) == 0 >>>> + fs->free_clst = ld_dword(fs->win + FSI_Free_Count); >>>> +#endif >>>> +#if (FF_FS_NOFSINFO & 2) == 0 >>>> + fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free); >>>> +#endif >>>> + } >>>> + } >>>> +#endif /* (FF_FS_NOFSINFO & 3) != 3 */ >>>> +#endif /* !FF_FS_READONLY */ >>>> + } >>>> + >>>> + fs->fs_type = (BYTE)fmt;/* FAT sub-type */ >>>> + fs->id = ++Fsid; /* Volume mount ID */ >>>> +#if FF_USE_LFN == 1 >>>> + fs->lfnbuf = LfnBuf; /* Static LFN working buffer */ >>>> +#if FF_FS_EXFAT >>>> + fs->dirbuf = DirBuf; /* Static directory block scratchpad >>>> buuffer */ >>>> +#endif >>>> +#endif >>>> +#if FF_FS_RPATH != 0 >>>> + fs->cdir = 0; /* Initialize current directory */ >>>> +#endif >>>> +#if FF_FS_LOCK != 0 /* Clear file lock semaphores */ >>>> + clear_lock(fs); >>>> +#endif >>>> + return FR_OK; >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Check if the file/directory object is valid or >>>> not */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ >>>> + FFOBJID* obj, /* Pointer to the FFOBJID, the 1st >>>> member in the FIL/DIR object, to check validity */ >>>> + FATFS** rfs /* Pointer to pointer to the owner >>>> filesystem object to return */ >>>> +) >>>> +{ >>>> + FRESULT res = FR_INVALID_OBJECT; >>>> + >>>> + >>>> + if (obj && obj->fs && obj->fs->fs_type && obj->id == >>>> obj->fs->id) { /* Test if the object is valid */ >>>> +#if FF_FS_REENTRANT >>>> + if (lock_fs(obj->fs)) { /* Obtain the filesystem object */ >>>> + if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* >>>> Test if the phsical drive is kept initialized */ >>>> + res = FR_OK; >>>> + } else { >>>> + unlock_fs(obj->fs, FR_OK); >>>> + } >>>> + } else { >>>> + res = FR_TIMEOUT; >>>> + } >>>> +#else >>>> + if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test >>>> if the phsical drive is kept initialized */ >>>> + res = FR_OK; >>>> + } >>>> +#endif >>>> + } >>>> + *rfs = (res == FR_OK) ? obj->fs : 0; /* Corresponding >>>> filesystem object */ >>>> + return res; >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*--------------------------------------------------------------------------- >>>> >>>> + >>>> + Public Functions (FatFs API) >>>> + >>>> +----------------------------------------------------------------------------*/ >>>> >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Mount/Unmount a Logical >>>> Drive */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_mount ( >>>> + FATFS* fs, /* Pointer to the filesystem object >>>> (NULL:unmount)*/ >>>> + const TCHAR* path, /* Logical drive number to be >>>> mounted/unmounted */ >>>> + BYTE opt /* Mode option 0:Do not mount (delayed >>>> mount), 1:Mount immediately */ >>>> +) >>>> +{ >>>> + FATFS *cfs; >>>> + int vol; >>>> + FRESULT res; >>>> + const TCHAR *rp = path; >>>> + >>>> + >>>> + /* Get logical drive number */ >>>> + vol = get_ldnumber(&rp); >>>> + if (vol < 0) return FR_INVALID_DRIVE; >>>> + cfs = FatFs[vol]; /* Pointer to fs object */ >>>> + >>>> + if (cfs) { >>>> +#if FF_FS_LOCK != 0 >>>> + clear_lock(cfs); >>>> +#endif >>>> +#if FF_FS_REENTRANT /* Discard sync object >>>> of the current volume */ >>>> + if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR; >>>> +#endif >>>> + cfs->fs_type = 0; /* Clear old fs object */ >>>> + } >>>> + >>>> + if (fs) { >>>> + fs->fs_type = 0; /* Clear new fs object */ >>>> +#if FF_FS_REENTRANT /* Create sync object >>>> for the new volume */ >>>> + if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR; >>>> +#endif >>>> + } >>>> + FatFs[vol] = fs; /* Register new fs object */ >>>> + >>>> + if (opt == 0) return FR_OK; /* Do not mount now, it >>>> will be mounted later */ >>>> + >>>> + res = mount_volume(&path, &fs, 0); /* Force mounted the >>>> volume */ >>>> + LEAVE_FF(fs, res); >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Open or Create a >>>> File */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_open ( >>>> + FIL* fp, /* Pointer to the blank file object */ >>>> + const TCHAR* path, /* Pointer to the file name */ >>>> + BYTE mode /* Access mode and file open mode flags */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + DIR dj; >>>> + FATFS *fs; >>>> +#if !FF_FS_READONLY >>>> + DWORD cl, bcs, clst, tm; >>>> + LBA_t sc; >>>> + FSIZE_t ofs; >>>> +#endif >>>> + DEF_NAMBUF >>>> + >>>> + >>>> + if (!fp) return FR_INVALID_OBJECT; >>>> + >>>> + /* Get logical drive number */ >>>> + mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | >>>> FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND; >>>> + res = mount_volume(&path, &fs, mode); >>>> + if (res == FR_OK) { >>>> + dj.obj.fs = fs; >>>> + INIT_NAMBUF(fs); >>>> + res = follow_path(&dj, path); /* Follow the file path */ >>>> +#if !FF_FS_READONLY /* Read/Write configuration */ >>>> + if (res == FR_OK) { >>>> + if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory >>>> itself? */ >>>> + res = FR_INVALID_NAME; >>>> + } >>>> +#if FF_FS_LOCK != 0 >>>> + else { >>>> + res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : >>>> 0); /* Check if the file can be used */ >>>> + } >>>> +#endif >>>> + } >>>> + /* Create or Open a file */ >>>> + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | >>>> FA_CREATE_NEW)) { >>>> + if (res != FR_OK) { /* No file, >>>> create new */ >>>> + if (res == FR_NO_FILE) { /* There is no file >>>> to open, create a new entry */ >>>> +#if FF_FS_LOCK != 0 >>>> + res = enq_lock() ? dir_register(&dj) : >>>> FR_TOO_MANY_OPEN_FILES; >>>> +#else >>>> + res = dir_register(&dj); >>>> +#endif >>>> + } >>>> + mode |= FA_CREATE_ALWAYS; /* File is created */ >>>> + } >>>> + else { /* Any object >>>> with the same name is already existing */ >>>> + if (dj.obj.attr & (AM_RDO | AM_DIR)) { /* Cannot >>>> overwrite it (R/O or DIR) */ >>>> + res = FR_DENIED; >>>> + } else { >>>> + if (mode & FA_CREATE_NEW) res = FR_EXIST; /* >>>> Cannot create as new file */ >>>> + } >>>> + } >>>> + if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* >>>> Truncate the file if overwrite mode */ >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { >>>> + /* Get current allocation info */ >>>> + fp->obj.fs = fs; >>>> + init_alloc_info(fs, &fp->obj); >>>> + /* Set directory entry block initial state */ >>>> + mem_set(fs->dirbuf + 2, 0, 30); /* Clear >>>> 85 entry except for NumSec */ >>>> + mem_set(fs->dirbuf + 38, 0, 26); /* Clear C0 >>>> entry except for NumName and NameHash */ >>>> + fs->dirbuf[XDIR_Attr] = AM_ARC; >>>> + st_dword(fs->dirbuf + XDIR_CrtTime, >>>> GET_FATTIME()); >>>> + fs->dirbuf[XDIR_GenFlags] = 1; >>>> + res = store_xdir(&dj); >>>> + if (res == FR_OK && fp->obj.sclust != 0) { >>>> /* Remove the cluster chain if exist */ >>>> + res = remove_chain(&fp->obj, >>>> fp->obj.sclust, 0); >>>> + fs->last_clst = fp->obj.sclust - 1; /* >>>> Reuse the cluster hole */ >>>> + } >>>> + } else >>>> +#endif >>>> + { >>>> + /* Set directory entry initial state */ >>>> + tm = GET_FATTIME(); /* Set >>>> created time */ >>>> + st_dword(dj.dir + DIR_CrtTime, tm); >>>> + st_dword(dj.dir + DIR_ModTime, tm); >>>> + cl = ld_clust(fs, dj.dir); /* Get >>>> current cluster chain */ >>>> + dj.dir[DIR_Attr] = AM_ARC; /* Reset >>>> attribute */ >>>> + st_clust(fs, dj.dir, 0); /* Reset >>>> file allocation info */ >>>> + st_dword(dj.dir + DIR_FileSize, 0); >>>> + fs->wflag = 1; >>>> + if (cl != 0) { /* Remove >>>> the cluster chain if exist */ >>>> + sc = fs->winsect; >>>> + res = remove_chain(&dj.obj, cl, 0); >>>> + if (res == FR_OK) { >>>> + res = move_window(fs, sc); >>>> + fs->last_clst = cl - 1; /* Reuse >>>> the cluster hole */ >>>> + } >>>> + } >>>> + } >>>> + } >>>> + } >>>> + else { /* Open an existing file */ >>>> + if (res == FR_OK) { /* Is the object >>>> exsiting? */ >>>> + if (dj.obj.attr & AM_DIR) { /* File open >>>> against a directory */ >>>> + res = FR_NO_FILE; >>>> + } else { >>>> + if ((mode & FA_WRITE) && (dj.obj.attr & >>>> AM_RDO)) { /* Write mode open against R/O file */ >>>> + res = FR_DENIED; >>>> + } >>>> + } >>>> + } >>>> + } >>>> + if (res == FR_OK) { >>>> + if (mode & FA_CREATE_ALWAYS) mode |= FA_MODIFIED; /* >>>> Set file change flag if created or overwritten */ >>>> + fp->dir_sect = fs->winsect; /* Pointer to >>>> the directory entry */ >>>> + fp->dir_ptr = dj.dir; >>>> +#if FF_FS_LOCK != 0 >>>> + fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : >>>> 0); /* Lock the file for this session */ >>>> + if (fp->obj.lockid == 0) res = FR_INT_ERR; >>>> +#endif >>>> + } >>>> +#else /* R/O configuration */ >>>> + if (res == FR_OK) { >>>> + if (dj.fn[NSFLAG] & NS_NONAME) { /* Is it origin >>>> directory itself? */ >>>> + res = FR_INVALID_NAME; >>>> + } else { >>>> + if (dj.obj.attr & AM_DIR) { /* Is it a >>>> directory? */ >>>> + res = FR_NO_FILE; >>>> + } >>>> + } >>>> + } >>>> +#endif >>>> + >>>> + if (res == FR_OK) { >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { >>>> + fp->obj.c_scl = >>>> dj.obj.sclust; /* Get containing >>>> directory info */ >>>> + fp->obj.c_size = ((DWORD)dj.obj.objsize & >>>> 0xFFFFFF00) | dj.obj.stat; >>>> + fp->obj.c_ofs = dj.blk_ofs; >>>> + init_alloc_info(fs, &fp->obj); >>>> + } else >>>> +#endif >>>> + { >>>> + fp->obj.sclust = ld_clust(fs, >>>> dj.dir); /* Get object allocation info */ >>>> + fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize); >>>> + } >>>> +#if FF_USE_FASTSEEK >>>> + fp->cltbl = 0; /* Disable fast seek mode */ >>>> +#endif >>>> + fp->obj.fs = fs; /* Validate the file object */ >>>> + fp->obj.id = fs->id; >>>> + fp->flag = mode; /* Set file access mode */ >>>> + fp->err = 0; /* Clear error flag */ >>>> + fp->sect = 0; /* Invalidate current data >>>> sector */ >>>> + fp->fptr = 0; /* Set file pointer top of the >>>> file */ >>>> +#if !FF_FS_READONLY >>>> +#if !FF_FS_TINY >>>> + mem_set(fp->buf, 0, sizeof fp->buf); /* Clear sector >>>> buffer */ >>>> +#endif >>>> + if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* >>>> Seek to end of file if FA_OPEN_APPEND is specified */ >>>> + fp->fptr = fp->obj.objsize; /* Offset to >>>> seek */ >>>> + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size >>>> in byte */ >>>> + clst = fp->obj.sclust; /* Follow the >>>> cluster chain */ >>>> + for (ofs = fp->obj.objsize; res == FR_OK && ofs > >>>> bcs; ofs -= bcs) { >>>> + clst = get_fat(&fp->obj, clst); >>>> + if (clst <= 1) res = FR_INT_ERR; >>>> + if (clst == 0xFFFFFFFF) res = FR_DISK_ERR; >>>> + } >>>> + fp->clust = clst; >>>> + if (res == FR_OK && ofs % SS(fs)) { /* Fill >>>> sector buffer if not on the sector boundary */ >>>> + sc = clst2sect(fs, clst); >>>> + if (sc == 0) { >>>> + res = FR_INT_ERR; >>>> + } else { >>>> + fp->sect = sc + (DWORD)(ofs / SS(fs)); >>>> +#if !FF_FS_TINY >>>> + if (disk_read(fs->pdrv, fp->buf, fp->sect, >>>> 1) != RES_OK) res = FR_DISK_ERR; >>>> +#endif >>>> + } >>>> + } >>>> + } >>>> +#endif >>>> + } >>>> + >>>> + FREE_NAMBUF(); >>>> + } >>>> + >>>> + if (res != FR_OK) fp->obj.fs = 0; /* Invalidate file object >>>> on error */ >>>> + >>>> + LEAVE_FF(fs, res); >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Read >>>> File */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_read ( >>>> + FIL* fp, /* Pointer to the file object */ >>>> + void* buff, /* Pointer to data buffer */ >>>> + UINT btr, /* Number of bytes to read */ >>>> + UINT* br /* Pointer to number of bytes read */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + FATFS *fs; >>>> + DWORD clst; >>>> + LBA_t sect; >>>> + FSIZE_t remain; >>>> + UINT rcnt, cc, csect; >>>> + BYTE *rbuff = (BYTE*)buff; >>>> + >>>> + >>>> + *br = 0; /* Clear read byte counter */ >>>> + res = validate(&fp->obj, &fs); /* Check validity >>>> of the file object */ >>>> + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) >>>> LEAVE_FF(fs, res); /* Check validity */ >>>> + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check >>>> access mode */ >>>> + remain = fp->obj.objsize - fp->fptr; >>>> + if (btr > remain) btr = (UINT)remain; /* Truncate btr by >>>> remaining bytes */ >>>> + >>>> + for ( ; btr; /* Repeat until >>>> btr bytes read */ >>>> + btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) { >>>> + if (fp->fptr % SS(fs) == 0) { /* On the sector >>>> boundary? */ >>>> + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* >>>> Sector offset in the cluster */ >>>> + if (csect == 0) { /* On the cluster >>>> boundary? */ >>>> + if (fp->fptr == 0) { /* On the top of >>>> the file? */ >>>> + clst = fp->obj.sclust; /* Follow cluster >>>> chain from the origin */ >>>> + } else { /* Middle or end of >>>> the file */ >>>> +#if FF_USE_FASTSEEK >>>> + if (fp->cltbl) { >>>> + clst = clmt_clust(fp, fp->fptr); /* Get >>>> cluster# from the CLMT */ >>>> + } else >>>> +#endif >>>> + { >>>> + clst = get_fat(&fp->obj, fp->clust); /* >>>> Follow cluster chain on the FAT */ >>>> + } >>>> + } >>>> + if (clst < 2) ABORT(fs, FR_INT_ERR); >>>> + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); >>>> + fp->clust = clst; /* Update current >>>> cluster */ >>>> + } >>>> + sect = clst2sect(fs, fp->clust); /* Get current >>>> sector */ >>>> + if (sect == 0) ABORT(fs, FR_INT_ERR); >>>> + sect += csect; >>>> + cc = btr / SS(fs); /* When remaining >>>> bytes >= sector size, */ >>>> + if (cc > 0) { /* Read maximum >>>> contiguous sectors directly */ >>>> + if (csect + cc > fs->csize) { /* Clip at cluster >>>> boundary */ >>>> + cc = fs->csize - csect; >>>> + } >>>> + if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) >>>> ABORT(fs, FR_DISK_ERR); >>>> +#if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of >>>> the read sectors with cached data if it contains a dirty sector */ >>>> +#if FF_FS_TINY >>>> + if (fs->wflag && fs->winsect - sect < cc) { >>>> + mem_cpy(rbuff + ((fs->winsect - sect) * >>>> SS(fs)), fs->win, SS(fs)); >>>> + } >>>> +#else >>>> + if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) { >>>> + mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), >>>> fp->buf, SS(fs)); >>>> + } >>>> +#endif >>>> +#endif >>>> + rcnt = SS(fs) * cc; /* Number of >>>> bytes transferred */ >>>> + continue; >>>> + } >>>> +#if !FF_FS_TINY >>>> + if (fp->sect != sect) { /* Load data sector >>>> if not in cache */ >>>> +#if !FF_FS_READONLY >>>> + if (fp->flag & FA_DIRTY) { /* Write-back >>>> dirty sector cache */ >>>> + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) >>>> != RES_OK) ABORT(fs, FR_DISK_ERR); >>>> + fp->flag &= (BYTE)~FA_DIRTY; >>>> + } >>>> +#endif >>>> + if (disk_read(fs->pdrv, fp->buf, sect, 1) != >>>> RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ >>>> + } >>>> +#endif >>>> + fp->sect = sect; >>>> + } >>>> + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of >>>> bytes remains in the sector */ >>>> + if (rcnt > btr) rcnt = btr; /* Clip it >>>> by btr if needed */ >>>> +#if FF_FS_TINY >>>> + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, >>>> FR_DISK_ERR); /* Move sector window */ >>>> + mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* >>>> Extract partial sector */ >>>> +#else >>>> + mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* >>>> Extract partial sector */ >>>> +#endif >>>> + } >>>> + >>>> + LEAVE_FF(fs, FR_OK); >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +#if !FF_FS_READONLY >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Write >>>> File */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_write ( >>>> + FIL* fp, /* Pointer to the file object */ >>>> + const void* buff, /* Pointer to the data to be written */ >>>> + UINT btw, /* Number of bytes to write */ >>>> + UINT* bw /* Pointer to number of bytes written */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + FATFS *fs; >>>> + DWORD clst; >>>> + LBA_t sect; >>>> + UINT wcnt, cc, csect; >>>> + const BYTE *wbuff = (const BYTE*)buff; >>>> + >>>> + >>>> + *bw = 0; /* Clear write byte counter */ >>>> + res = validate(&fp->obj, &fs); /* Check validity of >>>> the file object */ >>>> + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) >>>> LEAVE_FF(fs, res); /* Check validity */ >>>> + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* >>>> Check access mode */ >>>> + >>>> + /* Check fptr wrap-around (file size cannot reach 4 GiB at FAT >>>> volume) */ >>>> + if ((!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) && >>>> (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) { >>>> + btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr); >>>> + } >>>> + >>>> + for ( ; btw; /* Repeat until all >>>> data written */ >>>> + btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, >>>> fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : >>>> fp->obj.objsize) { >>>> + if (fp->fptr % SS(fs) == 0) { /* On the sector >>>> boundary? */ >>>> + csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* >>>> Sector offset in the cluster */ >>>> + if (csect == 0) { /* On the cluster >>>> boundary? */ >>>> + if (fp->fptr == 0) { /* On the top of the >>>> file? */ >>>> + clst = fp->obj.sclust; /* Follow from the >>>> origin */ >>>> + if (clst == 0) { /* If no cluster is >>>> allocated, */ >>>> + clst = create_chain(&fp->obj, 0); /* >>>> create a new cluster chain */ >>>> + } >>>> + } else { /* On the middle or end >>>> of the file */ >>>> +#if FF_USE_FASTSEEK >>>> + if (fp->cltbl) { >>>> + clst = clmt_clust(fp, fp->fptr); /* Get >>>> cluster# from the CLMT */ >>>> + } else >>>> +#endif >>>> + { >>>> + clst = create_chain(&fp->obj, fp->clust); >>>> /* Follow or stretch cluster chain on the FAT */ >>>> + } >>>> + } >>>> + if (clst == 0) break; /* Could not allocate >>>> a new cluster (disk full) */ >>>> + if (clst == 1) ABORT(fs, FR_INT_ERR); >>>> + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); >>>> + fp->clust = clst; /* Update current >>>> cluster */ >>>> + if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* >>>> Set start cluster if the first write */ >>>> + } >>>> +#if FF_FS_TINY >>>> + if (fs->winsect == fp->sect && sync_window(fs) != >>>> FR_OK) ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */ >>>> +#else >>>> + if (fp->flag & FA_DIRTY) { /* Write-back sector >>>> cache */ >>>> + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != >>>> RES_OK) ABORT(fs, FR_DISK_ERR); >>>> + fp->flag &= (BYTE)~FA_DIRTY; >>>> + } >>>> +#endif >>>> + sect = clst2sect(fs, fp->clust); /* Get current >>>> sector */ >>>> + if (sect == 0) ABORT(fs, FR_INT_ERR); >>>> + sect += csect; >>>> + cc = btw / SS(fs); /* When remaining >>>> bytes >= sector size, */ >>>> + if (cc > 0) { /* Write maximum >>>> contiguous sectors directly */ >>>> + if (csect + cc > fs->csize) { /* Clip at cluster >>>> boundary */ >>>> + cc = fs->csize - csect; >>>> + } >>>> + if (disk_write(fs->pdrv, wbuff, sect, cc) != >>>> RES_OK) ABORT(fs, FR_DISK_ERR); >>>> +#if FF_FS_MINIMIZE <= 2 >>>> +#if FF_FS_TINY >>>> + if (fs->winsect - sect < cc) { /* Refill sector >>>> cache if it gets invalidated by the direct write */ >>>> + mem_cpy(fs->win, wbuff + ((fs->winsect - sect) >>>> * SS(fs)), SS(fs)); >>>> + fs->wflag = 0; >>>> + } >>>> +#else >>>> + if (fp->sect - sect < cc) { /* Refill sector cache >>>> if it gets invalidated by the direct write */ >>>> + mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * >>>> SS(fs)), SS(fs)); >>>> + fp->flag &= (BYTE)~FA_DIRTY; >>>> + } >>>> +#endif >>>> +#endif >>>> + wcnt = SS(fs) * cc; /* Number of bytes >>>> transferred */ >>>> + continue; >>>> + } >>>> +#if FF_FS_TINY >>>> + if (fp->fptr >= fp->obj.objsize) { /* Avoid silly >>>> cache filling on the growing edge */ >>>> + if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); >>>> + fs->winsect = sect; >>>> + } >>>> +#else >>>> + if (fp->sect != sect && /* Fill sector cache >>>> with file data */ >>>> + fp->fptr < fp->obj.objsize && >>>> + disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) { >>>> + ABORT(fs, FR_DISK_ERR); >>>> + } >>>> +#endif >>>> + fp->sect = sect; >>>> + } >>>> + wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of >>>> bytes remains in the sector */ >>>> + if (wcnt > btw) wcnt = btw; /* Clip it >>>> by btw if needed */ >>>> +#if FF_FS_TINY >>>> + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, >>>> FR_DISK_ERR); /* Move sector window */ >>>> + mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* >>>> Fit data to the sector */ >>>> + fs->wflag = 1; >>>> +#else >>>> + mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* >>>> Fit data to the sector */ >>>> + fp->flag |= FA_DIRTY; >>>> +#endif >>>> + } >>>> + >>>> + fp->flag |= FA_MODIFIED; /* Set file change flag */ >>>> + >>>> + LEAVE_FF(fs, FR_OK); >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Synchronize the >>>> File */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_sync ( >>>> + FIL* fp /* Pointer to the file object */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + FATFS *fs; >>>> + DWORD tm; >>>> + BYTE *dir; >>>> + >>>> + >>>> + res = validate(&fp->obj, &fs); /* Check validity of the file >>>> object */ >>>> + if (res == FR_OK) { >>>> + if (fp->flag & FA_MODIFIED) { /* Is there any change to >>>> the file? */ >>>> +#if !FF_FS_TINY >>>> + if (fp->flag & FA_DIRTY) { /* Write-back cached data >>>> if needed */ >>>> + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != >>>> RES_OK) LEAVE_FF(fs, FR_DISK_ERR); >>>> + fp->flag &= (BYTE)~FA_DIRTY; >>>> + } >>>> +#endif >>>> + /* Update the directory entry */ >>>> + tm = GET_FATTIME(); /* Modified time */ >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { >>>> + res = fill_first_frag(&fp->obj); /* Fill first >>>> fragment on the FAT if needed */ >>>> + if (res == FR_OK) { >>>> + res = fill_last_frag(&fp->obj, fp->clust, >>>> 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */ >>>> + } >>>> + if (res == FR_OK) { >>>> + DIR dj; >>>> + DEF_NAMBUF >>>> + >>>> + INIT_NAMBUF(fs); >>>> + res = load_obj_xdir(&dj, &fp->obj); /* Load >>>> directory entry block */ >>>> + if (res == FR_OK) { >>>> + fs->dirbuf[XDIR_Attr] |= >>>> AM_ARC; /* Set archive attribute to indicate that the >>>> file has been changed */ >>>> + fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | >>>> 1; /* Update file allocation information */ >>>> + st_dword(fs->dirbuf + XDIR_FstClus, >>>> fp->obj.sclust); /* Update start cluster */ >>>> + st_qword(fs->dirbuf + XDIR_FileSize, >>>> fp->obj.objsize); /* Update file size */ >>>> + st_qword(fs->dirbuf + XDIR_ValidFileSize, >>>> fp->obj.objsize); /* (FatFs does not support Valid File Size >>>> feature) */ >>>> + st_dword(fs->dirbuf + XDIR_ModTime, >>>> tm); /* Update modified time */ >>>> + fs->dirbuf[XDIR_ModTime10] = 0; >>>> + st_dword(fs->dirbuf + XDIR_AccTime, 0); >>>> + res = store_xdir(&dj); /* Restore it to >>>> the directory */ >>>> + if (res == FR_OK) { >>>> + res = sync_fs(fs); >>>> + fp->flag &= (BYTE)~FA_MODIFIED; >>>> + } >>>> + } >>>> + FREE_NAMBUF(); >>>> + } >>>> + } else >>>> +#endif >>>> + { >>>> + res = move_window(fs, fp->dir_sect); >>>> + if (res == FR_OK) { >>>> + dir = fp->dir_ptr; >>>> + dir[DIR_Attr] |= AM_ARC; /* Set archive >>>> attribute to indicate that the file has been changed */ >>>> + st_clust(fp->obj.fs, dir, fp->obj.sclust); /* >>>> Update file allocation information */ >>>> + st_dword(dir + DIR_FileSize, >>>> (DWORD)fp->obj.objsize); /* Update file size */ >>>> + st_dword(dir + DIR_ModTime, tm); /* Update >>>> modified time */ >>>> + st_word(dir + DIR_LstAccDate, 0); >>>> + fs->wflag = 1; >>>> + res = sync_fs(fs); /* >>>> Restore it to the directory */ >>>> + fp->flag &= (BYTE)~FA_MODIFIED; >>>> + } >>>> + } >>>> + } >>>> + } >>>> + >>>> + LEAVE_FF(fs, res); >>>> +} >>>> + >>>> +#endif /* !FF_FS_READONLY */ >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Close >>>> File */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_close ( >>>> + FIL* fp /* Pointer to the file object to be closed */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + FATFS *fs; >>>> + >>>> +#if !FF_FS_READONLY >>>> + res = f_sync(fp); /* Flush cached data */ >>>> + if (res == FR_OK) >>>> +#endif >>>> + { >>>> + res = validate(&fp->obj, &fs); /* Lock volume */ >>>> + if (res == FR_OK) { >>>> +#if FF_FS_LOCK != 0 >>>> + res = dec_lock(fp->obj.lockid); /* Decrement >>>> file open counter */ >>>> + if (res == FR_OK) fp->obj.fs = 0; /* Invalidate file >>>> object */ >>>> +#else >>>> + fp->obj.fs = 0; /* Invalidate file object */ >>>> +#endif >>>> +#if FF_FS_REENTRANT >>>> + unlock_fs(fs, FR_OK); /* Unlock volume */ >>>> +#endif >>>> + } >>>> + } >>>> + return res; >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +#if FF_FS_RPATH >= 1 >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Change Current Directory or Current Drive, Get Current >>>> Directory */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_chdrive ( >>>> + const TCHAR* path /* Drive number to set */ >>>> +) >>>> +{ >>>> + int vol; >>>> + >>>> + >>>> + /* Get logical drive number */ >>>> + vol = get_ldnumber(&path); >>>> + if (vol < 0) return FR_INVALID_DRIVE; >>>> + CurrVol = (BYTE)vol; /* Set it as current volume */ >>>> + >>>> + return FR_OK; >>>> +} >>>> + >>>> + >>>> + >>>> +FRESULT f_chdir ( >>>> + const TCHAR* path /* Pointer to the directory path */ >>>> +) >>>> +{ >>>> +#if FF_STR_VOLUME_ID == 2 >>>> + UINT i; >>>> +#endif >>>> + FRESULT res; >>>> + DIR dj; >>>> + FATFS *fs; >>>> + DEF_NAMBUF >>>> + >>>> + >>>> + /* Get logical drive */ >>>> + res = mount_volume(&path, &fs, 0); >>>> + if (res == FR_OK) { >>>> + dj.obj.fs = fs; >>>> + INIT_NAMBUF(fs); >>>> + res = follow_path(&dj, path); /* Follow the path */ >>>> + if (res == FR_OK) { /* Follow completed */ >>>> + if (dj.fn[NSFLAG] & NS_NONAME) { /* Is it the start >>>> directory itself? */ >>>> + fs->cdir = dj.obj.sclust; >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { >>>> + fs->cdc_scl = dj.obj.c_scl; >>>> + fs->cdc_size = dj.obj.c_size; >>>> + fs->cdc_ofs = dj.obj.c_ofs; >>>> + } >>>> +#endif >>>> + } else { >>>> + if (dj.obj.attr & AM_DIR) { /* It is a >>>> sub-directory */ >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { >>>> + fs->cdir = ld_dword(fs->dirbuf + >>>> XDIR_FstClus); /* Sub-directory cluster */ >>>> + fs->cdc_scl = >>>> dj.obj.sclust; /* Save containing directory >>>> information */ >>>> + fs->cdc_size = ((DWORD)dj.obj.objsize & >>>> 0xFFFFFF00) | dj.obj.stat; >>>> + fs->cdc_ofs = dj.blk_ofs; >>>> + } else >>>> +#endif >>>> + { >>>> + fs->cdir = ld_clust(fs, >>>> dj.dir); /* Sub-directory cluster */ >>>> + } >>>> + } else { >>>> + res = FR_NO_PATH; /* Reached but a file */ >>>> + } >>>> + } >>>> + } >>>> + FREE_NAMBUF(); >>>> + if (res == FR_NO_FILE) res = FR_NO_PATH; >>>> +#if FF_STR_VOLUME_ID == 2 /* Also current drive is changed at >>>> Unix style volume ID */ >>>> + if (res == FR_OK) { >>>> + for (i = FF_VOLUMES - 1; i && fs != FatFs[i]; i--) ; /* >>>> Set current drive */ >>>> + CurrVol = (BYTE)i; >>>> + } >>>> +#endif >>>> + } >>>> + >>>> + LEAVE_FF(fs, res); >>>> +} >>>> + >>>> + >>>> +#if FF_FS_RPATH >= 2 >>>> +FRESULT f_getcwd ( >>>> + TCHAR* buff, /* Pointer to the directory path */ >>>> + UINT len /* Size of buff in unit of TCHAR */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + DIR dj; >>>> + FATFS *fs; >>>> + UINT i, n; >>>> + DWORD ccl; >>>> + TCHAR *tp = buff; >>>> +#if FF_VOLUMES >= 2 >>>> + UINT vl; >>>> +#if FF_STR_VOLUME_ID >>>> + const char *vp; >>>> +#endif >>>> +#endif >>>> + FILINFO fno; >>>> + DEF_NAMBUF >>>> + >>>> + >>>> + /* Get logical drive */ >>>> + buff[0] = 0; /* Set null string to get current volume */ >>>> + res = mount_volume((const TCHAR**)&buff, &fs, 0); /* Get >>>> current volume */ >>>> + if (res == FR_OK) { >>>> + dj.obj.fs = fs; >>>> + INIT_NAMBUF(fs); >>>> + >>>> + /* Follow parent directories and create the path */ >>>> + i = len; /* Bottom of buffer (directory stack >>>> base) */ >>>> + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* >>>> (Cannot do getcwd on exFAT and returns root path) */ >>>> + dj.obj.sclust = fs->cdir; /* Start to >>>> follow upper directory from current directory */ >>>> + while ((ccl = dj.obj.sclust) != 0) { /* Repeat while >>>> current directory is a sub-directory */ >>>> + res = dir_sdi(&dj, 1 * SZDIRE); /* Get parent >>>> directory */ >>>> + if (res != FR_OK) break; >>>> + res = move_window(fs, dj.sect); >>>> + if (res != FR_OK) break; >>>> + dj.obj.sclust = ld_clust(fs, dj.dir); /* Goto >>>> parent directory */ >>>> + res = dir_sdi(&dj, 0); >>>> + if (res != FR_OK) break; >>>> + do { /* Find the entry >>>> links to the child directory */ >>>> + res = DIR_READ_FILE(&dj); >>>> + if (res != FR_OK) break; >>>> + if (ccl == ld_clust(fs, dj.dir)) break; /* >>>> Found the entry */ >>>> + res = dir_next(&dj, 0); >>>> + } while (res == FR_OK); >>>> + if (res == FR_NO_FILE) res = FR_INT_ERR;/* It >>>> cannot be 'not found'. */ >>>> + if (res != FR_OK) break; >>>> + get_fileinfo(&dj, &fno); /* Get the >>>> directory name and push it to the buffer */ >>>> + for (n = 0; fno.fname[n]; n++) ; /* Name length */ >>>> + if (i < n + 1) { /* Insufficient space to store >>>> the path name? */ >>>> + res = FR_NOT_ENOUGH_CORE; break; >>>> + } >>>> + while (n) buff[--i] = fno.fname[--n]; /* Stack >>>> the name */ >>>> + buff[--i] = '/'; >>>> + } >>>> + } >>>> + if (res == FR_OK) { >>>> + if (i == len) buff[--i] = '/'; /* Is it the >>>> root-directory? */ >>>> +#if FF_VOLUMES >= 2 /* Put drive prefix */ >>>> + vl = 0; >>>> +#if FF_STR_VOLUME_ID >= 1 /* String volume ID */ >>>> + for (n = 0, vp = (const char*)VolumeStr[CurrVol]; >>>> vp[n]; n++) ; >>>> + if (i >= n + 2) { >>>> + if (FF_STR_VOLUME_ID == 2) *tp++ = (TCHAR)'/'; >>>> + for (vl = 0; vl < n; *tp++ = (TCHAR)vp[vl], vl++) ; >>>> + if (FF_STR_VOLUME_ID == 1) *tp++ = (TCHAR)':'; >>>> + vl++; >>>> + } >>>> +#else /* Numeric volume ID */ >>>> + if (i >= 3) { >>>> + *tp++ = (TCHAR)'0' + CurrVol; >>>> + *tp++ = (TCHAR)':'; >>>> + vl = 2; >>>> + } >>>> +#endif >>>> + if (vl == 0) res = FR_NOT_ENOUGH_CORE; >>>> +#endif >>>> + /* Add current directory path */ >>>> + if (res == FR_OK) { >>>> + do *tp++ = buff[i++]; while (i < len); /* Copy >>>> stacked path string */ >>>> + } >>>> + } >>>> + FREE_NAMBUF(); >>>> + } >>>> + >>>> + *tp = 0; >>>> + LEAVE_FF(fs, res); >>>> +} >>>> + >>>> +#endif /* FF_FS_RPATH >= 2 */ >>>> +#endif /* FF_FS_RPATH >= 1 */ >>>> + >>>> + >>>> + >>>> +#if FF_FS_MINIMIZE <= 2 >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Seek File Read/Write >>>> Pointer */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_lseek ( >>>> + FIL* fp, /* Pointer to the file object */ >>>> + FSIZE_t ofs /* File pointer from top of file */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + FATFS *fs; >>>> + DWORD clst, bcs; >>>> + LBA_t nsect; >>>> + FSIZE_t ifptr; >>>> +#if FF_USE_FASTSEEK >>>> + DWORD cl, pcl, ncl, tcl, tlen, ulen, *tbl; >>>> + LBA_t dsc; >>>> +#endif >>>> + >>>> + res = validate(&fp->obj, &fs); /* Check validity of the >>>> file object */ >>>> + if (res == FR_OK) res = (FRESULT)fp->err; >>>> +#if FF_FS_EXFAT && !FF_FS_READONLY >>>> + if (res == FR_OK && fs->fs_type == FS_EXFAT) { >>>> + res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); >>>> /* Fill last fragment on the FAT if needed */ >>>> + } >>>> +#endif >>>> + if (res != FR_OK) LEAVE_FF(fs, res); >>>> + >>>> +#if FF_USE_FASTSEEK >>>> + if (fp->cltbl) { /* Fast seek */ >>>> + if (ofs == CREATE_LINKMAP) { /* Create CLMT */ >>>> + tbl = fp->cltbl; >>>> + tlen = *tbl++; ulen = 2; /* Given table size and >>>> required table size */ >>>> + cl = fp->obj.sclust; /* Origin of the chain */ >>>> + if (cl != 0) { >>>> + do { >>>> + /* Get a fragment */ >>>> + tcl = cl; ncl = 0; ulen += 2; /* Top, length >>>> and used items */ >>>> + do { >>>> + pcl = cl; ncl++; >>>> + cl = get_fat(&fp->obj, cl); >>>> + if (cl <= 1) ABORT(fs, FR_INT_ERR); >>>> + if (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); >>>> + } while (cl == pcl + 1); >>>> + if (ulen <= tlen) { /* Store the length >>>> and top of the fragment */ >>>> + *tbl++ = ncl; *tbl++ = tcl; >>>> + } >>>> + } while (cl < fs->n_fatent); /* Repeat until end >>>> of chain */ >>>> + } >>>> + *fp->cltbl = ulen; /* Number of items used */ >>>> + if (ulen <= tlen) { >>>> + *tbl = 0; /* Terminate table */ >>>> + } else { >>>> + res = FR_NOT_ENOUGH_CORE; /* Given table size is >>>> smaller than required */ >>>> + } >>>> + } else { /* Fast seek */ >>>> + if (ofs > fp->obj.objsize) ofs = fp->obj.objsize; /* >>>> Clip offset at the file size */ >>>> + fp->fptr = ofs; /* Set file pointer */ >>>> + if (ofs > 0) { >>>> + fp->clust = clmt_clust(fp, ofs - 1); >>>> + dsc = clst2sect(fs, fp->clust); >>>> + if (dsc == 0) ABORT(fs, FR_INT_ERR); >>>> + dsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1); >>>> + if (fp->fptr % SS(fs) && dsc != fp->sect) { /* >>>> Refill sector cache if needed */ >>>> +#if !FF_FS_TINY >>>> +#if !FF_FS_READONLY >>>> + if (fp->flag & FA_DIRTY) { /* Write-back >>>> dirty sector cache */ >>>> + if (disk_write(fs->pdrv, fp->buf, fp->sect, >>>> 1) != RES_OK) ABORT(fs, FR_DISK_ERR); >>>> + fp->flag &= (BYTE)~FA_DIRTY; >>>> + } >>>> +#endif >>>> + if (disk_read(fs->pdrv, fp->buf, dsc, 1) != >>>> RES_OK) ABORT(fs, FR_DISK_ERR); /* Load current sector */ >>>> +#endif >>>> + fp->sect = dsc; >>>> + } >>>> + } >>>> + } >>>> + } else >>>> +#endif >>>> + >>>> + /* Normal Seek */ >>>> + { >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = >>>> 0xFFFFFFFF; /* Clip at 4 GiB - 1 if at FATxx */ >>>> +#endif >>>> + if (ofs > fp->obj.objsize && (FF_FS_READONLY || !(fp->flag >>>> & FA_WRITE))) { /* In read-only mode, clip offset with the file >>>> size */ >>>> + ofs = fp->obj.objsize; >>>> + } >>>> + ifptr = fp->fptr; >>>> + fp->fptr = nsect = 0; >>>> + if (ofs > 0) { >>>> + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size >>>> (byte) */ >>>> + if (ifptr > 0 && >>>> + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When >>>> seek to same or following cluster, */ >>>> + fp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1); /* >>>> start from the current cluster */ >>>> + ofs -= fp->fptr; >>>> + clst = fp->clust; >>>> + } else { /* When >>>> seek to back cluster, */ >>>> + clst = fp->obj.sclust; /* start >>>> from the first cluster */ >>>> +#if !FF_FS_READONLY >>>> + if (clst == 0) { /* If no >>>> cluster chain, create a new chain */ >>>> + clst = create_chain(&fp->obj, 0); >>>> + if (clst == 1) ABORT(fs, FR_INT_ERR); >>>> + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); >>>> + fp->obj.sclust = clst; >>>> + } >>>> +#endif >>>> + fp->clust = clst; >>>> + } >>>> + if (clst != 0) { >>>> + while (ofs > bcs) { /* >>>> Cluster following loop */ >>>> + ofs -= bcs; fp->fptr += bcs; >>>> +#if !FF_FS_READONLY >>>> + if (fp->flag & FA_WRITE) { /* Check >>>> if in write mode or not */ >>>> + if (FF_FS_EXFAT && fp->fptr > >>>> fp->obj.objsize) { /* No FAT chain object needs correct objsize >>>> to generate FAT value */ >>>> + fp->obj.objsize = fp->fptr; >>>> + fp->flag |= FA_MODIFIED; >>>> + } >>>> + clst = create_chain(&fp->obj, clst); /* >>>> Follow chain with forceed stretch */ >>>> + if (clst == 0) { /* Clip >>>> file size in case of disk full */ >>>> + ofs = 0; break; >>>> + } >>>> + } else >>>> +#endif >>>> + { >>>> + clst = get_fat(&fp->obj, clst); /* >>>> Follow cluster chain if not in write mode */ >>>> + } >>>> + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); >>>> + if (clst <= 1 || clst >= fs->n_fatent) >>>> ABORT(fs, FR_INT_ERR); >>>> + fp->clust = clst; >>>> + } >>>> + fp->fptr += ofs; >>>> + if (ofs % SS(fs)) { >>>> + nsect = clst2sect(fs, clst); /* Current >>>> sector */ >>>> + if (nsect == 0) ABORT(fs, FR_INT_ERR); >>>> + nsect += (DWORD)(ofs / SS(fs)); >>>> + } >>>> + } >>>> + } >>>> + if (!FF_FS_READONLY && fp->fptr > fp->obj.objsize) { /* >>>> Set file change flag if the file size is extended */ >>>> + fp->obj.objsize = fp->fptr; >>>> + fp->flag |= FA_MODIFIED; >>>> + } >>>> + if (fp->fptr % SS(fs) && nsect != fp->sect) { /* Fill >>>> sector cache if needed */ >>>> +#if !FF_FS_TINY >>>> +#if !FF_FS_READONLY >>>> + if (fp->flag & FA_DIRTY) { /* Write-back >>>> dirty sector cache */ >>>> + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != >>>> RES_OK) ABORT(fs, FR_DISK_ERR); >>>> + fp->flag &= (BYTE)~FA_DIRTY; >>>> + } >>>> +#endif >>>> + if (disk_read(fs->pdrv, fp->buf, nsect, 1) != RES_OK) >>>> ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ >>>> +#endif >>>> + fp->sect = nsect; >>>> + } >>>> + } >>>> + >>>> + LEAVE_FF(fs, res); >>>> +} >>>> + >>>> + >>>> + >>>> +#if FF_FS_MINIMIZE <= 1 >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Create a Directory >>>> Object */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_opendir ( >>>> + DIR* dp, /* Pointer to directory object to create */ >>>> + const TCHAR* path /* Pointer to the directory path */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + FATFS *fs; >>>> + DEF_NAMBUF >>>> + >>>> + >>>> + if (!dp) return FR_INVALID_OBJECT; >>>> + >>>> + /* Get logical drive */ >>>> + res = mount_volume(&path, &fs, 0); >>>> + if (res == FR_OK) { >>>> + dp->obj.fs = fs; >>>> + INIT_NAMBUF(fs); >>>> + res = follow_path(dp, path); /* Follow the path >>>> to the directory */ >>>> + if (res == FR_OK) { /* Follow >>>> completed */ >>>> + if (!(dp->fn[NSFLAG] & NS_NONAME)) { /* It is not >>>> the origin directory itself */ >>>> + if (dp->obj.attr & AM_DIR) { /* This object >>>> is a sub-directory */ >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { >>>> + dp->obj.c_scl = >>>> dp->obj.sclust; /* Get containing >>>> directory inforamation */ >>>> + dp->obj.c_size = ((DWORD)dp->obj.objsize & >>>> 0xFFFFFF00) | dp->obj.stat; >>>> + dp->obj.c_ofs = dp->blk_ofs; >>>> + init_alloc_info(fs, &dp->obj); /* Get >>>> object allocation info */ >>>> + } else >>>> +#endif >>>> + { >>>> + dp->obj.sclust = ld_clust(fs, dp->dir); /* >>>> Get object allocation info */ >>>> + } >>>> + } else { /* This object is a >>>> file */ >>>> + res = FR_NO_PATH; >>>> + } >>>> + } >>>> + if (res == FR_OK) { >>>> + dp->obj.id = fs->id; >>>> + res = dir_sdi(dp, 0); /* Rewind >>>> directory */ >>>> +#if FF_FS_LOCK != 0 >>>> + if (res == FR_OK) { >>>> + if (dp->obj.sclust != 0) { >>>> + dp->obj.lockid = inc_lock(dp, 0); /* >>>> Lock the sub directory */ >>>> + if (!dp->obj.lockid) res = >>>> FR_TOO_MANY_OPEN_FILES; >>>> + } else { >>>> + dp->obj.lockid = 0; /* Root directory >>>> need not to be locked */ >>>> + } >>>> + } >>>> +#endif >>>> + } >>>> + } >>>> + FREE_NAMBUF(); >>>> + if (res == FR_NO_FILE) res = FR_NO_PATH; >>>> + } >>>> + if (res != FR_OK) dp->obj.fs = 0; /* Invalidate the >>>> directory object if function faild */ >>>> + >>>> + LEAVE_FF(fs, res); >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Close >>>> Directory */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_closedir ( >>>> + DIR *dp /* Pointer to the directory object to be closed */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + FATFS *fs; >>>> + >>>> + >>>> + res = validate(&dp->obj, &fs); /* Check validity of the file >>>> object */ >>>> + if (res == FR_OK) { >>>> +#if FF_FS_LOCK != 0 >>>> + if (dp->obj.lockid) res = dec_lock(dp->obj.lockid); /* >>>> Decrement sub-directory open counter */ >>>> + if (res == FR_OK) dp->obj.fs = 0; /* Invalidate >>>> directory object */ >>>> +#else >>>> + dp->obj.fs = 0; /* Invalidate directory object */ >>>> +#endif >>>> +#if FF_FS_REENTRANT >>>> + unlock_fs(fs, FR_OK); /* Unlock volume */ >>>> +#endif >>>> + } >>>> + return res; >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Read Directory Entries in >>>> Sequence */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_readdir ( >>>> + DIR* dp, /* Pointer to the open directory object */ >>>> + FILINFO* fno /* Pointer to file information to return */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + FATFS *fs; >>>> + DEF_NAMBUF >>>> + >>>> + >>>> + res = validate(&dp->obj, &fs); /* Check validity of the >>>> directory object */ >>>> + if (res == FR_OK) { >>>> + if (!fno) { >>>> + res = dir_sdi(dp, 0); /* Rewind the >>>> directory object */ >>>> + } else { >>>> + INIT_NAMBUF(fs); >>>> + res = DIR_READ_FILE(dp); /* Read an item */ >>>> + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of >>>> directory */ >>>> + if (res == FR_OK) { /* A valid entry is >>>> found */ >>>> + get_fileinfo(dp, fno); /* Get the object >>>> information */ >>>> + res = dir_next(dp, 0); /* Increment index >>>> for next */ >>>> + if (res == FR_NO_FILE) res = FR_OK; /* Ignore >>>> end of directory now */ >>>> + } >>>> + FREE_NAMBUF(); >>>> + } >>>> + } >>>> + LEAVE_FF(fs, res); >>>> +} >>>> + >>>> + >>>> + >>>> +#if FF_USE_FIND >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Find Next >>>> File */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_findnext ( >>>> + DIR* dp, /* Pointer to the open directory object */ >>>> + FILINFO* fno /* Pointer to the file information structure */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + >>>> + >>>> + for (;;) { >>>> + res = f_readdir(dp, fno); /* Get a directory item */ >>>> + if (res != FR_OK || !fno || !fno->fname[0]) break; /* >>>> Terminate if any error or end of directory */ >>>> + if (pattern_match(dp->pat, fno->fname, 0, FIND_RECURS)) >>>> break; /* Test for the file name */ >>>> +#if FF_USE_LFN && FF_USE_FIND == 2 >>>> + if (pattern_match(dp->pat, fno->altname, 0, FIND_RECURS)) >>>> break; /* Test for alternative name if exist */ >>>> +#endif >>>> + } >>>> + return res; >>>> +} >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Find First >>>> File */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_findfirst ( >>>> + DIR* dp, /* Pointer to the blank directory >>>> object */ >>>> + FILINFO* fno, /* Pointer to the file information >>>> structure */ >>>> + const TCHAR* path, /* Pointer to the directory to open */ >>>> + const TCHAR* pattern /* Pointer to the matching pattern */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + >>>> + >>>> + dp->pat = pattern; /* Save pointer to pattern string */ >>>> + res = f_opendir(dp, path); /* Open the target directory */ >>>> + if (res == FR_OK) { >>>> + res = f_findnext(dp, fno); /* Find the first item */ >>>> + } >>>> + return res; >>>> +} >>>> + >>>> +#endif /* FF_USE_FIND */ >>>> + >>>> + >>>> + >>>> +#if FF_FS_MINIMIZE == 0 >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Get File >>>> Status */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_stat ( >>>> + const TCHAR* path, /* Pointer to the file path */ >>>> + FILINFO* fno /* Pointer to file information to return */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + DIR dj; >>>> + DEF_NAMBUF >>>> + >>>> + >>>> + /* Get logical drive */ >>>> + res = mount_volume(&path, &dj.obj.fs, 0); >>>> + if (res == FR_OK) { >>>> + INIT_NAMBUF(dj.obj.fs); >>>> + res = follow_path(&dj, path); /* Follow the file path */ >>>> + if (res == FR_OK) { /* Follow completed */ >>>> + if (dj.fn[NSFLAG] & NS_NONAME) { /* It is origin >>>> directory */ >>>> + res = FR_INVALID_NAME; >>>> + } else { /* Found an object */ >>>> + if (fno) get_fileinfo(&dj, fno); >>>> + } >>>> + } >>>> + FREE_NAMBUF(); >>>> + } >>>> + >>>> + LEAVE_FF(dj.obj.fs, res); >>>> +} >>>> + >>>> + >>>> + >>>> +#if !FF_FS_READONLY >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Get Number of Free >>>> Clusters */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_getfree ( >>>> + const TCHAR* path, /* Logical drive number */ >>>> + DWORD* nclst, /* Pointer to a variable to return number >>>> of free clusters */ >>>> + FATFS** fatfs /* Pointer to return pointer to >>>> corresponding filesystem object */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + FATFS *fs; >>>> + DWORD nfree, clst, stat; >>>> + LBA_t sect; >>>> + UINT i; >>>> + FFOBJID obj; >>>> + >>>> + >>>> + /* Get logical drive */ >>>> + res = mount_volume(&path, &fs, 0); >>>> + if (res == FR_OK) { >>>> + *fatfs = fs; /* Return ptr to the fs object */ >>>> + /* If free_clst is valid, return it without full FAT scan */ >>>> + if (fs->free_clst <= fs->n_fatent - 2) { >>>> + *nclst = fs->free_clst; >>>> + } else { >>>> + /* Scan FAT to obtain number of free clusters */ >>>> + nfree = 0; >>>> + if (fs->fs_type == FS_FAT12) { /* FAT12: Scan bit >>>> field FAT entries */ >>>> + clst = 2; obj.fs = fs; >>>> + do { >>>> + stat = get_fat(&obj, clst); >>>> + if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; >>>> break; } >>>> + if (stat == 1) { res = FR_INT_ERR; break; } >>>> + if (stat == 0) nfree++; >>>> + } while (++clst < fs->n_fatent); >>>> + } else { >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { /* exFAT: Scan >>>> allocation bitmap */ >>>> + BYTE bm; >>>> + UINT b; >>>> + >>>> + clst = fs->n_fatent - 2; /* Number of >>>> clusters */ >>>> + sect = fs->bitbase; /* Bitmap sector */ >>>> + i = 0; /* Offset in the >>>> sector */ >>>> + do { /* Counts numbuer of bits with zero in >>>> the bitmap */ >>>> + if (i == 0) { >>>> + res = move_window(fs, sect++); >>>> + if (res != FR_OK) break; >>>> + } >>>> + for (b = 8, bm = fs->win[i]; b && clst; >>>> b--, clst--) { >>>> + if (!(bm & 1)) nfree++; >>>> + bm >>= 1; >>>> + } >>>> + i = (i + 1) % SS(fs); >>>> + } while (clst); >>>> + } else >>>> +#endif >>>> + { /* FAT16/32: Scan WORD/DWORD FAT entries */ >>>> + clst = fs->n_fatent; /* Number of entries */ >>>> + sect = fs->fatbase; /* Top of the FAT */ >>>> + i = 0; /* Offset in the >>>> sector */ >>>> + do { /* Counts numbuer of entries with zero >>>> in the FAT */ >>>> + if (i == 0) { >>>> + res = move_window(fs, sect++); >>>> + if (res != FR_OK) break; >>>> + } >>>> + if (fs->fs_type == FS_FAT16) { >>>> + if (ld_word(fs->win + i) == 0) nfree++; >>>> + i += 2; >>>> + } else { >>>> + if ((ld_dword(fs->win + i) & >>>> 0x0FFFFFFF) == 0) nfree++; >>>> + i += 4; >>>> + } >>>> + i %= SS(fs); >>>> + } while (--clst); >>>> + } >>>> + } >>>> + *nclst = nfree; /* Return the free clusters */ >>>> + fs->free_clst = nfree; /* Now free_clst is valid */ >>>> + fs->fsi_flag |= 1; /* FAT32: FSInfo is to be >>>> updated */ >>>> + } >>>> + } >>>> + >>>> + LEAVE_FF(fs, res); >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Truncate >>>> File */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_truncate ( >>>> + FIL* fp /* Pointer to the file object */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + FATFS *fs; >>>> + DWORD ncl; >>>> + >>>> + >>>> + res = validate(&fp->obj, &fs); /* Check validity of the file >>>> object */ >>>> + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) >>>> LEAVE_FF(fs, res); >>>> + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* >>>> Check access mode */ >>>> + >>>> + if (fp->fptr < fp->obj.objsize) { /* Process when fptr is >>>> not on the eof */ >>>> + if (fp->fptr == 0) { /* When set file size to zero, >>>> remove entire cluster chain */ >>>> + res = remove_chain(&fp->obj, fp->obj.sclust, 0); >>>> + fp->obj.sclust = 0; >>>> + } else { /* When truncate a part of the >>>> file, remove remaining clusters */ >>>> + ncl = get_fat(&fp->obj, fp->clust); >>>> + res = FR_OK; >>>> + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; >>>> + if (ncl == 1) res = FR_INT_ERR; >>>> + if (res == FR_OK && ncl < fs->n_fatent) { >>>> + res = remove_chain(&fp->obj, ncl, fp->clust); >>>> + } >>>> + } >>>> + fp->obj.objsize = fp->fptr; /* Set file size to current >>>> read/write point */ >>>> + fp->flag |= FA_MODIFIED; >>>> +#if !FF_FS_TINY >>>> + if (res == FR_OK && (fp->flag & FA_DIRTY)) { >>>> + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != >>>> RES_OK) { >>>> + res = FR_DISK_ERR; >>>> + } else { >>>> + fp->flag &= (BYTE)~FA_DIRTY; >>>> + } >>>> + } >>>> +#endif >>>> + if (res != FR_OK) ABORT(fs, res); >>>> + } >>>> + >>>> + LEAVE_FF(fs, res); >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Delete a >>>> File/Directory */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_unlink ( >>>> + const TCHAR* path /* Pointer to the file or directory >>>> path */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + DIR dj, sdj; >>>> + DWORD dclst = 0; >>>> + FATFS *fs; >>>> +#if FF_FS_EXFAT >>>> + FFOBJID obj; >>>> +#endif >>>> + DEF_NAMBUF >>>> + >>>> + >>>> + /* Get logical drive */ >>>> + res = mount_volume(&path, &fs, FA_WRITE); >>>> + if (res == FR_OK) { >>>> + dj.obj.fs = fs; >>>> + INIT_NAMBUF(fs); >>>> + res = follow_path(&dj, path); /* Follow the file >>>> path */ >>>> + if (FF_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) { >>>> + res = FR_INVALID_NAME; /* Cannot remove dot >>>> entry */ >>>> + } >>>> +#if FF_FS_LOCK != 0 >>>> + if (res == FR_OK) res = chk_lock(&dj, 2); /* Check if it >>>> is an open object */ >>>> +#endif >>>> + if (res == FR_OK) { /* The object is >>>> accessible */ >>>> + if (dj.fn[NSFLAG] & NS_NONAME) { >>>> + res = FR_INVALID_NAME; /* Cannot remove the >>>> origin directory */ >>>> + } else { >>>> + if (dj.obj.attr & AM_RDO) { >>>> + res = FR_DENIED; /* Cannot remove R/O >>>> object */ >>>> + } >>>> + } >>>> + if (res == FR_OK) { >>>> +#if FF_FS_EXFAT >>>> + obj.fs = fs; >>>> + if (fs->fs_type == FS_EXFAT) { >>>> + init_alloc_info(fs, &obj); >>>> + dclst = obj.sclust; >>>> + } else >>>> +#endif >>>> + { >>>> + dclst = ld_clust(fs, dj.dir); >>>> + } >>>> + if (dj.obj.attr & AM_DIR) { /* Is it a >>>> sub-directory? */ >>>> +#if FF_FS_RPATH != 0 >>>> + if (dclst == fs->cdir) { /* Is it >>>> the current directory? */ >>>> + res = FR_DENIED; >>>> + } else >>>> +#endif >>>> + { >>>> + sdj.obj.fs = fs; /* Open the >>>> sub-directory */ >>>> + sdj.obj.sclust = dclst; >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { >>>> + sdj.obj.objsize = obj.objsize; >>>> + sdj.obj.stat = obj.stat; >>>> + } >>>> +#endif >>>> + res = dir_sdi(&sdj, 0); >>>> + if (res == FR_OK) { >>>> + res = DIR_READ_FILE(&sdj); >>>> /* Test if the directory is empty */ >>>> + if (res == FR_OK) res = FR_DENIED; >>>> /* Not empty? */ >>>> + if (res == FR_NO_FILE) res = FR_OK; /* >>>> Empty? */ >>>> + } >>>> + } >>>> + } >>>> + } >>>> + if (res == FR_OK) { >>>> + res = dir_remove(&dj); /* Remove the >>>> directory entry */ >>>> + if (res == FR_OK && dclst != 0) { /* Remove the >>>> cluster chain if exist */ >>>> +#if FF_FS_EXFAT >>>> + res = remove_chain(&obj, dclst, 0); >>>> +#else >>>> + res = remove_chain(&dj.obj, dclst, 0); >>>> +#endif >>>> + } >>>> + if (res == FR_OK) res = sync_fs(fs); >>>> + } >>>> + } >>>> + FREE_NAMBUF(); >>>> + } >>>> + >>>> + LEAVE_FF(fs, res); >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Create a >>>> Directory */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_mkdir ( >>>> + const TCHAR* path /* Pointer to the directory path */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + DIR dj; >>>> + FFOBJID sobj; >>>> + FATFS *fs; >>>> + DWORD dcl, pcl, tm; >>>> + DEF_NAMBUF >>>> + >>>> + >>>> + res = mount_volume(&path, &fs, FA_WRITE); /* Get logical >>>> drive */ >>>> + if (res == FR_OK) { >>>> + dj.obj.fs = fs; >>>> + INIT_NAMBUF(fs); >>>> + res = follow_path(&dj, path); /* Follow the file >>>> path */ >>>> + if (res == FR_OK) res = FR_EXIST; /* Name collision? */ >>>> + if (FF_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & >>>> NS_DOT)) { /* Invalid name? */ >>>> + res = FR_INVALID_NAME; >>>> + } >>>> + if (res == FR_NO_FILE) { /* It is clear to >>>> create a new directory */ >>>> + sobj.fs = fs; /* New object id >>>> to create a new chain */ >>>> + dcl = create_chain(&sobj, 0); /* Allocate a >>>> cluster for the new directory */ >>>> + res = FR_OK; >>>> + if (dcl == 0) res = FR_DENIED; /* No space to >>>> allocate a new cluster? */ >>>> + if (dcl == 1) res = FR_INT_ERR; /* Any insanity? */ >>>> + if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; /* Disk >>>> error? */ >>>> + tm = GET_FATTIME(); >>>> + if (res == FR_OK) { >>>> + res = dir_clear(fs, dcl); /* Clean up the >>>> new table */ >>>> + if (res == FR_OK) { >>>> + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { >>>> /* Create dot entries (FAT only) */ >>>> + mem_set(fs->win + DIR_Name, ' ', 11); /* >>>> Create "." entry */ >>>> + fs->win[DIR_Name] = '.'; >>>> + fs->win[DIR_Attr] = AM_DIR; >>>> + st_dword(fs->win + DIR_ModTime, tm); >>>> + st_clust(fs, fs->win, dcl); >>>> + mem_cpy(fs->win + SZDIRE, fs->win, SZDIRE); >>>> /* Create ".." entry */ >>>> + fs->win[SZDIRE + 1] = '.'; pcl = >>>> dj.obj.sclust; >>>> + st_clust(fs, fs->win + SZDIRE, pcl); >>>> + fs->wflag = 1; >>>> + } >>>> + res = dir_register(&dj); /* Register the >>>> object to the parent directoy */ >>>> + } >>>> + } >>>> + if (res == FR_OK) { >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { /* Initialize >>>> directory entry block */ >>>> + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* >>>> Created time */ >>>> + st_dword(fs->dirbuf + XDIR_FstClus, dcl); /* >>>> Table start cluster */ >>>> + st_dword(fs->dirbuf + XDIR_FileSize, >>>> (DWORD)fs->csize * SS(fs)); /* Directory size needs to be valid */ >>>> + st_dword(fs->dirbuf + XDIR_ValidFileSize, >>>> (DWORD)fs->csize * SS(fs)); >>>> + fs->dirbuf[XDIR_GenFlags] = 3; >>>> /* Initialize the object flag */ >>>> + fs->dirbuf[XDIR_Attr] = AM_DIR; /* Attribute */ >>>> + res = store_xdir(&dj); >>>> + } else >>>> +#endif >>>> + { >>>> + st_dword(dj.dir + DIR_ModTime, tm); /* >>>> Created time */ >>>> + st_clust(fs, dj.dir, dcl); /* Table >>>> start cluster */ >>>> + dj.dir[DIR_Attr] = AM_DIR; /* >>>> Attribute */ >>>> + fs->wflag = 1; >>>> + } >>>> + if (res == FR_OK) { >>>> + res = sync_fs(fs); >>>> + } >>>> + } else { >>>> + remove_chain(&sobj, dcl, 0); /* Could not >>>> register, remove the allocated cluster */ >>>> + } >>>> + } >>>> + FREE_NAMBUF(); >>>> + } >>>> + >>>> + LEAVE_FF(fs, res); >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Rename a >>>> File/Directory */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_rename ( >>>> + const TCHAR* path_old, /* Pointer to the object name to be >>>> renamed */ >>>> + const TCHAR* path_new /* Pointer to the new name */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + DIR djo, djn; >>>> + FATFS *fs; >>>> + BYTE buf[FF_FS_EXFAT ? SZDIRE * 2 : SZDIRE], *dir; >>>> + LBA_t sect; >>>> + DEF_NAMBUF >>>> + >>>> + >>>> + get_ldnumber(&path_new); /* Snip the >>>> drive number of new name off */ >>>> + res = mount_volume(&path_old, &fs, FA_WRITE); /* Get logical >>>> drive of the old object */ >>>> + if (res == FR_OK) { >>>> + djo.obj.fs = fs; >>>> + INIT_NAMBUF(fs); >>>> + res = follow_path(&djo, path_old); /* Check old >>>> object */ >>>> + if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | >>>> NS_NONAME))) res = FR_INVALID_NAME; /* Check validity of name */ >>>> +#if FF_FS_LOCK != 0 >>>> + if (res == FR_OK) { >>>> + res = chk_lock(&djo, 2); >>>> + } >>>> +#endif >>>> + if (res == FR_OK) { /* Object to be >>>> renamed is found */ >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { /* At exFAT volume */ >>>> + BYTE nf, nn; >>>> + WORD nh; >>>> + >>>> + mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save >>>> 85+C0 entry of old object */ >>>> + mem_cpy(&djn, &djo, sizeof djo); >>>> + res = follow_path(&djn, path_new); /* Make >>>> sure if new object name is not in use */ >>>> + if (res == FR_OK) { /* Is >>>> new name already in use by any other object? */ >>>> + res = (djn.obj.sclust == djo.obj.sclust && >>>> djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; >>>> + } >>>> + if (res == FR_NO_FILE) { /* It is a >>>> valid path and no name collision */ >>>> + res = dir_register(&djn); /* >>>> Register the new entry */ >>>> + if (res == FR_OK) { >>>> + nf = fs->dirbuf[XDIR_NumSec]; nn = >>>> fs->dirbuf[XDIR_NumName]; >>>> + nh = ld_word(fs->dirbuf + XDIR_NameHash); >>>> + mem_cpy(fs->dirbuf, buf, SZDIRE * 2); /* >>>> Restore 85+C0 entry */ >>>> + fs->dirbuf[XDIR_NumSec] = nf; >>>> fs->dirbuf[XDIR_NumName] = nn; >>>> + st_word(fs->dirbuf + XDIR_NameHash, nh); >>>> + if (!(fs->dirbuf[XDIR_Attr] & AM_DIR)) >>>> fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute if it >>>> is a file */ >>>> +/* Start of critical section where an interruption can cause a >>>> cross-link */ >>>> + res = store_xdir(&djn); >>>> + } >>>> + } >>>> + } else >>>> +#endif >>>> + { /* At FAT/FAT32 volume */ >>>> + mem_cpy(buf, djo.dir, SZDIRE); /* Save >>>> directory entry of the object */ >>>> + mem_cpy(&djn, &djo, sizeof (DIR)); /* >>>> Duplicate the directory object */ >>>> + res = follow_path(&djn, path_new); /* Make >>>> sure if new object name is not in use */ >>>> + if (res == FR_OK) { /* Is >>>> new name already in use by any other object? */ >>>> + res = (djn.obj.sclust == djo.obj.sclust && >>>> djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; >>>> + } >>>> + if (res == FR_NO_FILE) { /* It is a >>>> valid path and no name collision */ >>>> + res = dir_register(&djn); /* >>>> Register the new entry */ >>>> + if (res == FR_OK) { >>>> + dir = djn.dir; /* Copy >>>> directory entry of the object except name */ >>>> + mem_cpy(dir + 13, buf + 13, SZDIRE - 13); >>>> + dir[DIR_Attr] = buf[DIR_Attr]; >>>> + if (!(dir[DIR_Attr] & AM_DIR)) >>>> dir[DIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */ >>>> + fs->wflag = 1; >>>> + if ((dir[DIR_Attr] & AM_DIR) && >>>> djo.obj.sclust != djn.obj.sclust) { /* Update .. entry in the >>>> sub-directory if needed */ >>>> + sect = clst2sect(fs, ld_clust(fs, dir)); >>>> + if (sect == 0) { >>>> + res = FR_INT_ERR; >>>> + } else { >>>> +/* Start of critical section where an interruption can cause a >>>> cross-link */ >>>> + res = move_window(fs, sect); >>>> + dir = fs->win + SZDIRE * 1; /* >>>> Ptr to .. entry */ >>>> + if (res == FR_OK && dir[1] == '.') { >>>> + st_clust(fs, dir, djn.obj.sclust); >>>> + fs->wflag = 1; >>>> + } >>>> + } >>>> + } >>>> + } >>>> + } >>>> + } >>>> + if (res == FR_OK) { >>>> + res = dir_remove(&djo); /* Remove old entry */ >>>> + if (res == FR_OK) { >>>> + res = sync_fs(fs); >>>> + } >>>> + } >>>> +/* End of the critical section */ >>>> + } >>>> + FREE_NAMBUF(); >>>> + } >>>> + >>>> + LEAVE_FF(fs, res); >>>> +} >>>> + >>>> +#endif /* !FF_FS_READONLY */ >>>> +#endif /* FF_FS_MINIMIZE == 0 */ >>>> +#endif /* FF_FS_MINIMIZE <= 1 */ >>>> +#endif /* FF_FS_MINIMIZE <= 2 */ >>>> + >>>> + >>>> + >>>> +#if FF_USE_CHMOD && !FF_FS_READONLY >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Change >>>> Attribute */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_chmod ( >>>> + const TCHAR* path, /* Pointer to the file path */ >>>> + BYTE attr, /* Attribute bits */ >>>> + BYTE mask /* Attribute mask to change */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + DIR dj; >>>> + FATFS *fs; >>>> + DEF_NAMBUF >>>> + >>>> + >>>> + res = mount_volume(&path, &fs, FA_WRITE); /* Get logical >>>> drive */ >>>> + if (res == FR_OK) { >>>> + dj.obj.fs = fs; >>>> + INIT_NAMBUF(fs); >>>> + res = follow_path(&dj, path); /* Follow the file path */ >>>> + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) >>>> res = FR_INVALID_NAME; /* Check object validity */ >>>> + if (res == FR_OK) { >>>> + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid >>>> attribute mask */ >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { >>>> + fs->dirbuf[XDIR_Attr] = (attr & mask) | >>>> (fs->dirbuf[XDIR_Attr] & (BYTE)~mask); /* Apply attribute change */ >>>> + res = store_xdir(&dj); >>>> + } else >>>> +#endif >>>> + { >>>> + dj.dir[DIR_Attr] = (attr & mask) | >>>> (dj.dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ >>>> + fs->wflag = 1; >>>> + } >>>> + if (res == FR_OK) { >>>> + res = sync_fs(fs); >>>> + } >>>> + } >>>> + FREE_NAMBUF(); >>>> + } >>>> + >>>> + LEAVE_FF(fs, res); >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Change >>>> Timestamp */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_utime ( >>>> + const TCHAR* path, /* Pointer to the file/directory name */ >>>> + const FILINFO* fno /* Pointer to the timestamp to be set */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + DIR dj; >>>> + FATFS *fs; >>>> + DEF_NAMBUF >>>> + >>>> + >>>> + res = mount_volume(&path, &fs, FA_WRITE); /* Get logical >>>> drive */ >>>> + if (res == FR_OK) { >>>> + dj.obj.fs = fs; >>>> + INIT_NAMBUF(fs); >>>> + res = follow_path(&dj, path); /* Follow the file path */ >>>> + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) >>>> res = FR_INVALID_NAME; /* Check object validity */ >>>> + if (res == FR_OK) { >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { >>>> + st_dword(fs->dirbuf + XDIR_ModTime, >>>> (DWORD)fno->fdate << 16 | fno->ftime); >>>> + res = store_xdir(&dj); >>>> + } else >>>> +#endif >>>> + { >>>> + st_dword(dj.dir + DIR_ModTime, (DWORD)fno->fdate << >>>> 16 | fno->ftime); >>>> + fs->wflag = 1; >>>> + } >>>> + if (res == FR_OK) { >>>> + res = sync_fs(fs); >>>> + } >>>> + } >>>> + FREE_NAMBUF(); >>>> + } >>>> + >>>> + LEAVE_FF(fs, res); >>>> +} >>>> + >>>> +#endif /* FF_USE_CHMOD && !FF_FS_READONLY */ >>>> + >>>> + >>>> + >>>> +#if FF_USE_LABEL >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Get Volume >>>> Label */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_getlabel ( >>>> + const TCHAR* path, /* Logical drive number */ >>>> + TCHAR* label, /* Buffer to store the volume label */ >>>> + DWORD* vsn /* Variable to store the volume serial >>>> number */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + DIR dj; >>>> + FATFS *fs; >>>> + UINT si, di; >>>> + WCHAR wc; >>>> + >>>> + /* Get logical drive */ >>>> + res = mount_volume(&path, &fs, 0); >>>> + >>>> + /* Get volume label */ >>>> + if (res == FR_OK && label) { >>>> + dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root >>>> directory */ >>>> + res = dir_sdi(&dj, 0); >>>> + if (res == FR_OK) { >>>> + res = DIR_READ_LABEL(&dj); /* Find a volume >>>> label entry */ >>>> + if (res == FR_OK) { >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { >>>> + WCHAR hs; >>>> + >>>> + for (si = di = hs = 0; si < >>>> dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 >>>> entry */ >>>> + wc = ld_word(dj.dir + XDIR_Label + si * 2); >>>> + if (hs == 0 && IsSurrogate(wc)) { /* Is >>>> the code a surrogate? */ >>>> + hs = wc; continue; >>>> + } >>>> + wc = put_utf((DWORD)hs << 16 | wc, >>>> &label[di], 4); >>>> + if (wc == 0) { di = 0; break; } >>>> + di += wc; >>>> + hs = 0; >>>> + } >>>> + if (hs != 0) di = 0; /* Broken surrogate >>>> pair? */ >>>> + label[di] = 0; >>>> + } else >>>> +#endif >>>> + { >>>> + si = di = 0; /* Extract volume label >>>> from AM_VOL entry */ >>>> + while (si < 11) { >>>> + wc = dj.dir[si++]; >>>> +#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode output */ >>>> + if (dbc_1st((BYTE)wc) && si < 11) wc = wc >>>> << 8 | dj.dir[si++]; /* Is it a DBC? */ >>>> + wc = ff_oem2uni(wc, >>>> CODEPAGE); /* Convert it into Unicode */ >>>> + if (wc != 0) wc = put_utf(wc, &label[di], >>>> 4); /* Put it in Unicode */ >>>> + if (wc == 0) { di = 0; break; } >>>> + di += wc; >>>> +#else /* ANSI/OEM output */ >>>> + label[di++] = (TCHAR)wc; >>>> +#endif >>>> + } >>>> + do { /* Truncate trailing spaces */ >>>> + label[di] = 0; >>>> + if (di == 0) break; >>>> + } while (label[--di] == ' '); >>>> + } >>>> + } >>>> + } >>>> + if (res == FR_NO_FILE) { /* No label entry and return >>>> nul string */ >>>> + label[0] = 0; >>>> + res = FR_OK; >>>> + } >>>> + } >>>> + >>>> + /* Get volume serial number */ >>>> + if (res == FR_OK && vsn) { >>>> + res = move_window(fs, fs->volbase); >>>> + if (res == FR_OK) { >>>> + switch (fs->fs_type) { >>>> + case FS_EXFAT: >>>> + di = BPB_VolIDEx; >>>> + break; >>>> + >>>> + case FS_FAT32: >>>> + di = BS_VolID32; >>>> + break; >>>> + >>>> + default: >>>> + di = BS_VolID; >>>> + } >>>> + *vsn = ld_dword(fs->win + di); >>>> + } >>>> + } >>>> + >>>> + LEAVE_FF(fs, res); >>>> +} >>>> + >>>> + >>>> + >>>> +#if !FF_FS_READONLY >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Set Volume >>>> Label */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_setlabel ( >>>> + const TCHAR* label /* Volume label to set with heading >>>> logical drive number */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + DIR dj; >>>> + FATFS *fs; >>>> + BYTE dirvn[22]; >>>> + UINT di; >>>> + WCHAR wc; >>>> + static const char badchr[] = "+.,;=[]/\\\"*:<>\?|\x7F"; /* >>>> [0..] for FAT, [7..] for exFAT */ >>>> +#if FF_USE_LFN >>>> + DWORD dc; >>>> +#endif >>>> + >>>> + /* Get logical drive */ >>>> + res = mount_volume(&label, &fs, FA_WRITE); >>>> + if (res != FR_OK) LEAVE_FF(fs, res); >>>> + >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ >>>> + mem_set(dirvn, 0, 22); >>>> + di = 0; >>>> + while ((UINT)*label >= ' ') { /* Create volume label */ >>>> + dc = tchar2uni(&label); /* Get a Unicode character */ >>>> + if (dc >= 0x10000) { >>>> + if (dc == 0xFFFFFFFF || di >= 10) { /* Wrong >>>> surrogate or buffer overflow */ >>>> + dc = 0; >>>> + } else { >>>> + st_word(dirvn + di * 2, (WCHAR)(dc >> 16)); di++; >>>> + } >>>> + } >>>> + if (dc == 0 || chk_chr(badchr + 7, (int)dc) || di >= >>>> 11) { /* Check validity of the volume label */ >>>> + LEAVE_FF(fs, FR_INVALID_NAME); >>>> + } >>>> + st_word(dirvn + di * 2, (WCHAR)dc); di++; >>>> + } >>>> + } else >>>> +#endif >>>> + { /* On the FAT/FAT32 volume */ >>>> + mem_set(dirvn, ' ', 11); >>>> + di = 0; >>>> + while ((UINT)*label >= ' ') { /* Create volume label */ >>>> +#if FF_USE_LFN >>>> + dc = tchar2uni(&label); >>>> + wc = (dc < 0x10000) ? ff_uni2oem(ff_wtoupper(dc), >>>> CODEPAGE) : 0; >>>> +#else /* ANSI/OEM input */ >>>> + wc = (BYTE)*label++; >>>> + if (dbc_1st((BYTE)wc)) wc = dbc_2nd((BYTE)*label) ? wc >>>> << 8 | (BYTE)*label++ : 0; >>>> + if (IsLower(wc)) wc -= 0x20; /* To upper ASCII >>>> characters */ >>>> +#if FF_CODE_PAGE == 0 >>>> + if (ExCvt && wc >= 0x80) wc = ExCvt[wc - 0x80]; /* >>>> To upper extended characters (SBCS cfg) */ >>>> +#elif FF_CODE_PAGE < 900 >>>> + if (wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper >>>> extended characters (SBCS cfg) */ >>>> +#endif >>>> +#endif >>>> + if (wc == 0 || chk_chr(badchr + 0, (int)wc) || di >= >>>> (UINT)((wc >= 0x100) ? 10 : 11)) { /* Reject invalid characters >>>> for volume label */ >>>> + LEAVE_FF(fs, FR_INVALID_NAME); >>>> + } >>>> + if (wc >= 0x100) dirvn[di++] = (BYTE)(wc >> 8); >>>> + dirvn[di++] = (BYTE)wc; >>>> + } >>>> + if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* >>>> Reject illegal name (heading DDEM) */ >>>> + while (di && dirvn[di - 1] == ' ') di--; /* >>>> Snip trailing spaces */ >>>> + } >>>> + >>>> + /* Set volume label */ >>>> + dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */ >>>> + res = dir_sdi(&dj, 0); >>>> + if (res == FR_OK) { >>>> + res = DIR_READ_LABEL(&dj); /* Get volume label entry */ >>>> + if (res == FR_OK) { >>>> + if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { >>>> + dj.dir[XDIR_NumLabel] = (BYTE)di; /* Change the >>>> volume label */ >>>> + mem_cpy(dj.dir + XDIR_Label, dirvn, 22); >>>> + } else { >>>> + if (di != 0) { >>>> + mem_cpy(dj.dir, dirvn, 11); /* Change the >>>> volume label */ >>>> + } else { >>>> + dj.dir[DIR_Name] = DDEM; /* Remove the >>>> volume label */ >>>> + } >>>> + } >>>> + fs->wflag = 1; >>>> + res = sync_fs(fs); >>>> + } else { /* No volume label entry or an error */ >>>> + if (res == FR_NO_FILE) { >>>> + res = FR_OK; >>>> + if (di != 0) { /* Create a volume label entry */ >>>> + res = dir_alloc(&dj, 1); /* Allocate an >>>> entry */ >>>> + if (res == FR_OK) { >>>> + mem_set(dj.dir, 0, SZDIRE); /* Clean the >>>> entry */ >>>> + if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { >>>> + dj.dir[XDIR_Type] = ET_VLABEL; /* >>>> Create volume label entry */ >>>> + dj.dir[XDIR_NumLabel] = (BYTE)di; >>>> + mem_cpy(dj.dir + XDIR_Label, dirvn, 22); >>>> + } else { >>>> + dj.dir[DIR_Attr] = AM_VOL; /* >>>> Create volume label entry */ >>>> + mem_cpy(dj.dir, dirvn, 11); >>>> + } >>>> + fs->wflag = 1; >>>> + res = sync_fs(fs); >>>> + } >>>> + } >>>> + } >>>> + } >>>> + } >>>> + >>>> + LEAVE_FF(fs, res); >>>> +} >>>> + >>>> +#endif /* !FF_FS_READONLY */ >>>> +#endif /* FF_USE_LABEL */ >>>> + >>>> + >>>> + >>>> +#if FF_USE_EXPAND && !FF_FS_READONLY >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Allocate a Contiguous Blocks to the >>>> File */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_expand ( >>>> + FIL* fp, /* Pointer to the file object */ >>>> + FSIZE_t fsz, /* File size to be expanded to */ >>>> + BYTE opt /* Operation mode 0:Find and prepare or 1:Find >>>> and allocate */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + FATFS *fs; >>>> + DWORD n, clst, stcl, scl, ncl, tcl, lclst; >>>> + >>>> + >>>> + res = validate(&fp->obj, &fs); /* Check validity of the >>>> file object */ >>>> + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) >>>> LEAVE_FF(fs, res); >>>> + if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) >>>> LEAVE_FF(fs, FR_DENIED); >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, >>>> FR_DENIED); /* Check if in size limit */ >>>> +#endif >>>> + n = (DWORD)fs->csize * SS(fs); /* Cluster size */ >>>> + tcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0); /* >>>> Number of clusters required */ >>>> + stcl = fs->last_clst; lclst = 0; >>>> + if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2; >>>> + >>>> +#if FF_FS_EXFAT >>>> + if (fs->fs_type == FS_EXFAT) { >>>> + scl = find_bitmap(fs, stcl, tcl); /* Find a >>>> contiguous cluster block */ >>>> + if (scl == 0) res = FR_DENIED; /* No >>>> contiguous cluster block was found */ >>>> + if (scl == 0xFFFFFFFF) res = FR_DISK_ERR; >>>> + if (res == FR_OK) { /* A contiguous free area is found */ >>>> + if (opt) { /* Allocate it now */ >>>> + res = change_bitmap(fs, scl, tcl, 1); /* Mark >>>> the cluster block 'in use' */ >>>> + lclst = scl + tcl - 1; >>>> + } else { /* Set it as suggested point for next >>>> allocation */ >>>> + lclst = scl - 1; >>>> + } >>>> + } >>>> + } else >>>> +#endif >>>> + { >>>> + scl = clst = stcl; ncl = 0; >>>> + for (;;) { /* Find a contiguous cluster block */ >>>> + n = get_fat(&fp->obj, clst); >>>> + if (++clst >= fs->n_fatent) clst = 2; >>>> + if (n == 1) { res = FR_INT_ERR; break; } >>>> + if (n == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } >>>> + if (n == 0) { /* Is it a free cluster? */ >>>> + if (++ncl == tcl) break; /* Break if a >>>> contiguous cluster block is found */ >>>> + } else { >>>> + scl = clst; ncl = 0; /* Not a free cluster */ >>>> + } >>>> + if (clst == stcl) { res = FR_DENIED; break; } /* No >>>> contiguous cluster? */ >>>> + } >>>> + if (res == FR_OK) { /* A contiguous free area is found */ >>>> + if (opt) { /* Allocate it now */ >>>> + for (clst = scl, n = tcl; n; clst++, n--) { /* >>>> Create a cluster chain on the FAT */ >>>> + res = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : >>>> clst + 1); >>>> + if (res != FR_OK) break; >>>> + lclst = clst; >>>> + } >>>> + } else { /* Set it as suggested point for next >>>> allocation */ >>>> + lclst = scl - 1; >>>> + } >>>> + } >>>> + } >>>> + >>>> + if (res == FR_OK) { >>>> + fs->last_clst = lclst; /* Set suggested start >>>> cluster to start next */ >>>> + if (opt) { /* Is it allocated now? */ >>>> + fp->obj.sclust = scl; /* Update object >>>> allocation information */ >>>> + fp->obj.objsize = fsz; >>>> + if (FF_FS_EXFAT) fp->obj.stat = 2; /* Set status >>>> 'contiguous chain' */ >>>> + fp->flag |= FA_MODIFIED; >>>> + if (fs->free_clst <= fs->n_fatent - 2) { /* Update >>>> FSINFO */ >>>> + fs->free_clst -= tcl; >>>> + fs->fsi_flag |= 1; >>>> + } >>>> + } >>>> + } >>>> + >>>> + LEAVE_FF(fs, res); >>>> +} >>>> + >>>> +#endif /* FF_USE_EXPAND && !FF_FS_READONLY */ >>>> + >>>> + >>>> + >>>> +#if FF_USE_FORWARD >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Forward Data to the Stream >>>> Directly */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_forward ( >>>> + FIL* fp, /* Pointer to the file object */ >>>> + UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming >>>> function */ >>>> + UINT btf, /* Number of bytes to forward */ >>>> + UINT* bf /* Pointer to number of bytes >>>> forwarded */ >>>> +) >>>> +{ >>>> + FRESULT res; >>>> + FATFS *fs; >>>> + DWORD clst; >>>> + LBA_t sect; >>>> + FSIZE_t remain; >>>> + UINT rcnt, csect; >>>> + BYTE *dbuf; >>>> + >>>> + >>>> + *bf = 0; /* Clear transfer byte counter */ >>>> + res = validate(&fp->obj, &fs); /* Check validity of the >>>> file object */ >>>> + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) >>>> LEAVE_FF(fs, res); >>>> + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check >>>> access mode */ >>>> + >>>> + remain = fp->obj.objsize - fp->fptr; >>>> + if (btf > remain) btf = (UINT)remain; /* Truncate >>>> btf by remaining bytes */ >>>> + >>>> + for ( ; btf && (*func)(0, 0); /* Repeat >>>> until all data transferred or stream goes busy */ >>>> + fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { >>>> + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* >>>> Sector offset in the cluster */ >>>> + if (fp->fptr % SS(fs) == 0) { /* On the >>>> sector boundary? */ >>>> + if (csect == 0) { /* On the >>>> cluster boundary? */ >>>> + clst = (fp->fptr == 0) ? /* On the top >>>> of the file? */ >>>> + fp->obj.sclust : get_fat(&fp->obj, fp->clust); >>>> + if (clst <= 1) ABORT(fs, FR_INT_ERR); >>>> + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); >>>> + fp->clust = clst; /* Update >>>> current cluster */ >>>> + } >>>> + } >>>> + sect = clst2sect(fs, fp->clust); /* Get current >>>> data sector */ >>>> + if (sect == 0) ABORT(fs, FR_INT_ERR); >>>> + sect += csect; >>>> +#if FF_FS_TINY >>>> + if (move_window(fs, sect) != FR_OK) ABORT(fs, >>>> FR_DISK_ERR); /* Move sector window to the file data */ >>>> + dbuf = fs->win; >>>> +#else >>>> + if (fp->sect != sect) { /* Fill sector cache with >>>> file data */ >>>> +#if !FF_FS_READONLY >>>> + if (fp->flag & FA_DIRTY) { /* Write-back dirty >>>> sector cache */ >>>> + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != >>>> RES_OK) ABORT(fs, FR_DISK_ERR); >>>> + fp->flag &= (BYTE)~FA_DIRTY; >>>> + } >>>> +#endif >>>> + if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) >>>> ABORT(fs, FR_DISK_ERR); >>>> + } >>>> + dbuf = fp->buf; >>>> +#endif >>>> + fp->sect = sect; >>>> + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of >>>> bytes remains in the sector */ >>>> + if (rcnt > btf) rcnt = btf; /* Clip it >>>> by btr if needed */ >>>> + rcnt = (*func)(dbuf + ((UINT)fp->fptr % SS(fs)), rcnt); /* >>>> Forward the file data */ >>>> + if (rcnt == 0) ABORT(fs, FR_INT_ERR); >>>> + } >>>> + >>>> + LEAVE_FF(fs, FR_OK); >>>> +} >>>> +#endif /* FF_USE_FORWARD */ >>>> + >>>> + >>>> + >>>> +#if !FF_FS_READONLY && FF_USE_MKFS >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Create FAT/exFAT >>>> volume */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +#define N_SEC_TRACK 63 /* Sectors per track for >>>> determination of drive CHS */ >>>> +#define GPT_ALIGN 0x100000 /* Alignment of partitions in >>>> GPT [byte] (>=128KB) */ >>>> +#define GPT_ITEMS 128 /* Number of GPT table size >>>> (>=128, sector aligned) */ >>>> + >>>> + >>>> +/* Create partitions on the physical drive in format of MBR or GPT */ >>>> + >>>> +static FRESULT create_partition ( >>>> + BYTE drv, /* Physical drive number */ >>>> + const LBA_t plst[], /* Partition list */ >>>> + BYTE sys, /* System ID (for only MBR, temp setting) */ >>>> + BYTE* buf /* Working buffer for a sector */ >>>> +) >>>> +{ >>>> + UINT i, cy; >>>> + LBA_t sz_drv; >>>> + DWORD sz_drv32, s_lba32, n_lba32; >>>> + BYTE *pte, hd, n_hd, sc, n_sc; >>>> + >>>> + /* Get drive size */ >>>> + if (disk_ioctl(drv, GET_SECTOR_COUNT, &sz_drv) != RES_OK) >>>> return FR_DISK_ERR; >>>> + >>>> +#if FF_LBA64 >>>> + if (sz_drv >= FF_MIN_GPT) { /* Create partitions in GPT */ >>>> + WORD ss; >>>> + UINT sz_pt, pi, si, ofs; >>>> + DWORD bcc, rnd, align; >>>> + QWORD s_lba64, n_lba64, sz_pool, s_bpt; >>>> + static const BYTE gpt_mbr[16] = {0x00, 0x00, 0x02, 0x00, >>>> 0xEE, 0xFE, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, >>>> 0xFF}; >>>> + >>>> +#if FF_MAX_SS != FF_MIN_SS >>>> + if (disk_ioctl(drv, GET_SECTOR_SIZE, &ss) != RES_OK) return >>>> FR_DISK_ERR; /* Get sector size */ >>>> + if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) >>>> return FR_DISK_ERR; >>>> +#else >>>> + ss = FF_MAX_SS; >>>> +#endif >>>> + rnd = GET_FATTIME(); /* Random seed */ >>>> + align = GPT_ALIGN / ss; /* Partition alignment >>>> [sector] */ >>>> + sz_pt = GPT_ITEMS * SZ_GPTE / ss; /* Size of PT [sector] */ >>>> + s_bpt = sz_drv - sz_pt - 1; /* Backup PT start >>>> sector */ >>>> + s_lba64 = 2 + sz_pt; /* First allocatable sector */ >>>> + sz_pool = s_bpt - s_lba64; /* Size of allocatable >>>> area */ >>>> + bcc = 0xFFFFFFFF; n_lba64 = 1; >>>> + pi = si = 0; /* partition table index, size table index */ >>>> + do { >>>> + if (pi * SZ_GPTE % ss == 0) mem_set(buf, 0, ss); /* >>>> Clean the buffer if needed */ >>>> + if (n_lba64 != 0) { /* Is the size table not >>>> termintated? */ >>>> + s_lba64 = (s_lba64 + align - 1) & ((QWORD)0 - >>>> align); /* Align partition start */ >>>> + n_lba64 = plst[si++]; /* Get a partition size */ >>>> + if (n_lba64 <= 100) { /* Is the size in >>>> percentage? */ >>>> + n_lba64 = sz_pool * n_lba64 / 100; >>>> + n_lba64 = (n_lba64 + align - 1) & ((QWORD)0 - >>>> align); /* Align partition end (only if in percentage) */ >>>> + } >>>> + if (s_lba64 + n_lba64 > s_bpt) { /* Clip at end >>>> of the pool */ >>>> + n_lba64 = (s_lba64 < s_bpt) ? s_bpt - s_lba64 : 0; >>>> + } >>>> + } >>>> + if (n_lba64 != 0) { /* Add a partition? */ >>>> + ofs = pi * SZ_GPTE % ss; >>>> + mem_cpy(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, >>>> 16); /* Partition GUID (Microsoft Basic Data) */ >>>> + rnd = make_rand(rnd, buf + ofs + GPTE_UpGuid, >>>> 16); /* Unique partition GUID */ >>>> + st_qword(buf + ofs + GPTE_FstLba, >>>> s_lba64); /* Partition start LBA */ >>>> + st_qword(buf + ofs + GPTE_LstLba, s_lba64 + n_lba64 >>>> - 1); /* Partition end LBA */ >>>> + s_lba64 += n_lba64; /* Next partition LBA */ >>>> + } >>>> + if ((pi + 1) * SZ_GPTE % ss == 0) { /* Write the >>>> buffer if it is filled up */ >>>> + for (i = 0; i < ss; bcc = crc32(bcc, buf[i++])) ; >>>> /* Calculate table check sum */ >>>> + if (disk_write(drv, buf, 2 + pi * SZ_GPTE / ss, 1) >>>> != RES_OK) return FR_DISK_ERR; /* Primary table */ >>>> + if (disk_write(drv, buf, s_bpt + pi * SZ_GPTE / ss, >>>> 1) != RES_OK) return FR_DISK_ERR; /* Secondary table */ >>>> + } >>>> + } while (++pi < GPT_ITEMS); >>>> + >>>> + /* Create primary GPT header */ >>>> + mem_set(buf, 0, ss); >>>> + mem_cpy(buf + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", >>>> 16); /* Signature, version (1.0) and size (92) */ >>>> + st_dword(buf + GPTH_PtBcc, ~bcc); /* Table >>>> check sum */ >>>> + st_qword(buf + GPTH_CurLba, 1); /* LBA >>>> of this header */ >>>> + st_qword(buf + GPTH_BakLba, sz_drv - 1); /* LBA of >>>> another header */ >>>> + st_qword(buf + GPTH_FstLba, 2 + sz_pt); /* LBA >>>> of first allocatable sector */ >>>> + st_qword(buf + GPTH_LstLba, s_bpt - 1); /* LBA >>>> of last allocatable sector */ >>>> + st_dword(buf + GPTH_PteSize, SZ_GPTE); /* Size >>>> of a table entry */ >>>> + st_dword(buf + GPTH_PtNum, GPT_ITEMS); /* Number >>>> of table entries */ >>>> + st_dword(buf + GPTH_PtOfs, 2); /* LBA of >>>> this table */ >>>> + rnd = make_rand(rnd, buf + GPTH_DskGuid, 16); /* Disk >>>> GUID */ >>>> + for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, >>>> buf[i++])) ; /* Calculate header check sum */ >>>> + st_dword(buf + GPTH_Bcc, ~bcc); /* >>>> Header check sum */ >>>> + if (disk_write(drv, buf, 1, 1) != RES_OK) return FR_DISK_ERR; >>>> + >>>> + /* Create secondary GPT header */ >>>> + st_qword(buf + GPTH_CurLba, sz_drv - 1); /* LBA of >>>> this header */ >>>> + st_qword(buf + GPTH_BakLba, 1); /* LBA >>>> of another header */ >>>> + st_qword(buf + GPTH_PtOfs, s_bpt); /* LBA of >>>> this table */ >>>> + st_dword(buf + GPTH_Bcc, 0); >>>> + for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, >>>> buf[i++])) ; /* Calculate header check sum */ >>>> + st_dword(buf + GPTH_Bcc, ~bcc); /* >>>> Header check sum */ >>>> + if (disk_write(drv, buf, sz_drv - 1, 1) != RES_OK) return >>>> FR_DISK_ERR; >>>> + >>>> + /* Create protective MBR */ >>>> + mem_set(buf, 0, ss); >>>> + mem_cpy(buf + MBR_Table, gpt_mbr, 16); /* Create >>>> a GPT partition */ >>>> + st_word(buf + BS_55AA, 0xAA55); >>>> + if (disk_write(drv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; >>>> + >>>> + } else >>>> +#endif >>>> + { /* Create partitions in MBR */ >>>> + sz_drv32 = (DWORD)sz_drv; >>>> + n_sc = N_SEC_TRACK; /* Determine drive CHS without >>>> any consideration of the drive geometry */ >>>> + for (n_hd = 8; n_hd != 0 && sz_drv32 / n_hd / n_sc > 1024; >>>> n_hd *= 2) ; >>>> + if (n_hd == 0) n_hd = 255; /* Number of heads needs to >>>> be <256 */ >>>> + >>>> + mem_set(buf, 0, FF_MAX_SS); /* Clear MBR */ >>>> + pte = buf + MBR_Table; /* Partition table in the MBR */ >>>> + for (i = 0, s_lba32 = n_sc; i < 4 && s_lba32 != 0 && >>>> s_lba32 < sz_drv32; i++, s_lba32 += n_lba32) { >>>> + n_lba32 = (DWORD)plst[i]; /* Get partition size */ >>>> + if (n_lba32 <= 100) n_lba32 = (n_lba32 == 100) ? >>>> sz_drv32 : sz_drv32 / 100 * n_lba32; /* Size in percentage? */ >>>> + if (s_lba32 + n_lba32 > sz_drv32 || s_lba32 + n_lba32 < >>>> s_lba32) n_lba32 = sz_drv32 - s_lba32; /* Clip at drive size */ >>>> + if (n_lba32 == 0) break; /* End of table or no >>>> sector to allocate? */ >>>> + >>>> + st_dword(pte + PTE_StLba, s_lba32); /* Start LBA */ >>>> + st_dword(pte + PTE_SizLba, n_lba32); /* Number of >>>> sectors */ >>>> + pte[PTE_System] = sys; /* System >>>> type */ >>>> + >>>> + cy = (UINT)(s_lba32 / n_sc / n_hd); /* Start >>>> cylinder */ >>>> + hd = (BYTE)(s_lba32 / n_sc % n_hd); /* Start >>>> head */ >>>> + sc = (BYTE)(s_lba32 % n_sc + 1); /* Start sector */ >>>> + pte[PTE_StHead] = hd; >>>> + pte[PTE_StSec] = (BYTE)((cy >> 2 & 0xC0) | sc); >>>> + pte[PTE_StCyl] = (BYTE)cy; >>>> + >>>> + cy = (UINT)((s_lba32 + n_lba32 - 1) / n_sc / n_hd); /* >>>> End cylinder */ >>>> + hd = (BYTE)((s_lba32 + n_lba32 - 1) / n_sc % n_hd); /* >>>> End head */ >>>> + sc = (BYTE)((s_lba32 + n_lba32 - 1) % n_sc + 1); /* >>>> End sector */ >>>> + pte[PTE_EdHead] = hd; >>>> + pte[PTE_EdSec] = (BYTE)((cy >> 2 & 0xC0) | sc); >>>> + pte[PTE_EdCyl] = (BYTE)cy; >>>> + >>>> + pte += SZ_PTE; /* Next entry */ >>>> + } >>>> + >>>> + st_word(buf + BS_55AA, 0xAA55); /* MBR signature */ >>>> + if (disk_write(drv, buf, 0, 1) != RES_OK) return >>>> FR_DISK_ERR; /* Write it to the MBR */ >>>> + } >>>> + >>>> + return FR_OK; >>>> +} >>>> + >>>> + >>>> + >>>> +FRESULT f_mkfs ( >>>> + const TCHAR* path, /* Logical drive number */ >>>> + const MKFS_PARM* opt, /* Format options */ >>>> + void* work, /* Pointer to working buffer (null: >>>> use heap memory) */ >>>> + UINT len /* Size of working buffer [byte] */ >>>> +) >>>> +{ >>>> + static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* >>>> Cluster size boundary for FAT volume (4Ks unit) */ >>>> + static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* >>>> Cluster size boundary for FAT32 volume (128Ks unit) */ >>>> + static const MKFS_PARM defopt = {FM_ANY, 0, 0, 0, 0}; /* >>>> Default parameter */ >>>> + BYTE fsopt, fsty, sys, *buf, *pte, pdrv, ipart; >>>> + WORD ss; /* Sector size */ >>>> + DWORD sz_buf, sz_blk, n_clst, pau, nsect, n; >>>> + LBA_t sz_vol, b_vol, b_fat, b_data; /* Size of volume, >>>> Base LBA of volume, fat, data */ >>>> + LBA_t sect, lba[2]; >>>> + DWORD sz_rsv, sz_fat, sz_dir, sz_au; /* Size of reserved, >>>> fat, dir, data, cluster */ >>>> + UINT n_fat, n_root, i; /* Index, Number of >>>> FATs and Number of roor dir entries */ >>>> + int vol; >>>> + DSTATUS ds; >>>> + FRESULT fr; >>>> + >>>> + >>>> + /* Check mounted drive and clear work area */ >>>> + vol = get_ldnumber(&path); /* Get target >>>> logical drive */ >>>> + if (vol < 0) return FR_INVALID_DRIVE; >>>> + if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear the fs >>>> object if mounted */ >>>> + pdrv = LD2PD(vol); /* Physical drive */ >>>> + ipart = LD2PT(vol); /* Partition (0:create as new, >>>> 1..:get from partition table) */ >>>> + if (!opt) opt = &defopt; /* Use default parameter if it is >>>> not given */ >>>> + >>>> + /* Get physical drive status (sz_drv, sz_blk, ss) */ >>>> + ds = disk_initialize(pdrv); >>>> + if (ds & STA_NOINIT) return FR_NOT_READY; >>>> + if (ds & STA_PROTECT) return FR_WRITE_PROTECTED; >>>> + sz_blk = opt->align; >>>> + if (sz_blk == 0 && disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != >>>> RES_OK) sz_blk = 1; >>>> + if (sz_blk == 0 || sz_blk > 0x8000 || (sz_blk & (sz_blk - 1))) >>>> sz_blk = 1; >>>> +#if FF_MAX_SS != FF_MIN_SS >>>> + if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return >>>> FR_DISK_ERR; >>>> + if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) return >>>> FR_DISK_ERR; >>>> +#else >>>> + ss = FF_MAX_SS; >>>> +#endif >>>> + /* Options for FAT sub-type and FAT parameters */ >>>> + fsopt = opt->fmt & (FM_ANY | FM_SFD); >>>> + n_fat = (opt->n_fat >= 1 && opt->n_fat <= 2) ? opt->n_fat : 1; >>>> + n_root = (opt->n_root >= 1 && opt->n_root <= 32768 && >>>> (opt->n_root % (ss / SZDIRE)) == 0) ? opt->n_root : 512; >>>> + sz_au = (opt->au_size <= 0x1000000 && (opt->au_size & >>>> (opt->au_size - 1)) == 0) ? opt->au_size : 0; >>>> + sz_au /= ss; /* Byte --> Sector */ >>>> + >>>> + /* Get working buffer */ >>>> + sz_buf = len / ss; /* Size of working buffer [sector] */ >>>> + if (sz_buf == 0) return FR_NOT_ENOUGH_CORE; >>>> + buf = (BYTE*)work; /* Working buffer */ >>>> +#if FF_USE_LFN == 3 >>>> + if (!buf) buf = ff_memalloc(sz_buf * ss); /* Use heap memory >>>> for working buffer */ >>>> +#endif >>>> + if (!buf) return FR_NOT_ENOUGH_CORE; >>>> + >>>> + /* Determine where the volume to be located (b_vol, sz_vol) */ >>>> + b_vol = sz_vol = 0; >>>> + if (FF_MULTI_PARTITION && ipart != 0) { /* Is the volume >>>> associated with any specific partition? */ >>>> + /* Get partition location from the existing partition table */ >>>> + if (disk_read(pdrv, buf, 0, 1) != RES_OK) >>>> LEAVE_MKFS(FR_DISK_ERR); /* Load MBR */ >>>> + if (ld_word(buf + BS_55AA) != 0xAA55) >>>> LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if MBR is valid */ >>>> +#if FF_LBA64 >>>> + if (buf[MBR_Table + PTE_System] == 0xEE) { /* GPT >>>> protective MBR? */ >>>> + DWORD n_ent, ofs; >>>> + QWORD pt_lba; >>>> + >>>> + /* Get the partition location from GPT */ >>>> + if (disk_read(pdrv, buf, 1, 1) != RES_OK) >>>> LEAVE_MKFS(FR_DISK_ERR); /* Load GPT header sector (next to MBR) */ >>>> + if (!test_gpt_header(buf)) >>>> LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if GPT header is valid */ >>>> + n_ent = ld_dword(buf + GPTH_PtNum); /* Number of >>>> entries */ >>>> + pt_lba = ld_qword(buf + GPTH_PtOfs); /* Table start >>>> sector */ >>>> + ofs = i = 0; >>>> + while (n_ent) { /* Find MS Basic partition with >>>> order of ipart */ >>>> + if (ofs == 0 && disk_read(pdrv, buf, pt_lba++, 1) >>>> != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Get PT sector */ >>>> + if (!mem_cmp(buf + ofs + GPTE_PtGuid, >>>> GUID_MS_Basic, 16) && ++i == ipart) { /* MS basic data partition? */ >>>> + b_vol = ld_qword(buf + ofs + GPTE_FstLba); >>>> + sz_vol = ld_qword(buf + ofs + GPTE_LstLba) - >>>> b_vol + 1; >>>> + break; >>>> + } >>>> + n_ent--; ofs = (ofs + SZ_GPTE) % ss; /* Next >>>> entry */ >>>> + } >>>> + if (n_ent == 0) LEAVE_MKFS(FR_MKFS_ABORTED); /* >>>> Partition not found */ >>>> + fsopt |= 0x80; /* Partitioning is in GPT */ >>>> + } else >>>> +#endif >>>> + { /* Get the partition location from MBR partition table */ >>>> + pte = buf + (MBR_Table + (ipart - 1) * SZ_PTE); >>>> + if (ipart > 4 || pte[PTE_System] == 0) >>>> LEAVE_MKFS(FR_MKFS_ABORTED); /* No partition? */ >>>> + b_vol = ld_dword(pte + PTE_StLba); /* Get volume >>>> start sector */ >>>> + sz_vol = ld_dword(pte + PTE_SizLba); /* Get volume >>>> size */ >>>> + } >>>> + } else { /* The volume is associated with a physical drive */ >>>> + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) >>>> LEAVE_MKFS(FR_DISK_ERR); >>>> + if (!(fsopt & FM_SFD)) { /* To be partitioned? */ >>>> + /* Create a single-partition on the drive in this >>>> function */ >>>> +#if FF_LBA64 >>>> + if (sz_vol >= FF_MIN_GPT) { /* Which partition type >>>> to create, MBR or GPT? */ >>>> + fsopt |= 0x80; /* Partitioning is in GPT */ >>>> + b_vol = GPT_ALIGN / ss; sz_vol -= b_vol + GPT_ITEMS >>>> * SZ_GPTE / ss + 1; /* Estimated partition offset and size */ >>>> + } else >>>> +#endif >>>> + { /* Partitioning is in MBR */ >>>> + if (sz_vol > N_SEC_TRACK) { >>>> + b_vol = N_SEC_TRACK; sz_vol -= b_vol; /* >>>> Estimated partition offset and size */ >>>> + } >>>> + } >>>> + } >>>> + } >>>> + if (sz_vol < 128) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if >>>> volume size is >=128s */ >>>> + >>>> + /* Now start to create a FAT volume at b_vol and sz_vol */ >>>> + >>>> + do { /* Pre-determine the FAT type */ >>>> + if (FF_FS_EXFAT && (fsopt & FM_EXFAT)) { /* exFAT >>>> possible? */ >>>> + if ((fsopt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 >>>> || sz_au > 128) { /* exFAT only, vol >= 64MS or sz_au > 128S ? */ >>>> + fsty = FS_EXFAT; break; >>>> + } >>>> + } >>>> +#if FF_LBA64 >>>> + if (sz_vol >= 0x100000000) LEAVE_MKFS(FR_MKFS_ABORTED); /* >>>> Too large volume for FAT/FAT32 */ >>>> +#endif >>>> + if (sz_au > 128) sz_au = 128; /* Invalid AU for >>>> FAT/FAT32? */ >>>> + if (fsopt & FM_FAT32) { /* FAT32 possible? */ >>>> + if (!(fsopt & FM_FAT)) { /* no-FAT? */ >>>> + fsty = FS_FAT32; break; >>>> + } >>>> + } >>>> + if (!(fsopt & FM_FAT)) LEAVE_MKFS(FR_INVALID_PARAMETER); /* >>>> no-FAT? */ >>>> + fsty = FS_FAT16; >>>> + } while (0); >>>> + >>>> +#if FF_FS_EXFAT >>>> + if (fsty == FS_EXFAT) { /* Create an exFAT volume */ >>>> + DWORD szb_bit, szb_case, sum, nbit, clu, clen[3]; >>>> + WCHAR ch, si; >>>> + UINT j, st; >>>> + >>>> + if (sz_vol < 0x1000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too >>>> small volume for exFAT? */ >>>> +#if FF_USE_TRIM >>>> + lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform >>>> storage device that the volume area may be erased */ >>>> + disk_ioctl(pdrv, CTRL_TRIM, lba); >>>> +#endif >>>> + /* Determine FAT location, data location and number of >>>> clusters */ >>>> + if (sz_au == 0) { /* AU auto-selection */ >>>> + sz_au = 8; >>>> + if (sz_vol >= 0x80000) sz_au = 64; /* >= 512Ks */ >>>> + if (sz_vol >= 0x4000000) sz_au = 256; /* >= 64Ms */ >>>> + } >>>> + b_fat = b_vol + 32; /* FAT start at offset 32 */ >>>> + sz_fat = (DWORD)((sz_vol / sz_au + 2) * 4 + ss - 1) / ss; >>>> /* Number of FAT sectors */ >>>> + b_data = (b_fat + sz_fat + sz_blk - 1) & ~((LBA_t)sz_blk - >>>> 1); /* Align data area to the erase block boundary */ >>>> + if (b_data - b_vol >= sz_vol / 2) >>>> LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */ >>>> + n_clst = (DWORD)(sz_vol - (b_data - b_vol)) / sz_au; /* >>>> Number of clusters */ >>>> + if (n_clst <16) LEAVE_MKFS(FR_MKFS_ABORTED); /* >>>> Too few clusters? */ >>>> + if (n_clst > MAX_EXFAT) LEAVE_MKFS(FR_MKFS_ABORTED); /* >>>> Too many clusters? */ >>>> + >>>> + szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */ >>>> + clen[0] = (szb_bit + sz_au * ss - 1) / (sz_au * ss); /* >>>> Number of allocation bitmap clusters */ >>>> + >>>> + /* Create a compressed up-case table */ >>>> + sect = b_data + sz_au * clen[0]; /* Table start sector */ >>>> + sum = 0; /* Table checksum to be >>>> stored in the 82 entry */ >>>> + st = 0; si = 0; i = 0; j = 0; szb_case = 0; >>>> + do { >>>> + switch (st) { >>>> + case 0: >>>> + ch = (WCHAR)ff_wtoupper(si); /* Get an up-case >>>> char */ >>>> + if (ch != si) { >>>> + si++; break; /* Store the up-case char >>>> if exist */ >>>> + } >>>> + for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == >>>> ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case >>>> block */ >>>> + if (j >= 128) { >>>> + ch = 0xFFFF; st = 2; break; /* Compress the >>>> no-case block if run is >= 128 chars */ >>>> + } >>>> + st = 1; /* Do not compress short run */ >>>> + /* FALLTHROUGH */ >>>> + case 1: >>>> + ch = si++; /* Fill the short run */ >>>> + if (--j == 0) st = 0; >>>> + break; >>>> + >>>> + default: >>>> + ch = (WCHAR)j; si += (WCHAR)j; /* Number of >>>> chars to skip */ >>>> + st = 0; >>>> + } >>>> + sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put >>>> it into the write buffer */ >>>> + sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum); >>>> + i += 2; szb_case += 2; >>>> + if (si == 0 || i == sz_buf * ss) { /* Write >>>> buffered data when buffer full or end of process */ >>>> + n = (i + ss - 1) / ss; >>>> + if (disk_write(pdrv, buf, sect, n) != RES_OK) >>>> LEAVE_MKFS(FR_DISK_ERR); >>>> + sect += n; i = 0; >>>> + } >>>> + } while (si); >>>> + clen[1] = (szb_case + sz_au * ss - 1) / (sz_au * ss); /* >>>> Number of up-case table clusters */ >>>> + clen[2] = 1; /* Number of root dir clusters */ >>>> + >>>> + /* Initialize the allocation bitmap */ >>>> + sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start >>>> of bitmap and number of bitmap sectors */ >>>> + nbit = clen[0] + clen[1] + clen[2]; /* >>>> Number of clusters in-use by system (bitmap, up-case and root-dir) */ >>>> + do { >>>> + mem_set(buf, 0, sz_buf * ss); /* >>>> Initialize bitmap buffer */ >>>> + for (i = 0; nbit != 0 && i / 8 < sz_buf * ss; buf[i / >>>> 8] |= 1 << (i % 8), i++, nbit--) ; /* Mark used clusters */ >>>> + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write >>>> the buffered data */ >>>> + if (disk_write(pdrv, buf, sect, n) != RES_OK) >>>> LEAVE_MKFS(FR_DISK_ERR); >>>> + sect += n; nsect -= n; >>>> + } while (nsect); >>>> + >>>> + /* Initialize the FAT */ >>>> + sect = b_fat; nsect = sz_fat; /* Start of FAT and number >>>> of FAT sectors */ >>>> + j = nbit = clu = 0; >>>> + do { >>>> + mem_set(buf, 0, sz_buf * ss); i = 0; /* Clear work >>>> area and reset write offset */ >>>> + if (clu == 0) { /* Initialize FAT [0] and FAT[1] */ >>>> + st_dword(buf + i, 0xFFFFFFF8); i += 4; clu++; >>>> + st_dword(buf + i, 0xFFFFFFFF); i += 4; clu++; >>>> + } >>>> + do { /* Create chains of bitmap, up-case and >>>> root dir */ >>>> + while (nbit != 0 && i < sz_buf * ss) { /* Create >>>> a chain */ >>>> + st_dword(buf + i, (nbit > 1) ? clu + 1 : >>>> 0xFFFFFFFF); >>>> + i += 4; clu++; nbit--; >>>> + } >>>> + if (nbit == 0 && j < 3) nbit = clen[j++]; /* Get >>>> next chain length */ >>>> + } while (nbit != 0 && i < sz_buf * ss); >>>> + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the >>>> buffered data */ >>>> + if (disk_write(pdrv, buf, sect, n) != RES_OK) >>>> LEAVE_MKFS(FR_DISK_ERR); >>>> + sect += n; nsect -= n; >>>> + } while (nsect); >>>> + >>>> + /* Initialize the root directory */ >>>> + mem_set(buf, 0, sz_buf * ss); >>>> + buf[SZDIRE * 0 + 0] = ET_VLABEL; /* >>>> Volume label entry (no label) */ >>>> + buf[SZDIRE * 1 + 0] = ET_BITMAP; /* >>>> Bitmap entry */ >>>> + st_dword(buf + SZDIRE * 1 + 20, 2); /* >>>> cluster */ >>>> + st_dword(buf + SZDIRE * 1 + 24, szb_bit); /* >>>> size */ >>>> + buf[SZDIRE * 2 + 0] = ET_UPCASE; /* >>>> Up-case table entry */ >>>> + st_dword(buf + SZDIRE * 2 + 4, sum); /* sum */ >>>> + st_dword(buf + SZDIRE * 2 + 20, 2 + clen[0]); /* >>>> cluster */ >>>> + st_dword(buf + SZDIRE * 2 + 24, szb_case); /* >>>> size */ >>>> + sect = b_data + sz_au * (clen[0] + clen[1]); nsect = >>>> sz_au; /* Start of the root directory and number of sectors */ >>>> + do { /* Fill root directory sectors */ >>>> + n = (nsect > sz_buf) ? sz_buf : nsect; >>>> + if (disk_write(pdrv, buf, sect, n) != RES_OK) >>>> LEAVE_MKFS(FR_DISK_ERR); >>>> + mem_set(buf, 0, ss); /* Rest of entries are filled >>>> with zero */ >>>> + sect += n; nsect -= n; >>>> + } while (nsect); >>>> + >>>> + /* Create two set of the exFAT VBR blocks */ >>>> + sect = b_vol; >>>> + for (n = 0; n < 2; n++) { >>>> + /* Main record (+0) */ >>>> + mem_set(buf, 0, ss); >>>> + mem_cpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", >>>> 11); /* Boot jump code (x86), OEM name */ >>>> + st_qword(buf + BPB_VolOfsEx, b_vol); /* Volume offset >>>> in the physical drive [sector] */ >>>> + st_qword(buf + BPB_TotSecEx, sz_vol); /* Volume size >>>> [sector] */ >>>> + st_dword(buf + BPB_FatOfsEx, (DWORD)(b_fat - b_vol)); >>>> /* FAT offset [sector] */ >>>> + st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size >>>> [sector] */ >>>> + st_dword(buf + BPB_DataOfsEx, (DWORD)(b_data - >>>> b_vol)); /* Data offset [sector] */ >>>> + st_dword(buf + BPB_NumClusEx, n_clst); /* Number of >>>> clusters */ >>>> + st_dword(buf + BPB_RootClusEx, 2 + clen[0] + clen[1]); >>>> /* Root dir cluster # */ >>>> + st_dword(buf + BPB_VolIDEx, >>>> GET_FATTIME()); /* VSN */ >>>> + st_word(buf + BPB_FSVerEx, 0x100); /* Filesystem >>>> version (1.00) */ >>>> + for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; >>>> buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */ >>>> + for (buf[BPB_SecPerClusEx] = 0, i = sz_au; i >>= 1; >>>> buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */ >>>> + buf[BPB_NumFATsEx] = 1; /* Number of >>>> FATs */ >>>> + buf[BPB_DrvNumEx] = 0x80; /* Drive >>>> number (for int13) */ >>>> + st_word(buf + BS_BootCodeEx, 0xFEEB); /* Boot code >>>> (x86) */ >>>> + st_word(buf + BS_55AA, 0xAA55); /* Signature >>>> (placed here regardless of sector size) */ >>>> + for (i = sum = 0; i < ss; i++) { /* VBR checksum */ >>>> + if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && >>>> i != BPB_PercInUseEx) sum = xsum32(buf[i], sum); >>>> + } >>>> + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) >>>> LEAVE_MKFS(FR_DISK_ERR); >>>> + /* Extended bootstrap record (+1..+8) */ >>>> + mem_set(buf, 0, ss); >>>> + st_word(buf + ss - 2, 0xAA55); /* Signature (placed >>>> at end of sector) */ >>>> + for (j = 1; j < 9; j++) { >>>> + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; >>>> /* VBR checksum */ >>>> + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) >>>> LEAVE_MKFS(FR_DISK_ERR); >>>> + } >>>> + /* OEM/Reserved record (+9..+10) */ >>>> + mem_set(buf, 0, ss); >>>> + for ( ; j < 11; j++) { >>>> + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; >>>> /* VBR checksum */ >>>> + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) >>>> LEAVE_MKFS(FR_DISK_ERR); >>>> + } >>>> + /* Sum record (+11) */ >>>> + for (i = 0; i < ss; i += 4) st_dword(buf + i, >>>> sum); /* Fill with checksum value */ >>>> + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) >>>> LEAVE_MKFS(FR_DISK_ERR); >>>> + } >>>> + >>>> + } else >>>> +#endif /* FF_FS_EXFAT */ >>>> + { /* Create an FAT/FAT32 volume */ >>>> + do { >>>> + pau = sz_au; >>>> + /* Pre-determine number of clusters and FAT sub-type */ >>>> + if (fsty == FS_FAT32) { /* FAT32 volume */ >>>> + if (pau == 0) { /* AU auto-selection */ >>>> + n = (DWORD)sz_vol / 0x20000; /* Volume size >>>> in unit of 128KS */ >>>> + for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; >>>> i++, pau <<= 1) ; /* Get from table */ >>>> + } >>>> + n_clst = (DWORD)sz_vol / pau; /* Number of >>>> clusters */ >>>> + sz_fat = (n_clst * 4 + 8 + ss - 1) / ss; /* FAT >>>> size [sector] */ >>>> + sz_rsv = 32; /* Number of reserved sectors */ >>>> + sz_dir = 0; /* No static directory */ >>>> + if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) >>>> LEAVE_MKFS(FR_MKFS_ABORTED); >>>> + } else { /* FAT volume */ >>>> + if (pau == 0) { /* au auto-selection */ >>>> + n = (DWORD)sz_vol / 0x1000; /* Volume size >>>> in unit of 4KS */ >>>> + for (i = 0, pau = 1; cst[i] && cst[i] <= n; >>>> i++, pau <<= 1) ; /* Get from table */ >>>> + } >>>> + n_clst = (DWORD)sz_vol / pau; >>>> + if (n_clst > MAX_FAT12) { >>>> + n = n_clst * 2 + 4; /* FAT size [byte] */ >>>> + } else { >>>> + fsty = FS_FAT12; >>>> + n = (n_clst * 3 + 1) / 2 + 3; /* FAT size >>>> [byte] */ >>>> + } >>>> + sz_fat = (n + ss - 1) / ss; /* FAT size >>>> [sector] */ >>>> + sz_rsv = 1; /* Number of >>>> reserved sectors */ >>>> + sz_dir = (DWORD)n_root * SZDIRE / ss; /* Root >>>> dir size [sector] */ >>>> + } >>>> + b_fat = b_vol + sz_rsv; /* FAT >>>> base */ >>>> + b_data = b_fat + sz_fat * n_fat + sz_dir; /* Data >>>> base */ >>>> + >>>> + /* Align data area to erase block boundary (for flash >>>> memory media) */ >>>> + n = (DWORD)(((b_data + sz_blk - 1) & ~(sz_blk - 1)) - >>>> b_data); /* Sectors to next nearest from current data base */ >>>> + if (fsty == FS_FAT32) { /* FAT32: Move FAT */ >>>> + sz_rsv += n; b_fat += n; >>>> + } else { /* FAT: Expand FAT */ >>>> + if (n % n_fat) { /* Adjust fractional error if >>>> needed */ >>>> + n--; sz_rsv++; b_fat++; >>>> + } >>>> + sz_fat += n / n_fat; >>>> + } >>>> + >>>> + /* Determine number of clusters and final check of >>>> validity of the FAT sub-type */ >>>> + if (sz_vol < b_data + pau * 16 - b_vol) >>>> LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */ >>>> + n_clst = ((DWORD)sz_vol - sz_rsv - sz_fat * n_fat - >>>> sz_dir) / pau; >>>> + if (fsty == FS_FAT32) { >>>> + if (n_clst <= MAX_FAT16) { /* Too few clusters >>>> for FAT32? */ >>>> + if (sz_au == 0 && (sz_au = pau / 2) != 0) >>>> continue; /* Adjust cluster size and retry */ >>>> + LEAVE_MKFS(FR_MKFS_ABORTED); >>>> + } >>>> + } >>>> + if (fsty == FS_FAT16) { >>>> + if (n_clst > MAX_FAT16) { /* Too many clusters >>>> for FAT16 */ >>>> + if (sz_au == 0 && (pau * 2) <= 64) { >>>> + sz_au = pau * 2; continue; /* Adjust >>>> cluster size and retry */ >>>> + } >>>> + if ((fsopt & FM_FAT32)) { >>>> + fsty = FS_FAT32; continue; /* Switch >>>> type to FAT32 and retry */ >>>> + } >>>> + if (sz_au == 0 && (sz_au = pau * 2) <= 128) >>>> continue; /* Adjust cluster size and retry */ >>>> + LEAVE_MKFS(FR_MKFS_ABORTED); >>>> + } >>>> + if (n_clst <= MAX_FAT12) { /* Too few clusters >>>> for FAT16 */ >>>> + if (sz_au == 0 && (sz_au = pau * 2) <= 128) >>>> continue; /* Adjust cluster size and retry */ >>>> + LEAVE_MKFS(FR_MKFS_ABORTED); >>>> + } >>>> + } >>>> + if (fsty == FS_FAT12 && n_clst > MAX_FAT12) >>>> LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters for FAT12 */ >>>> + >>>> + /* Ok, it is the valid cluster configuration */ >>>> + break; >>>> + } while (1); >>>> + >>>> +#if FF_USE_TRIM >>>> + lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform >>>> storage device that the volume area may be erased */ >>>> + disk_ioctl(pdrv, CTRL_TRIM, lba); >>>> +#endif >>>> + /* Create FAT VBR */ >>>> + mem_set(buf, 0, ss); >>>> + mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* >>>> Boot jump code (x86), OEM name */ >>>> + st_word(buf + BPB_BytsPerSec, ss); /* Sector >>>> size [byte] */ >>>> + buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster >>>> size [sector] */ >>>> + st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of >>>> reserved area */ >>>> + buf[BPB_NumFATs] = (BYTE)n_fat; /* >>>> Number of FATs */ >>>> + st_word(buf + BPB_RootEntCnt, (WORD)((fsty == FS_FAT32) ? 0 >>>> : n_root)); /* Number of root directory entries */ >>>> + if (sz_vol < 0x10000) { >>>> + st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume >>>> size in 16-bit LBA */ >>>> + } else { >>>> + st_dword(buf + BPB_TotSec32, (DWORD)sz_vol); /* >>>> Volume size in 32-bit LBA */ >>>> + } >>>> + buf[BPB_Media] = 0xF8; /* Media >>>> descriptor byte */ >>>> + st_word(buf + BPB_SecPerTrk, 63); /* Number >>>> of sectors per track (for int13) */ >>>> + st_word(buf + BPB_NumHeads, 255); /* Number >>>> of heads (for int13) */ >>>> + st_dword(buf + BPB_HiddSec, (DWORD)b_vol); /* Volume >>>> offset in the physical drive [sector] */ >>>> + if (fsty == FS_FAT32) { >>>> + st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */ >>>> + st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size >>>> [sector] */ >>>> + st_dword(buf + BPB_RootClus32, 2); /* Root >>>> directory cluster # (2) */ >>>> + st_word(buf + BPB_FSInfo32, 1); /* >>>> Offset of FSINFO sector (VBR + 1) */ >>>> + st_word(buf + BPB_BkBootSec32, 6); /* Offset >>>> of backup VBR (VBR + 6) */ >>>> + buf[BS_DrvNum32] = 0x80; /* Drive >>>> number (for int13) */ >>>> + buf[BS_BootSig32] = 0x29; /* >>>> Extended boot signature */ >>>> + mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", >>>> 19); /* Volume label, FAT signature */ >>>> + } else { >>>> + st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */ >>>> + st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT >>>> size [sector] */ >>>> + buf[BS_DrvNum] = 0x80; /* Drive >>>> number (for int13) */ >>>> + buf[BS_BootSig] = 0x29; /* >>>> Extended boot signature */ >>>> + mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", >>>> 19); /* Volume label, FAT signature */ >>>> + } >>>> + st_word(buf + BS_55AA, 0xAA55); /* >>>> Signature (offset is fixed here regardless of sector size) */ >>>> + if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) >>>> LEAVE_MKFS(FR_DISK_ERR); /* Write it to the VBR sector */ >>>> + >>>> + /* Create FSINFO record if needed */ >>>> + if (fsty == FS_FAT32) { >>>> + disk_write(pdrv, buf, b_vol + 6, 1); /* Write >>>> backup VBR (VBR + 6) */ >>>> + mem_set(buf, 0, ss); >>>> + st_dword(buf + FSI_LeadSig, 0x41615252); >>>> + st_dword(buf + FSI_StrucSig, 0x61417272); >>>> + st_dword(buf + FSI_Free_Count, n_clst - 1); /* >>>> Number of free clusters */ >>>> + st_dword(buf + FSI_Nxt_Free, 2); /* Last >>>> allocated cluster# */ >>>> + st_word(buf + BS_55AA, 0xAA55); >>>> + disk_write(pdrv, buf, b_vol + 7, 1); /* Write >>>> backup FSINFO (VBR + 7) */ >>>> + disk_write(pdrv, buf, b_vol + 1, 1); /* Write >>>> original FSINFO (VBR + 1) */ >>>> + } >>>> + >>>> + /* Initialize FAT area */ >>>> + mem_set(buf, 0, sz_buf * ss); >>>> + sect = b_fat; /* FAT start sector */ >>>> + for (i = 0; i < n_fat; i++) { /* Initialize FATs >>>> each */ >>>> + if (fsty == FS_FAT32) { >>>> + st_dword(buf + 0, 0xFFFFFFF8); /* FAT[0] */ >>>> + st_dword(buf + 4, 0xFFFFFFFF); /* FAT[1] */ >>>> + st_dword(buf + 8, 0x0FFFFFFF); /* FAT[2] (root >>>> directory) */ >>>> + } else { >>>> + st_dword(buf + 0, (fsty == FS_FAT12) ? 0xFFFFF8 : >>>> 0xFFFFFFF8); /* FAT[0] and FAT[1] */ >>>> + } >>>> + nsect = sz_fat; /* Number of FAT sectors */ >>>> + do { /* Fill FAT sectors */ >>>> + n = (nsect > sz_buf) ? sz_buf : nsect; >>>> + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) >>>> LEAVE_MKFS(FR_DISK_ERR); >>>> + mem_set(buf, 0, ss); /* Rest of FAT all are >>>> cleared */ >>>> + sect += n; nsect -= n; >>>> + } while (nsect); >>>> + } >>>> + >>>> + /* Initialize root directory (fill with zero) */ >>>> + nsect = (fsty == FS_FAT32) ? pau : sz_dir; /* Number of >>>> root directory sectors */ >>>> + do { >>>> + n = (nsect > sz_buf) ? sz_buf : nsect; >>>> + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) >>>> LEAVE_MKFS(FR_DISK_ERR); >>>> + sect += n; nsect -= n; >>>> + } while (nsect); >>>> + } >>>> + >>>> + /* A FAT volume has been created here */ >>>> + >>>> + /* Determine system ID in the MBR partition table */ >>>> + if (FF_FS_EXFAT && fsty == FS_EXFAT) { >>>> + sys = 0x07; /* exFAT */ >>>> + } else { >>>> + if (fsty == FS_FAT32) { >>>> + sys = 0x0C; /* FAT32X */ >>>> + } else { >>>> + if (sz_vol >= 0x10000) { >>>> + sys = 0x06; /* FAT12/16 (large) */ >>>> + } else { >>>> + sys = (fsty == FS_FAT16) ? 0x04 : 0x01; /* FAT16 >>>> : FAT12 */ >>>> + } >>>> + } >>>> + } >>>> + >>>> + /* Update partition information */ >>>> + if (FF_MULTI_PARTITION && ipart != 0) { /* Volume is in the >>>> existing partition */ >>>> + if (!FF_LBA64 || !(fsopt & 0x80)) { >>>> + /* Update system ID in the partition table */ >>>> + if (disk_read(pdrv, buf, 0, 1) != RES_OK) >>>> LEAVE_MKFS(FR_DISK_ERR); /* Read the MBR */ >>>> + buf[MBR_Table + (ipart - 1) * SZ_PTE + PTE_System] = >>>> sys; /* Set system ID */ >>>> + if (disk_write(pdrv, buf, 0, 1) != RES_OK) >>>> LEAVE_MKFS(FR_DISK_ERR); /* Write it back to the MBR */ >>>> + } >>>> + } else { /* Volume as a new >>>> single partition */ >>>> + if (!(fsopt & FM_SFD)) { /* Create partition table if >>>> not in SFD */ >>>> + lba[0] = sz_vol, lba[1] = 0; >>>> + fr = create_partition(pdrv, lba, sys, buf); >>>> + if (fr != FR_OK) LEAVE_MKFS(fr); >>>> + } >>>> + } >>>> + >>>> + if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) >>>> LEAVE_MKFS(FR_DISK_ERR); >>>> + >>>> + LEAVE_MKFS(FR_OK); >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +#if FF_MULTI_PARTITION >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Create Partition Table on the Physical >>>> Drive */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_fdisk ( >>>> + BYTE pdrv, /* Physical drive number */ >>>> + const LBA_t ptbl[], /* Pointer to the size table for each >>>> partitions */ >>>> + void* work /* Pointer to the working buffer (null: >>>> use heap memory) */ >>>> +) >>>> +{ >>>> + BYTE *buf = (BYTE*)work; >>>> + DSTATUS stat; >>>> + >>>> + >>>> + stat = disk_initialize(pdrv); >>>> + if (stat & STA_NOINIT) return FR_NOT_READY; >>>> + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; >>>> +#if FF_USE_LFN == 3 >>>> + if (!buf) buf = ff_memalloc(FF_MAX_SS); /* Use heap memory >>>> for working buffer */ >>>> +#endif >>>> + if (!buf) return FR_NOT_ENOUGH_CORE; >>>> + >>>> + LEAVE_MKFS(create_partition(pdrv, ptbl, 0x07, buf)); >>>> +} >>>> + >>>> +#endif /* FF_MULTI_PARTITION */ >>>> +#endif /* !FF_FS_READONLY && FF_USE_MKFS */ >>>> + >>>> + >>>> + >>>> + >>>> +#if FF_USE_STRFUNC >>>> +#if FF_USE_LFN && FF_LFN_UNICODE && (FF_STRF_ENCODE < 0 || >>>> FF_STRF_ENCODE > 3) >>>> +#error Wrong FF_STRF_ENCODE setting >>>> +#endif >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Get a String from the >>>> File */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +TCHAR* f_gets ( >>>> + TCHAR* buff, /* Pointer to the buffer to store read string */ >>>> + int len, /* Size of string buffer (items) */ >>>> + FIL* fp /* Pointer to the file object */ >>>> +) >>>> +{ >>>> + int nc = 0; >>>> + TCHAR *p = buff; >>>> + BYTE s[4]; >>>> + UINT rc; >>>> + DWORD dc; >>>> +#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE <= 2 >>>> + WCHAR wc; >>>> +#endif >>>> +#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE == 3 >>>> + UINT ct; >>>> +#endif >>>> + >>>> +#if FF_USE_LFN && FF_LFN_UNICODE /* With code conversion >>>> (Unicode API) */ >>>> + /* Make a room for the character and terminator */ >>>> + if (FF_LFN_UNICODE == 1) len -= (FF_STRF_ENCODE == 0) ? 1 : 2; >>>> + if (FF_LFN_UNICODE == 2) len -= (FF_STRF_ENCODE == 0) ? 3 : 4; >>>> + if (FF_LFN_UNICODE == 3) len -= 1; >>>> + while (nc < len) { >>>> +#if FF_STRF_ENCODE == 0 /* Read a character in >>>> ANSI/OEM */ >>>> + f_read(fp, s, 1, &rc); /* Get a code unit */ >>>> + if (rc != 1) break; /* EOF? */ >>>> + wc = s[0]; >>>> + if (dbc_1st((BYTE)wc)) { /* DBC 1st byte? */ >>>> + f_read(fp, s, 1, &rc); /* Get DBC 2nd byte */ >>>> + if (rc != 1 || !dbc_2nd(s[0])) continue; /* Wrong >>>> code? */ >>>> + wc = wc << 8 | s[0]; >>>> + } >>>> + dc = ff_oem2uni(wc, CODEPAGE); /* OEM --> */ >>>> + if (dc == 0) continue; >>>> +#elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2 /* Read a >>>> character in UTF-16LE/BE */ >>>> + f_read(fp, s, 2, &rc); /* Get a code unit */ >>>> + if (rc != 2) break; /* EOF? */ >>>> + dc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1]; >>>> + if (IsSurrogateL(dc)) continue; /* Broken surrogate >>>> pair? */ >>>> + if (IsSurrogateH(dc)) { /* High surrogate? */ >>>> + f_read(fp, s, 2, &rc); /* Get low surrogate */ >>>> + if (rc != 2) break; /* EOF? */ >>>> + wc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | >>>> s[1]; >>>> + if (!IsSurrogateL(wc)) continue; /* Broken surrogate >>>> pair? */ >>>> + dc = ((dc & 0x3FF) + 0x40) << 10 | (wc & 0x3FF); /* >>>> Merge surrogate pair */ >>>> + } >>>> +#else /* Read a character in UTF-8 */ >>>> + f_read(fp, s, 1, &rc); /* Get a code unit */ >>>> + if (rc != 1) break; /* EOF? */ >>>> + dc = s[0]; >>>> + if (dc >= 0x80) { /* Multi-byte sequence? */ >>>> + ct = 0; >>>> + if ((dc & 0xE0) == 0xC0) { dc &= 0x1F; ct = 1; } /* >>>> 2-byte sequence? */ >>>> + if ((dc & 0xF0) == 0xE0) { dc &= 0x0F; ct = 2; } /* >>>> 3-byte sequence? */ >>>> + if ((dc & 0xF8) == 0xF0) { dc &= 0x07; ct = 3; } /* >>>> 4-byte sequence? */ >>>> + if (ct == 0) continue; >>>> + f_read(fp, s, ct, &rc); /* Get trailing bytes */ >>>> + if (rc != ct) break; >>>> + rc = 0; >>>> + do { /* Merge the byte sequence */ >>>> + if ((s[rc] & 0xC0) != 0x80) break; >>>> + dc = dc << 6 | (s[rc] & 0x3F); >>>> + } while (++rc < ct); >>>> + if (rc != ct || dc < 0x80 || IsSurrogate(dc) || dc >= >>>> 0x110000) continue; /* Wrong encoding? */ >>>> + } >>>> +#endif >>>> + /* A code point is avaialble in dc to be output */ >>>> + >>>> + if (FF_USE_STRFUNC == 2 && dc == '\r') continue; /* >>>> Strip \r off if needed */ >>>> +#if FF_LFN_UNICODE == 1 || FF_LFN_UNICODE == 3 /* Output it >>>> in UTF-16/32 encoding */ >>>> + if (FF_LFN_UNICODE == 1 && dc >= 0x10000) { /* Out of >>>> BMP at UTF-16? */ >>>> + *p++ = (TCHAR)(0xD800 | ((dc >> 10) - 0x40)); nc++; /* >>>> Make and output high surrogate */ >>>> + dc = 0xDC00 | (dc & 0x3FF); /* Make low >>>> surrogate */ >>>> + } >>>> + *p++ = (TCHAR)dc; nc++; >>>> + if (dc == '\n') break; /* End of line? */ >>>> +#elif FF_LFN_UNICODE == 2 /* Output it in UTF-8 encoding */ >>>> + if (dc < 0x80) { /* Single byte? */ >>>> + *p++ = (TCHAR)dc; >>>> + nc++; >>>> + if (dc == '\n') break; /* End of line? */ >>>> + } else { >>>> + if (dc < 0x800) { /* 2-byte sequence? */ >>>> + *p++ = (TCHAR)(0xC0 | (dc >> 6 & 0x1F)); >>>> + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); >>>> + nc += 2; >>>> + } else { >>>> + if (dc < 0x10000) { /* 3-byte sequence? */ >>>> + *p++ = (TCHAR)(0xE0 | (dc >> 12 & 0x0F)); >>>> + *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F)); >>>> + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); >>>> + nc += 3; >>>> + } else { /* 4-byte sequence? */ >>>> + *p++ = (TCHAR)(0xF0 | (dc >> 18 & 0x07)); >>>> + *p++ = (TCHAR)(0x80 | (dc >> 12 & 0x3F)); >>>> + *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F)); >>>> + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); >>>> + nc += 4; >>>> + } >>>> + } >>>> + } >>>> +#endif >>>> + } >>>> + >>>> +#else /* Byte-by-byte read without any conversion >>>> (ANSI/OEM API) */ >>>> + len -= 1; /* Make a room for the terminator */ >>>> + while (nc < len) { >>>> + f_read(fp, s, 1, &rc); /* Get a byte */ >>>> + if (rc != 1) break; /* EOF? */ >>>> + dc = s[0]; >>>> + if (FF_USE_STRFUNC == 2 && dc == '\r') continue; >>>> + *p++ = (TCHAR)dc; nc++; >>>> + if (dc == '\n') break; >>>> + } >>>> +#endif >>>> + >>>> + *p = 0; /* Terminate the string */ >>>> + return nc ? buff : 0; /* When no data read due to EOF or >>>> error, return with error. */ >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +#if !FF_FS_READONLY >>>> +#include <stdarg.h> >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Put a Character to the File >>>> (sub-functions) */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +/* Putchar output buffer and work area */ >>>> + >>>> +typedef struct { >>>> + FIL *fp; /* Ptr to the writing file */ >>>> + int idx, nchr; /* Write index of buf[] (-1:error), number of >>>> encoding units written */ >>>> +#if FF_USE_LFN && FF_LFN_UNICODE == 1 >>>> + WCHAR hs; >>>> +#elif FF_USE_LFN && FF_LFN_UNICODE == 2 >>>> + BYTE bs[4]; >>>> + UINT wi, ct; >>>> +#endif >>>> + BYTE buf[64]; /* Write buffer */ >>>> +} putbuff; >>>> + >>>> + >>>> +/* Buffered write with code conversion */ >>>> + >>>> +static void putc_bfd (putbuff* pb, TCHAR c) >>>> +{ >>>> + UINT n; >>>> + int i, nc; >>>> +#if FF_USE_LFN && FF_LFN_UNICODE >>>> + WCHAR hs, wc; >>>> +#if FF_LFN_UNICODE == 2 >>>> + DWORD dc; >>>> + const TCHAR *tp; >>>> +#endif >>>> +#endif >>>> + >>>> + if (FF_USE_STRFUNC == 2 && c == '\n') { /* LF -> CRLF >>>> conversion */ >>>> + putc_bfd(pb, '\r'); >>>> + } >>>> + >>>> + i = pb->idx; /* Write index of pb->buf[] */ >>>> + if (i < 0) return; >>>> + nc = pb->nchr; /* Write unit counter */ >>>> + >>>> +#if FF_USE_LFN && FF_LFN_UNICODE >>>> +#if FF_LFN_UNICODE == 1 /* UTF-16 input */ >>>> + if (IsSurrogateH(c)) { /* High surrogate? */ >>>> + pb->hs = c; return; /* Save it for next */ >>>> + } >>>> + hs = pb->hs; pb->hs = 0; >>>> + if (hs != 0) { /* There is a leading high surrogate */ >>>> + if (!IsSurrogateL(c)) hs = 0; /* Discard high surrogate >>>> if not a surrogate pair */ >>>> + } else { >>>> + if (IsSurrogateL(c)) return; /* Discard stray low >>>> surrogate */ >>>> + } >>>> + wc = c; >>>> +#elif FF_LFN_UNICODE == 2 /* UTF-8 input */ >>>> + for (;;) { >>>> + if (pb->ct == 0) { /* Out of multi-byte sequence? */ >>>> + pb->bs[pb->wi = 0] = (BYTE)c; /* Save 1st byte */ >>>> + if ((BYTE)c < 0x80) break; /* Single >>>> byte? */ >>>> + if (((BYTE)c & 0xE0) == 0xC0) pb->ct = 1; /* 2-byte >>>> sequence? */ >>>> + if (((BYTE)c & 0xF0) == 0xE0) pb->ct = 2; /* 3-byte >>>> sequence? */ >>>> + if (((BYTE)c & 0xF1) == 0xF0) pb->ct = 3; /* 4-byte >>>> sequence? */ >>>> + return; >>>> + } else { /* In the multi-byte sequence */ >>>> + if (((BYTE)c & 0xC0) != 0x80) { /* Broken sequence? */ >>>> + pb->ct = 0; continue; >>>> + } >>>> + pb->bs[++pb->wi] = (BYTE)c; /* Save the trailing >>>> byte */ >>>> + if (--pb->ct == 0) break; /* End of multi-byte >>>> sequence? */ >>>> + return; >>>> + } >>>> + } >>>> + tp = (const TCHAR*)pb->bs; >>>> + dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */ >>>> + if (dc == 0xFFFFFFFF) return; /* Wrong code? */ >>>> + wc = (WCHAR)dc; >>>> + hs = (WCHAR)(dc >> 16); >>>> +#elif FF_LFN_UNICODE == 3 /* UTF-32 input */ >>>> + if (IsSurrogate(c) || c >= 0x110000) return; /* Discard >>>> invalid code */ >>>> + if (c >= 0x10000) { /* Out of BMP? */ >>>> + hs = (WCHAR)(0xD800 | ((c >> 10) - 0x40)); /* Make high >>>> surrogate */ >>>> + wc = 0xDC00 | (c & 0x3FF); /* Make low >>>> surrogate */ >>>> + } else { >>>> + hs = 0; >>>> + wc = (WCHAR)c; >>>> + } >>>> +#endif >>>> + /* A code point in UTF-16 is available in hs and wc */ >>>> + >>>> +#if FF_STRF_ENCODE == 1 /* Write a code point in UTF-16LE */ >>>> + if (hs != 0) { /* Surrogate pair? */ >>>> + st_word(&pb->buf[i], hs); >>>> + i += 2; >>>> + nc++; >>>> + } >>>> + st_word(&pb->buf[i], wc); >>>> + i += 2; >>>> +#elif FF_STRF_ENCODE == 2 /* Write a code point in UTF-16BE */ >>>> + if (hs != 0) { /* Surrogate pair? */ >>>> + pb->buf[i++] = (BYTE)(hs >> 8); >>>> + pb->buf[i++] = (BYTE)hs; >>>> + nc++; >>>> + } >>>> + pb->buf[i++] = (BYTE)(wc >> 8); >>>> + pb->buf[i++] = (BYTE)wc; >>>> +#elif FF_STRF_ENCODE == 3 /* Write a code point in UTF-8 */ >>>> + if (hs != 0) { /* 4-byte sequence? */ >>>> + nc += 3; >>>> + hs = (hs & 0x3FF) + 0x40; >>>> + pb->buf[i++] = (BYTE)(0xF0 | hs >> 8); >>>> + pb->buf[i++] = (BYTE)(0x80 | (hs >> 2 & 0x3F)); >>>> + pb->buf[i++] = (BYTE)(0x80 | (hs & 3) << 4 | (wc >> 6 & >>>> 0x0F)); >>>> + pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F)); >>>> + } else { >>>> + if (wc < 0x80) { /* Single byte? */ >>>> + pb->buf[i++] = (BYTE)wc; >>>> + } else { >>>> + if (wc < 0x800) { /* 2-byte sequence? */ >>>> + nc += 1; >>>> + pb->buf[i++] = (BYTE)(0xC0 | wc >> 6); >>>> + } else { /* 3-byte sequence */ >>>> + nc += 2; >>>> + pb->buf[i++] = (BYTE)(0xE0 | wc >> 12); >>>> + pb->buf[i++] = (BYTE)(0x80 | (wc >> 6 & 0x3F)); >>>> + } >>>> + pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F)); >>>> + } >>>> + } >>>> +#else /* Write a code point in ANSI/OEM */ >>>> + if (hs != 0) return; >>>> + wc = ff_uni2oem(wc, CODEPAGE); /* UTF-16 ==> ANSI/OEM */ >>>> + if (wc == 0) return; >>>> + if (wc >= 0x100) { >>>> + pb->buf[i++] = (BYTE)(wc >> 8); nc++; >>>> + } >>>> + pb->buf[i++] = (BYTE)wc; >>>> +#endif >>>> + >>>> +#else /* ANSI/OEM input (without >>>> re-encoding) */ >>>> + pb->buf[i++] = (BYTE)c; >>>> +#endif >>>> + >>>> + if (i >= (int)(sizeof pb->buf) - 4) { /* Write buffered >>>> characters to the file */ >>>> + f_write(pb->fp, pb->buf, (UINT)i, &n); >>>> + i = (n == (UINT)i) ? 0 : -1; >>>> + } >>>> + pb->idx = i; >>>> + pb->nchr = nc + 1; >>>> +} >>>> + >>>> + >>>> +/* Flush remaining characters in the buffer */ >>>> + >>>> +static int putc_flush (putbuff* pb) >>>> +{ >>>> + UINT nw; >>>> + >>>> + if ( pb->idx >= 0 /* Flush buffered characters to the file */ >>>> + && f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK >>>> + && (UINT)pb->idx == nw) return pb->nchr; >>>> + return -1; >>>> +} >>>> + >>>> + >>>> +/* Initialize write buffer */ >>>> + >>>> +static void putc_init (putbuff* pb, FIL* fp) >>>> +{ >>>> + mem_set(pb, 0, sizeof (putbuff)); >>>> + pb->fp = fp; >>>> +} >>>> + >>>> + >>>> + >>>> +int f_putc ( >>>> + TCHAR c, /* A character to be output */ >>>> + FIL* fp /* Pointer to the file object */ >>>> +) >>>> +{ >>>> + putbuff pb; >>>> + >>>> + >>>> + putc_init(&pb, fp); >>>> + putc_bfd(&pb, c); /* Put the character */ >>>> + return putc_flush(&pb); >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Put a String to the >>>> File */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +int f_puts ( >>>> + const TCHAR* str, /* Pointer to the string to be output */ >>>> + FIL* fp /* Pointer to the file object */ >>>> +) >>>> +{ >>>> + putbuff pb; >>>> + >>>> + >>>> + putc_init(&pb, fp); >>>> + while (*str) putc_bfd(&pb, *str++); /* Put the string */ >>>> + return putc_flush(&pb); >>>> +} >>>> + >>>> + >>>> + >>>> + >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Put a Formatted String to the >>>> File */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +int f_printf ( >>>> + FIL* fp, /* Pointer to the file object */ >>>> + const TCHAR* fmt, /* Pointer to the format string */ >>>> + ... /* Optional arguments... */ >>>> +) >>>> +{ >>>> + va_list arp; >>>> + putbuff pb; >>>> + BYTE f, r; >>>> + UINT i, j, w; >>>> + DWORD v; >>>> + TCHAR c, d, str[32], *p; >>>> + >>>> + >>>> + putc_init(&pb, fp); >>>> + >>>> + va_start(arp, fmt); >>>> + >>>> + for (;;) { >>>> + c = *fmt++; >>>> + if (c == 0) break; /* End of string */ >>>> + if (c != '%') { /* Non escape character */ >>>> + putc_bfd(&pb, c); >>>> + continue; >>>> + } >>>> + w = f = 0; >>>> + c = *fmt++; >>>> + if (c == '0') { /* Flag: '0' padding */ >>>> + f = 1; c = *fmt++; >>>> + } else { >>>> + if (c == '-') { /* Flag: left justified */ >>>> + f = 2; c = *fmt++; >>>> + } >>>> + } >>>> + if (c == '*') { /* Minimum width by argument */ >>>> + w = va_arg(arp, int); >>>> + c = *fmt++; >>>> + } else { >>>> + while (IsDigit(c)) { /* Minimum width */ >>>> + w = w * 10 + c - '0'; >>>> + c = *fmt++; >>>> + } >>>> + } >>>> + if (c == 'l' || c == 'L') { /* Type prefix: Size is long >>>> int */ >>>> + f |= 4; c = *fmt++; >>>> + } >>>> + if (c == 0) break; >>>> + d = c; >>>> + if (IsLower(d)) d -= 0x20; >>>> + switch (d) { /* Atgument type is... */ >>>> + case 'S': /* String */ >>>> + p = va_arg(arp, TCHAR*); >>>> + for (j = 0; p[j]; j++) ; >>>> + if (!(f & 2)) { /* Right padded */ >>>> + while (j++ < w) putc_bfd(&pb, ' ') ; >>>> + } >>>> + while (*p) putc_bfd(&pb, *p++) ; /* String body */ >>>> + while (j++ < w) putc_bfd(&pb, ' ') ; /* Left padded */ >>>> + continue; >>>> + >>>> + case 'C': /* Character */ >>>> + putc_bfd(&pb, (TCHAR)va_arg(arp, int)); >>>> + continue; >>>> + >>>> + case 'B': /* Unsigned binary */ >>>> + r = 2; >>>> + break; >>>> + >>>> + case 'O': /* Unsigned octal */ >>>> + r = 8; >>>> + break; >>>> + >>>> + case 'D': /* Signed decimal */ >>>> + case 'U': /* Unsigned decimal */ >>>> + r = 10; >>>> + break; >>>> + >>>> + case 'X': /* Unsigned hexdecimal */ >>>> + r = 16; >>>> + break; >>>> + >>>> + default: /* Unknown type (pass-through) */ >>>> + putc_bfd(&pb, c); continue; >>>> + } >>>> + >>>> + /* Get an argument and put it in numeral */ >>>> + v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? >>>> (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int)); >>>> + if (d == 'D' && (v & 0x80000000)) { >>>> + v = 0 - v; >>>> + f |= 8; >>>> + } >>>> + i = 0; >>>> + do { >>>> + d = (TCHAR)(v % r); v /= r; >>>> + if (d > 9) d += (c == 'x') ? 0x27 : 0x07; >>>> + str[i++] = d + '0'; >>>> + } while (v && i < sizeof str / sizeof *str); >>>> + if (f & 8) str[i++] = '-'; >>>> + j = i; d = (f & 1) ? '0' : ' '; >>>> + if (!(f & 2)) { >>>> + while (j++ < w) putc_bfd(&pb, d); /* Right pad */ >>>> + } >>>> + do { >>>> + putc_bfd(&pb, str[--i]); /* Number body */ >>>> + } while (i); >>>> + while (j++ < w) putc_bfd(&pb, d); /* Left pad */ >>>> + } >>>> + >>>> + va_end(arp); >>>> + >>>> + return putc_flush(&pb); >>>> +} >>>> + >>>> +#endif /* !FF_FS_READONLY */ >>>> +#endif /* FF_USE_STRFUNC */ >>>> + >>>> + >>>> + >>>> +#if FF_CODE_PAGE == 0 >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> +/* Set Active Codepage for the Path >>>> Name */ >>>> +/*-----------------------------------------------------------------------*/ >>>> >>>> + >>>> +FRESULT f_setcp ( >>>> + WORD cp /* Value to be set as active code page */ >>>> +) >>>> +{ >>>> + static const WORD validcp[] = { 437, 720, 737, 771, >>>> 775, 850, 852, 857, 860, 861, 862, 863, 864, 865, >>>> 866, 869, 932, 936, 949, 950, 0}; >>>> + static const BYTE* const tables[] = {Ct437, Ct720, Ct737, >>>> Ct771, Ct775, Ct850, Ct852, Ct857, Ct860, Ct861, Ct862, Ct863, >>>> Ct864, Ct865, Ct866, Ct869, Dc932, Dc936, Dc949, Dc950, 0}; >>>> + UINT i; >>>> + >>>> + >>>> + for (i = 0; validcp[i] != 0 && validcp[i] != cp; i++) ; /* >>>> Find the code page */ >>>> + if (validcp[i] != cp) return FR_INVALID_PARAMETER; /* Not >>>> found? */ >>>> + >>>> + CodePage = cp; >>>> + if (cp >= 900) { /* DBCS */ >>>> + ExCvt = 0; >>>> + DbcTbl = tables[i]; >>>> + } else { /* SBCS */ >>>> + ExCvt = tables[i]; >>>> + DbcTbl = 0; >>>> + } >>>> + return FR_OK; >>>> +} >>>> +#endif /* FF_CODE_PAGE == 0 */ >>>> diff --git a/fatfs/ff.h b/fatfs/ff.h >>>> new file mode 100644 >>>> index 0000000..ac41fa4 >>>> --- /dev/null >>>> +++ b/fatfs/ff.h >>>> @@ -0,0 +1,425 @@ >>>> +/*----------------------------------------------------------------------------/ >>>> >>>> +/ FatFs - Generic FAT Filesystem module >>>> R0.14a / >>>> +/-----------------------------------------------------------------------------/ >>>> >>>> +/ >>>> +/ Copyright (C) 2020, ChaN, all right reserved. >>>> +/ >>>> +/ FatFs module is an open source software. Redistribution and use >>>> of FatFs in >>>> +/ source and binary forms, with or without modification, are >>>> permitted provided >>>> +/ that the following condition is met: >>>> +/ >>>> +/ 1. Redistributions of source code must retain the above copyright >>>> notice, >>>> +/ this condition and the following disclaimer. >>>> +/ >>>> +/ This software is provided by the copyright holder and >>>> contributors "AS IS" >>>> +/ and any warranties related to this software are DISCLAIMED. >>>> +/ The copyright owner or contributors be NOT LIABLE for any damages >>>> caused >>>> +/ by use of this software. >>>> +/ >>>> +/ SPDX-License-Identifier: BSD-1-Clause >>>> +/ >>>> +/----------------------------------------------------------------------------*/ >>>> >>>> + >>>> + >>>> +#ifndef FF_DEFINED >>>> +#define FF_DEFINED 80196 /* Revision ID */ >>>> + >>>> +#ifdef __cplusplus >>>> +extern "C" { >>>> +#endif >>>> + >>>> +#include "ffconf.h" /* FatFs configuration options */ >>>> + >>>> +#if FF_DEFINED != FFCONF_DEF >>>> +#error Wrong configuration file (ffconf.h). >>>> +#endif >>>> + >>>> + >>>> +/* Integer types used for FatFs API */ >>>> + >>>> +#if defined(_WIN32) /* Main development platform */ >>>> +#define FF_INTDEF 2 >>>> +#include <windows.h> >>>> +typedef unsigned __int64 QWORD; >>>> +#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || >>>> defined(__cplusplus) /* C99 or later */ >>>> +#define FF_INTDEF 2 >>>> +#include <stdint.h> >>>> +typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ >>>> +typedef unsigned char BYTE; /* char must be 8-bit */ >>>> +typedef uint16_t WORD; /* 16-bit unsigned integer */ >>>> +typedef uint32_t DWORD; /* 32-bit unsigned integer */ >>>> +typedef uint64_t QWORD; /* 64-bit unsigned integer */ >>>> +typedef WORD WCHAR; /* UTF-16 character type */ >>>> +#else /* Earlier than C99 */ >>>> +#define FF_INTDEF 1 >>>> +typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ >>>> +typedef unsigned char BYTE; /* char must be 8-bit */ >>>> +typedef unsigned short WORD; /* 16-bit unsigned integer */ >>>> +typedef unsigned long DWORD; /* 32-bit unsigned integer */ >>>> +typedef WORD WCHAR; /* UTF-16 character type */ >>>> +#endif >>>> + >>>> + >>>> +/* Definitions of volume management */ >>>> + >>>> +#if FF_MULTI_PARTITION /* Multiple partition configuration */ >>>> +typedef struct { >>>> + BYTE pd; /* Physical drive number */ >>>> + BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ >>>> +} PARTITION; >>>> +extern PARTITION VolToPart[]; /* Volume - Partition mapping >>>> table */ >>>> +#endif >>>> + >>>> +#if FF_STR_VOLUME_ID >>>> +#ifndef FF_VOLUME_STRS >>>> +extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume >>>> ID */ >>>> +#endif >>>> +#endif >>>> + >>>> + >>>> + >>>> +/* Type of path name strings on FatFs API */ >>>> + >>>> +#ifndef _INC_TCHAR >>>> +#define _INC_TCHAR >>>> + >>>> +#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 >>>> encoding */ >>>> +typedef WCHAR TCHAR; >>>> +#define _T(x) L ## x >>>> +#define _TEXT(x) L ## x >>>> +#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 >>>> encoding */ >>>> +typedef char TCHAR; >>>> +#define _T(x) u8 ## x >>>> +#define _TEXT(x) u8 ## x >>>> +#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 >>>> encoding */ >>>> +typedef DWORD TCHAR; >>>> +#define _T(x) U ## x >>>> +#define _TEXT(x) U ## x >>>> +#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3) >>>> +#error Wrong FF_LFN_UNICODE setting >>>> +#else /* ANSI/OEM code in >>>> SBCS/DBCS */ >>>> +typedef char TCHAR; >>>> +#define _T(x) x >>>> +#define _TEXT(x) x >>>> +#endif >>>> + >>>> +#endif >>>> + >>>> + >>>> + >>>> +/* Type of file size and LBA variables */ >>>> + >>>> +#if FF_FS_EXFAT >>>> +#if FF_INTDEF != 2 >>>> +#error exFAT feature wants C99 or later >>>> +#endif >>>> +typedef QWORD FSIZE_t; >>>> +#if FF_LBA64 >>>> +typedef QWORD LBA_t; >>>> +#else >>>> +typedef DWORD LBA_t; >>>> +#endif >>>> +#else >>>> +#if FF_LBA64 >>>> +#error exFAT needs to be enabled when enable 64-bit LBA >>>> +#endif >>>> +typedef DWORD FSIZE_t; >>>> +typedef DWORD LBA_t; >>>> +#endif >>>> + >>>> + >>>> + >>>> +/* Filesystem object structure (FATFS) */ >>>> + >>>> +typedef struct { >>>> + BYTE fs_type; /* Filesystem type (0:not mounted) */ >>>> + BYTE pdrv; /* Associated physical drive */ >>>> + BYTE n_fats; /* Number of FATs (1 or 2) */ >>>> + BYTE wflag; /* win[] flag (b0:dirty) */ >>>> + BYTE fsi_flag; /* FSINFO flags (b7:disabled, >>>> b0:dirty) */ >>>> + WORD id; /* Volume mount ID */ >>>> + WORD n_rootdir; /* Number of root directory entries >>>> (FAT12/16) */ >>>> + WORD csize; /* Cluster size [sectors] */ >>>> +#if FF_MAX_SS != FF_MIN_SS >>>> + WORD ssize; /* Sector size (512, 1024, 2048 or >>>> 4096) */ >>>> +#endif >>>> +#if FF_USE_LFN >>>> + WCHAR* lfnbuf; /* LFN working buffer */ >>>> +#endif >>>> +#if FF_FS_EXFAT >>>> + BYTE* dirbuf; /* Directory entry block scratchpad >>>> buffer for exFAT */ >>>> +#endif >>>> +#if FF_FS_REENTRANT >>>> + FF_SYNC_t sobj; /* Identifier of sync object */ >>>> +#endif >>>> +#if !FF_FS_READONLY >>>> + DWORD last_clst; /* Last allocated cluster */ >>>> + DWORD free_clst; /* Number of free clusters */ >>>> +#endif >>>> +#if FF_FS_RPATH >>>> + DWORD cdir; /* Current directory start cluster >>>> (0:root) */ >>>> +#if FF_FS_EXFAT >>>> + DWORD cdc_scl; /* Containing directory start cluster >>>> (invalid when cdir is 0) */ >>>> + DWORD cdc_size; /* b31-b8:Size of containing >>>> directory, b7-b0: Chain status */ >>>> + DWORD cdc_ofs; /* Offset in the containing directory >>>> (invalid when cdir is 0) */ >>>> +#endif >>>> +#endif >>>> + DWORD n_fatent; /* Number of FAT entries (number of >>>> clusters + 2) */ >>>> + DWORD fsize; /* Size of an FAT [sectors] */ >>>> + LBA_t volbase; /* Volume base sector */ >>>> + LBA_t fatbase; /* FAT base sector */ >>>> + LBA_t dirbase; /* Root directory base sector/cluster */ >>>> + LBA_t database; /* Data base sector */ >>>> +#if FF_FS_EXFAT >>>> + LBA_t bitbase; /* Allocation bitmap base sector */ >>>> +#endif >>>> + LBA_t winsect; /* Current sector appearing in the >>>> win[] */ >>>> + BYTE win[FF_MAX_SS]; /* Disk access window for Directory, >>>> FAT (and file data at tiny cfg) */ >>>> +} FATFS; >>>> + >>>> + >>>> + >>>> +/* Object ID and allocation information (FFOBJID) */ >>>> + >>>> +typedef struct { >>>> + FATFS* fs; /* Pointer to the hosting volume >>>> of this object */ >>>> + WORD id; /* Hosting volume mount ID */ >>>> + BYTE attr; /* Object attribute */ >>>> + BYTE stat; /* Object chain status (b1-0: =0:not >>>> contiguous, =2:contiguous, =3:fragmented in this session, >>>> b2:sub-directory stretched) */ >>>> + DWORD sclust; /* Object data start cluster (0:no >>>> cluster or root directory) */ >>>> + FSIZE_t objsize; /* Object size (valid when sclust != >>>> 0) */ >>>> +#if FF_FS_EXFAT >>>> + DWORD n_cont; /* Size of first fragment - 1 >>>> (valid when stat == 3) */ >>>> + DWORD n_frag; /* Size of last fragment needs to >>>> be written to FAT (valid when not zero) */ >>>> + DWORD c_scl; /* Containing directory start >>>> cluster (valid when sclust != 0) */ >>>> + DWORD c_size; /* b31-b8:Size of containing >>>> directory, b7-b0: Chain status (valid when c_scl != 0) */ >>>> + DWORD c_ofs; /* Offset in the containing >>>> directory (valid when file object and sclust != 0) */ >>>> +#endif >>>> +#if FF_FS_LOCK >>>> + UINT lockid; /* File lock ID origin from 1 (index >>>> of file semaphore table Files[]) */ >>>> +#endif >>>> +} FFOBJID; >>>> + >>>> + >>>> + >>>> +/* File object structure (FIL) */ >>>> + >>>> +typedef struct { >>>> + FFOBJID obj; /* Object identifier (must be the >>>> 1st member to detect invalid object pointer) */ >>>> + BYTE flag; /* File status flags */ >>>> + BYTE err; /* Abort flag (error code) */ >>>> + FSIZE_t fptr; /* File read/write pointer (Zeroed >>>> on file open) */ >>>> + DWORD clust; /* Current cluster of fpter (invalid >>>> when fptr is 0) */ >>>> + LBA_t sect; /* Sector number appearing in buf[] >>>> (0:invalid) */ >>>> +#if !FF_FS_READONLY >>>> + LBA_t dir_sect; /* Sector number containing the >>>> directory entry (not used at exFAT) */ >>>> + BYTE* dir_ptr; /* Pointer to the directory entry in >>>> the win[] (not used at exFAT) */ >>>> +#endif >>>> +#if FF_USE_FASTSEEK >>>> + DWORD* cltbl; /* Pointer to the cluster link map >>>> table (nulled on open, set by application) */ >>>> +#endif >>>> +#if !FF_FS_TINY >>>> + BYTE buf[FF_MAX_SS]; /* File private data read/write >>>> window */ >>>> +#endif >>>> +} FIL; >>>> + >>>> + >>>> + >>>> +/* Directory object structure (DIR) */ >>>> + >>>> +typedef struct { >>>> + FFOBJID obj; /* Object identifier */ >>>> + DWORD dptr; /* Current read/write offset */ >>>> + DWORD clust; /* Current cluster */ >>>> + LBA_t sect; /* Current sector (0:Read operation >>>> has terminated) */ >>>> + BYTE* dir; /* Pointer to the directory item in >>>> the win[] */ >>>> + BYTE fn[12]; /* SFN (in/out) >>>> {body[8],ext[3],status[1]} */ >>>> +#if FF_USE_LFN >>>> + DWORD blk_ofs; /* Offset of current entry block being >>>> processed (0xFFFFFFFF:Invalid) */ >>>> +#endif >>>> +#if FF_USE_FIND >>>> + const TCHAR* pat; /* Pointer to the name matching >>>> pattern */ >>>> +#endif >>>> +} DIR; >>>> + >>>> + >>>> + >>>> +/* File information structure (FILINFO) */ >>>> + >>>> +typedef struct { >>>> + FSIZE_t fsize; /* File size */ >>>> + WORD fdate; /* Modified date */ >>>> + WORD ftime; /* Modified time */ >>>> + BYTE fattrib; /* File attribute */ >>>> +#if FF_USE_LFN >>>> + TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */ >>>> + TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */ >>>> +#else >>>> + TCHAR fname[12 + 1]; /* File name */ >>>> +#endif >>>> +} FILINFO; >>>> + >>>> + >>>> + >>>> +/* Format parameter structure (MKFS_PARM) */ >>>> + >>>> +typedef struct { >>>> + BYTE fmt; /* Format option (FM_FAT, FM_FAT32, >>>> FM_EXFAT and FM_SFD) */ >>>> + BYTE n_fat; /* Number of FATs */ >>>> + UINT align; /* Data area alignment (sector) */ >>>> + UINT n_root; /* Number of root directory entries */ >>>> + DWORD au_size; /* Cluster size (byte) */ >>>> +} MKFS_PARM; >>>> + >>>> + >>>> + >>>> +/* File function return code (FRESULT) */ >>>> + >>>> +typedef enum { >>>> + FR_OK = 0, /* (0) Succeeded */ >>>> + FR_DISK_ERR, /* (1) A hard error occurred in the low >>>> level disk I/O layer */ >>>> + FR_INT_ERR, /* (2) Assertion failed */ >>>> + FR_NOT_READY, /* (3) The physical drive cannot work */ >>>> + FR_NO_FILE, /* (4) Could not find the file */ >>>> + FR_NO_PATH, /* (5) Could not find the path */ >>>> + FR_INVALID_NAME, /* (6) The path name format is invalid */ >>>> + FR_DENIED, /* (7) Access denied due to >>>> prohibited access or directory full */ >>>> + FR_EXIST, /* (8) Access denied due to prohibited >>>> access */ >>>> + FR_INVALID_OBJECT, /* (9) The file/directory object is >>>> invalid */ >>>> + FR_WRITE_PROTECTED, /* (10) The physical drive is write >>>> protected */ >>>> + FR_INVALID_DRIVE, /* (11) The logical drive number is >>>> invalid */ >>>> + FR_NOT_ENABLED, /* (12) The volume has no work area */ >>>> + FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ >>>> + FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any >>>> problem */ >>>> + FR_TIMEOUT, /* (15) Could not get a grant to >>>> access the volume within defined period */ >>>> + FR_LOCKED, /* (16) The operation is rejected >>>> according to the file sharing policy */ >>>> + FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not >>>> be allocated */ >>>> + FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > >>>> FF_FS_LOCK */ >>>> + FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ >>>> +} FRESULT; >>>> + >>>> + >>>> + >>>> +/*--------------------------------------------------------------*/ >>>> +/* FatFs module application interface */ >>>> + >>>> +FRESULT f_open (FIL* fp, const TCHAR* path, BYTE >>>> mode); /* Open or create a file */ >>>> +FRESULT f_close (FIL* fp); /* Close an open file object */ >>>> +FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read >>>> data from the file */ >>>> +FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* >>>> Write data to the file */ >>>> +FRESULT f_lseek (FIL* fp, FSIZE_t >>>> ofs); /* Move file pointer of the >>>> file object */ >>>> +FRESULT f_truncate (FIL* fp); /* Truncate the file */ >>>> +FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */ >>>> +FRESULT f_opendir (DIR* dp, const TCHAR* >>>> path); /* Open a directory */ >>>> +FRESULT f_closedir (DIR* dp); /* Close an open directory */ >>>> +FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */ >>>> +FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, >>>> const TCHAR* pattern); /* Find first file */ >>>> +FRESULT f_findnext (DIR* dp, FILINFO* >>>> fno); /* Find next file */ >>>> +FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */ >>>> +FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or >>>> directory */ >>>> +FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* >>>> Rename/Move a file or directory */ >>>> +FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file >>>> status */ >>>> +FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* >>>> Change attribute of a file/dir */ >>>> +FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change >>>> timestamp of a file/dir */ >>>> +FRESULT f_chdir (const TCHAR* path); /* Change current directory */ >>>> +FRESULT f_chdrive (const TCHAR* path); /* Change current drive */ >>>> +FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */ >>>> +FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** >>>> fatfs); /* Get number of free clusters on the drive */ >>>> +FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); >>>> /* Get volume label */ >>>> +FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ >>>> +FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT >>>> btf, UINT* bf); /* Forward data to the stream */ >>>> +FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a >>>> contiguous block to the file */ >>>> +FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* >>>> Mount/Unmount a logical drive */ >>>> +FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* >>>> work, UINT len); /* Create a FAT volume */ >>>> +FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* >>>> Divide a physical drive into some partitions */ >>>> +FRESULT f_setcp (WORD cp); /* Set current code page */ >>>> +int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */ >>>> +int f_puts (const TCHAR* str, FIL* >>>> cp); /* Put a string to the file */ >>>> +int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted >>>> string to the file */ >>>> +TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from >>>> the file */ >>>> + >>>> +#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) >>>> +#define f_error(fp) ((fp)->err) >>>> +#define f_tell(fp) ((fp)->fptr) >>>> +#define f_size(fp) ((fp)->obj.objsize) >>>> +#define f_rewind(fp) f_lseek((fp), 0) >>>> +#define f_rewinddir(dp) f_readdir((dp), 0) >>>> +#define f_rmdir(path) f_unlink(path) >>>> +#define f_unmount(path) f_mount(0, path, 0) >>>> + >>>> + >>>> + >>>> + >>>> +/*--------------------------------------------------------------*/ >>>> +/* Additional user defined functions */ >>>> + >>>> +/* RTC function */ >>>> +#if !FF_FS_READONLY && !FF_FS_NORTC >>>> +DWORD get_fattime (void); >>>> +#endif >>>> + >>>> +/* LFN support functions */ >>>> +#if FF_USE_LFN >= 1 /* Code conversion >>>> (defined in unicode.c) */ >>>> +WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode >>>> conversion */ >>>> +WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code >>>> conversion */ >>>> +DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case >>>> conversion */ >>>> +#endif >>>> +#if FF_USE_LFN == 3 /* Dynamic memory >>>> allocation */ >>>> +void* ff_memalloc (UINT msize); /* Allocate memory block */ >>>> +void ff_memfree (void* mblock); /* Free memory block */ >>>> +#endif >>>> + >>>> +/* Sync functions */ >>>> +#if FF_FS_REENTRANT >>>> +int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync >>>> object */ >>>> +int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */ >>>> +void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */ >>>> +int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */ >>>> +#endif >>>> + >>>> +/* Function for assigning UNIX devices to FatFs library. */ >>>> +extern int fatfs_init(char *device); >>>> +extern void fatfs_release(void); >>>> + >>>> +/*--------------------------------------------------------------*/ >>>> +/* Flags and offset address */ >>>> + >>>> + >>>> +/* File access mode and open method flags (3rd argument of f_open) */ >>>> +#define FA_READ 0x01 >>>> +#define FA_WRITE 0x02 >>>> +#define FA_OPEN_EXISTING 0x00 >>>> +#define FA_CREATE_NEW 0x04 >>>> +#define FA_CREATE_ALWAYS 0x08 >>>> +#define FA_OPEN_ALWAYS 0x10 >>>> +#define FA_OPEN_APPEND 0x30 >>>> + >>>> +/* Fast seek controls (2nd argument of f_lseek) */ >>>> +#define CREATE_LINKMAP ((FSIZE_t)0 - 1) >>>> + >>>> +/* Format options (2nd argument of f_mkfs) */ >>>> +#define FM_FAT 0x01 >>>> +#define FM_FAT32 0x02 >>>> +#define FM_EXFAT 0x04 >>>> +#define FM_ANY 0x07 >>>> +#define FM_SFD 0x08 >>>> + >>>> +/* Filesystem type (FATFS.fs_type) */ >>>> +#define FS_FAT12 1 >>>> +#define FS_FAT16 2 >>>> +#define FS_FAT32 3 >>>> +#define FS_EXFAT 4 >>>> + >>>> +/* File attribute bits for directory entry (FILINFO.fattrib) */ >>>> +#define AM_RDO 0x01 /* Read only */ >>>> +#define AM_HID 0x02 /* Hidden */ >>>> +#define AM_SYS 0x04 /* System */ >>>> +#define AM_DIR 0x10 /* Directory */ >>>> +#define AM_ARC 0x20 /* Archive */ >>>> + >>>> + >>>> +#ifdef __cplusplus >>>> +} >>>> +#endif >>>> + >>>> +#endif /* FF_DEFINED */ >>>> diff --git a/fatfs/ffconf.h b/fatfs/ffconf.h >>>> new file mode 100644 >>>> index 0000000..b85bbb0 >>>> --- /dev/null >>>> +++ b/fatfs/ffconf.h >>>> @@ -0,0 +1,317 @@ >>>> +/*---------------------------------------------------------------------------/ >>>> >>>> +/ FatFs Functional Configurations >>>> +/----------------------------------------------------------------------------/ >>>> >>>> +/ >>>> +/ Copyright (C) 2019, ChaN, all right reserved. >>>> +/ >>>> +/ FatFs module is an open source software. Redistribution and use >>>> of FatFs in >>>> +/ source and binary forms, with or without modification, are >>>> permitted provided >>>> +/ that the following condition is met: >>>> +/ >>>> +/ 1. Redistributions of source code must retain the above copyright >>>> notice, >>>> +/ this condition and the following disclaimer. >>>> +/ >>>> +/ This software is provided by the copyright holder and >>>> contributors "AS IS" >>>> +/ and any warranties related to this software are DISCLAIMED. >>>> +/ The copyright owner or contributors be NOT LIABLE for any damages >>>> caused >>>> +/ by use of this software. >>>> +/ >>>> +/ SPDX-License-Identifier: BSD-1-Clause >>>> +/ >>>> +/---------------------------------------------------------------------------*/ >>>> >>>> + >>>> + >>>> +#define FFCONF_DEF 80196 /* Revision ID */ >>>> + >>>> +/*---------------------------------------------------------------------------/ >>>> >>>> +/ Function Configurations >>>> +/---------------------------------------------------------------------------*/ >>>> >>>> + >>>> +#define FF_FS_READONLY 0 >>>> +/* This option switches read-only configuration. (0:Read/Write or >>>> 1:Read-only) >>>> +/ Read-only configuration removes writing API functions, >>>> f_write(), f_sync(), >>>> +/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), >>>> f_getfree() >>>> +/ and optional writing functions as well. */ >>>> + >>>> + >>>> +#define FF_FS_MINIMIZE 3 >>>> +/* This option defines minimization level to remove some basic API >>>> functions. >>>> +/ >>>> +/ 0: Basic functions are fully enabled. >>>> +/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() >>>> and f_rename() >>>> +/ are removed. >>>> +/ 2: f_opendir(), f_readdir() and f_closedir() are removed in >>>> addition to 1. >>>> +/ 3: f_lseek() function is removed in addition to 2. */ >>>> + >>>> + >>>> +#define FF_USE_STRFUNC 0 >>>> +/* This option switches string functions, f_gets(), f_putc(), >>>> f_puts() and f_printf(). >>>> +/ >>>> +/ 0: Disable string functions. >>>> +/ 1: Enable without LF-CRLF conversion. >>>> +/ 2: Enable with LF-CRLF conversion. */ >>>> + >>>> + >>>> +#define FF_USE_FIND 0 >>>> +/* This option switches filtered directory read functions, >>>> f_findfirst() and >>>> +/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching >>>> altname[] too) */ >>>> + >>>> + >>>> +#define FF_USE_MKFS 1 >>>> +/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ >>>> + >>>> + >>>> +#define FF_USE_FASTSEEK 0 >>>> +/* This option switches fast seek function. (0:Disable or 1:Enable) */ >>>> + >>>> + >>>> +#define FF_USE_EXPAND 0 >>>> +/* This option switches f_expand function. (0:Disable or 1:Enable) */ >>>> + >>>> + >>>> +#define FF_USE_CHMOD 0 >>>> +/* This option switches attribute manipulation functions, f_chmod() >>>> and f_utime(). >>>> +/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to >>>> enable this option. */ >>>> + >>>> + >>>> +#define FF_USE_LABEL 0 >>>> +/* This option switches volume label functions, f_getlabel() and >>>> f_setlabel(). >>>> +/ (0:Disable or 1:Enable) */ >>>> + >>>> + >>>> +#define FF_USE_FORWARD 0 >>>> +/* This option switches f_forward() function. (0:Disable or >>>> 1:Enable) */ >>>> + >>>> + >>>> +/*---------------------------------------------------------------------------/ >>>> >>>> +/ Locale and Namespace Configurations >>>> +/---------------------------------------------------------------------------*/ >>>> >>>> + >>>> +#define FF_CODE_PAGE 932 >>>> +/* This option specifies the OEM code page to be used on the target >>>> system. >>>> +/ Incorrect code page setting can cause a file open failure. >>>> +/ >>>> +/ 437 - U.S. >>>> +/ 720 - Arabic >>>> +/ 737 - Greek >>>> +/ 771 - KBL >>>> +/ 775 - Baltic >>>> +/ 850 - Latin 1 >>>> +/ 852 - Latin 2 >>>> +/ 855 - Cyrillic >>>> +/ 857 - Turkish >>>> +/ 860 - Portuguese >>>> +/ 861 - Icelandic >>>> +/ 862 - Hebrew >>>> +/ 863 - Canadian French >>>> +/ 864 - Arabic >>>> +/ 865 - Nordic >>>> +/ 866 - Russian >>>> +/ 869 - Greek 2 >>>> +/ 932 - Japanese (DBCS) >>>> +/ 936 - Simplified Chinese (DBCS) >>>> +/ 949 - Korean (DBCS) >>>> +/ 950 - Traditional Chinese (DBCS) >>>> +/ 0 - Include all code pages above and configured by f_setcp() >>>> +*/ >>>> + >>>> + >>>> +#define FF_USE_LFN 0 >>>> +#define FF_MAX_LFN 255 >>>> +/* The FF_USE_LFN switches the support for LFN (long file name). >>>> +/ >>>> +/ 0: Disable LFN. FF_MAX_LFN has no effect. >>>> +/ 1: Enable LFN with static working buffer on the BSS. Always >>>> NOT thread-safe. >>>> +/ 2: Enable LFN with dynamic working buffer on the STACK. >>>> +/ 3: Enable LFN with dynamic working buffer on the HEAP. >>>> +/ >>>> +/ To enable the LFN, ffunicode.c needs to be added to the project. >>>> The LFN function >>>> +/ requiers certain internal working buffer occupies (FF_MAX_LFN + >>>> 1) * 2 bytes and >>>> +/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled. >>>> +/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code >>>> unit and it can >>>> +/ be in range of 12 to 255. It is recommended to be set it 255 to >>>> fully support LFN >>>> +/ specification. >>>> +/ When use stack for the working buffer, take care on stack >>>> overflow. When use heap >>>> +/ memory for the working buffer, memory management functions, >>>> ff_memalloc() and >>>> +/ ff_memfree() exemplified in ffsystem.c, need to be added to the >>>> project. */ >>>> + >>>> + >>>> +#define FF_LFN_UNICODE 0 >>>> +/* This option switches the character encoding on the API when LFN >>>> is enabled. >>>> +/ >>>> +/ 0: ANSI/OEM in current CP (TCHAR = char) >>>> +/ 1: Unicode in UTF-16 (TCHAR = WCHAR) >>>> +/ 2: Unicode in UTF-8 (TCHAR = char) >>>> +/ 3: Unicode in UTF-32 (TCHAR = DWORD) >>>> +/ >>>> +/ Also behavior of string I/O functions will be affected by this >>>> option. >>>> +/ When LFN is not enabled, this option has no effect. */ >>>> + >>>> + >>>> +#define FF_LFN_BUF 255 >>>> +#define FF_SFN_BUF 12 >>>> +/* This set of options defines size of file name members in the >>>> FILINFO structure >>>> +/ which is used to read out directory items. These values should >>>> be suffcient for >>>> +/ the file names to read. The maximum possible length of the read >>>> file name depends >>>> +/ on character encoding. When LFN is not enabled, these options >>>> have no effect. */ >>>> + >>>> + >>>> +#define FF_STRF_ENCODE 3 >>>> +/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, >>>> f_gets(), >>>> +/ f_putc(), f_puts and f_printf() convert the character encoding >>>> in it. >>>> +/ This option selects assumption of character encoding ON THE FILE >>>> to be >>>> +/ read/written via those functions. >>>> +/ >>>> +/ 0: ANSI/OEM in current CP >>>> +/ 1: Unicode in UTF-16LE >>>> +/ 2: Unicode in UTF-16BE >>>> +/ 3: Unicode in UTF-8 >>>> +*/ >>>> + >>>> + >>>> +#define FF_FS_RPATH 0 >>>> +/* This option configures support for relative path. >>>> +/ >>>> +/ 0: Disable relative path and remove related functions. >>>> +/ 1: Enable relative path. f_chdir() and f_chdrive() are available. >>>> +/ 2: f_getcwd() function is available in addition to 1. >>>> +*/ >>>> + >>>> + >>>> +/*---------------------------------------------------------------------------/ >>>> >>>> +/ Drive/Volume Configurations >>>> +/---------------------------------------------------------------------------*/ >>>> >>>> + >>>> +#define FF_VOLUMES 1 >>>> +/* Number of volumes (logical drives) to be used. (1-10) */ >>>> + >>>> + >>>> +#define FF_STR_VOLUME_ID 0 >>>> +#define FF_VOLUME_STRS >>>> "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" >>>> +/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary >>>> strings. >>>> +/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be >>>> used as drive >>>> +/ number in the path name. FF_VOLUME_STRS defines the volume ID >>>> strings for each >>>> +/ logical drives. Number of items must not be less than >>>> FF_VOLUMES. Valid >>>> +/ characters for the volume ID strings are A-Z, a-z and 0-9, >>>> however, they are >>>> +/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and >>>> FF_VOLUME_STRS is >>>> +/ not defined, a user defined volume string table needs to be >>>> defined as: >>>> +/ >>>> +/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",... >>>> +*/ >>>> + >>>> + >>>> +#define FF_MULTI_PARTITION 0 >>>> +/* This option switches support for multiple volumes on the >>>> physical drive. >>>> +/ By default (0), each logical drive number is bound to the same >>>> physical drive >>>> +/ number and only an FAT volume found on the physical drive will >>>> be mounted. >>>> +/ When this function is enabled (1), each logical drive number can >>>> be bound to >>>> +/ arbitrary physical drive and partition listed in the >>>> VolToPart[]. Also f_fdisk() >>>> +/ funciton will be available. */ >>>> + >>>> + >>>> +#define FF_MIN_SS 512 >>>> +#define FF_MAX_SS 512 >>>> +/* This set of options configures the range of sector size to be >>>> supported. (512, >>>> +/ 1024, 2048 or 4096) Always set both 512 for most systems, >>>> generic memory card and >>>> +/ harddisk. But a larger value may be required for on-board flash >>>> memory and some >>>> +/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, >>>> FatFs is configured >>>> +/ for variable sector size mode and disk_ioctl() function needs to >>>> implement >>>> +/ GET_SECTOR_SIZE command. */ >>>> + >>>> + >>>> +#define FF_LBA64 0 >>>> +/* This option switches support for 64-bit LBA. (0:Disable or >>>> 1:Enable) >>>> +/ To enable the 64-bit LBA, also exFAT needs to be enabled. >>>> (FF_FS_EXFAT == 1) */ >>>> + >>>> + >>>> +#define FF_MIN_GPT 0x10000000 >>>> +/* Minimum number of sectors to switch GPT as partitioning format >>>> in f_mkfs and >>>> +/ f_fdisk function. 0x100000000 max. This option has no effect >>>> when FF_LBA64 == 0. */ >>>> + >>>> + >>>> +#define FF_USE_TRIM 0 >>>> +/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable) >>>> +/ To enable Trim function, also CTRL_TRIM command should be >>>> implemented to the >>>> +/ disk_ioctl() function. */ >>>> + >>>> + >>>> + >>>> +/*---------------------------------------------------------------------------/ >>>> >>>> +/ System Configurations >>>> +/---------------------------------------------------------------------------*/ >>>> >>>> + >>>> +#define FF_FS_TINY 0 >>>> +/* This option switches tiny buffer configuration. (0:Normal or >>>> 1:Tiny) >>>> +/ At the tiny configuration, size of file object (FIL) is shrinked >>>> FF_MAX_SS bytes. >>>> +/ Instead of private sector buffer eliminated from the file >>>> object, common sector >>>> +/ buffer in the filesystem object (FATFS) is used for the file >>>> data transfer. */ >>>> + >>>> + >>>> +#define FF_FS_EXFAT 0 >>>> +/* This option switches support for exFAT filesystem. (0:Disable or >>>> 1:Enable) >>>> +/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1) >>>> +/ Note that enabling exFAT discards ANSI C (C89) compatibility. */ >>>> + >>>> + >>>> +#define FF_FS_NORTC 0 >>>> +#define FF_NORTC_MON 1 >>>> +#define FF_NORTC_MDAY 1 >>>> +#define FF_NORTC_YEAR 2020 >>>> +/* The option FF_FS_NORTC switches timestamp functiton. If the >>>> system does not have >>>> +/ any RTC function or valid timestamp is not needed, set >>>> FF_FS_NORTC = 1 to disable >>>> +/ the timestamp function. Every object modified by FatFs will have >>>> a fixed timestamp >>>> +/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in >>>> local time. >>>> +/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() >>>> function need to be >>>> +/ added to the project to read current time form real-time clock. >>>> FF_NORTC_MON, >>>> +/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect. >>>> +/ These options have no effect in read-only configuration >>>> (FF_FS_READONLY = 1). */ >>>> + >>>> + >>>> +#define FF_FS_NOFSINFO 0 >>>> +/* If you need to know correct free space on the FAT32 volume, set >>>> bit 0 of this >>>> +/ option, and f_getfree() function at first time after volume >>>> mount will force >>>> +/ a full FAT scan. Bit 1 controls the use of last allocated >>>> cluster number. >>>> +/ >>>> +/ bit0=0: Use free cluster count in the FSINFO if available. >>>> +/ bit0=1: Do not trust free cluster count in the FSINFO. >>>> +/ bit1=0: Use last allocated cluster number in the FSINFO if >>>> available. >>>> +/ bit1=1: Do not trust last allocated cluster number in the FSINFO. >>>> +*/ >>>> + >>>> + >>>> +#define FF_FS_LOCK 0 >>>> +/* The option FF_FS_LOCK switches file lock function to control >>>> duplicated file open >>>> +/ and illegal operation to open objects. This option must be 0 >>>> when FF_FS_READONLY >>>> +/ is 1. >>>> +/ >>>> +/ 0: Disable file lock function. To avoid volume corruption, >>>> application program >>>> +/ should avoid illegal open, remove and rename to the open >>>> objects. >>>> +/ >0: Enable file lock function. The value defines how many >>>> files/sub-directories >>>> +/ can be opened simultaneously under file lock control. Note >>>> that the file >>>> +/ lock control is independent of re-entrancy. */ >>>> + >>>> + >>>> +/* #include <somertos.h> // O/S definitions */ >>>> +#define FF_FS_REENTRANT 0 >>>> +#define FF_FS_TIMEOUT 1000 >>>> +#define FF_SYNC_t HANDLE >>>> +/* The option FF_FS_REENTRANT switches the re-entrancy (thread >>>> safe) of the FatFs >>>> +/ module itself. Note that regardless of this option, file access >>>> to different >>>> +/ volume is always re-entrant and volume control functions, >>>> f_mount(), f_mkfs() >>>> +/ and f_fdisk() function, are always not re-entrant. Only >>>> file/directory access >>>> +/ to the same volume is under control of this function. >>>> +/ >>>> +/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no >>>> effect. >>>> +/ 1: Enable re-entrancy. Also user provided synchronization >>>> handlers, >>>> +/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and >>>> ff_cre_syncobj() >>>> +/ function, must be added to the project. Samples are >>>> available in >>>> +/ option/syscall.c. >>>> +/ >>>> +/ The FF_FS_TIMEOUT defines timeout period in unit of time tick. >>>> +/ The FF_SYNC_t defines O/S dependent sync object type. e.g. >>>> HANDLE, ID, OS_EVENT*, >>>> +/ SemaphoreHandle_t and etc. A header file for O/S definitions >>>> needs to be >>>> +/ included somewhere in the scope of ff.h. */ >>>> + >>>> + >>>> + >>>> +/*--- End of configuration options ---*/ >>>> >>> >>> >> >>
diff --git a/fatfs/diskio.c b/fatfs/diskio.c new file mode 100644 index 0000000..1976add --- /dev/null +++ b/fatfs/diskio.c @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2021 Weidmueller Interface GmbH & Co. KG + * Roland Gaudig <roland.gaudig@weidmueller.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <errno.h> +#include <fcntl.h> +#include <time.h> +#include <unistd.h> +#include <linux/fs.h> +#include <sys/ioctl.h> + +#include "swupdate.h" +#include "util.h" + +#include "ff.h" +#include "diskio.h" + + +#define SECTOR_SIZE 512 + +static int file_descriptor; +static char device_name[MAX_VOLNAME]; +static bool init_status; + +int fatfs_init(char *device); +void fatfs_release(void); + + +int fatfs_init(char *device) +{ + if (strnlen(device_name, MAX_VOLNAME)) { + ERROR("Called fatfs_init second time without fatfs_release"); + return -1; + } + + strncpy(device_name, device, sizeof(device_name)); + file_descriptor = open(device_name, O_RDWR); + + if (file_descriptor < 0) { + ERROR("Device %s cannot be opened: %s", device_name, strerror(errno)); + return -ENODEV; + } + + return 0; +} + +void fatfs_release(void) +{ + (void)close(file_descriptor); + memset(device_name, 0, MAX_VOLNAME); + init_status = false; +} + +DSTATUS disk_status(BYTE pdrv) +{ + DSTATUS status = 0; + (void)pdrv; + + if (!strnlen(device_name, MAX_VOLNAME)) + status |= STA_NODISK; + + if (!init_status) + status |= STA_NOINIT; + + return status; +} + +DSTATUS disk_initialize(BYTE pdrv) +{ + init_status = true; + + return disk_status(pdrv); +} + +DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) +{ + (void)pdrv; + + if (!buff) + return RES_PARERR; + + if (disk_status(pdrv)) + return RES_NOTRDY; + + if (pread(file_descriptor, buff, count * SECTOR_SIZE, sector * SECTOR_SIZE) != count * SECTOR_SIZE) + return RES_ERROR; + + return RES_OK; +} + +#if FF_FS_READONLY == 0 +DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) +{ + (void)pdrv; + + if (!buff) + return RES_PARERR; + + if (disk_status(pdrv)) + return RES_NOTRDY; + + if (pwrite(file_descriptor, buff, count * SECTOR_SIZE, sector * SECTOR_SIZE) != count * SECTOR_SIZE) + return RES_ERROR; + + return RES_OK; +} +#endif + +DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) +{ + (void) pdrv; + + if (disk_status(pdrv)) + return RES_NOTRDY; + + switch (cmd) { + case CTRL_SYNC: + if (syncfs(file_descriptor) != 0) + return RES_ERROR; + break; + case GET_SECTOR_COUNT: + { + off_t size = lseek(file_descriptor, 0, SEEK_END) / SECTOR_SIZE; + + if (!buff) + return RES_PARERR; + + *(LBA_t*)buff = size; + break; + } + case GET_SECTOR_SIZE: + if (!buff) + return RES_PARERR; + + *(WORD*)buff = SECTOR_SIZE; + break; + case GET_BLOCK_SIZE: + /* Get erase block size of flash memories, return 1 if not a + * Flash memory or unknown. + */ + if (!buff) + return RES_PARERR; + + *(WORD*)buff = 1; + break; + default: + ERROR("cmd %d not implemented", cmd); + return RES_PARERR; + break; + } + + return RES_OK; +} + +DWORD get_fattime(void) +{ + time_t unix_time = time(NULL); + struct tm *t = gmtime(&unix_time); + + /* FatFs times are based on year 1980 */ + DWORD tdos = ((t->tm_year - 80) & 0x3F) << 25; + /* FatFs months start with 1 */ + tdos |= (t->tm_mon + 1) << 21; + tdos |= t->tm_mday << 16; + tdos |= t->tm_hour << 11; + tdos |= t->tm_min << 5; + /* Don't know how FatFs copes with leap seconds, therefore limit them */ + tdos |= (t->tm_sec % 60) / 2; + + return tdos; +} diff --git a/fatfs/diskio.h b/fatfs/diskio.h new file mode 100644 index 0000000..1f019a1 --- /dev/null +++ b/fatfs/diskio.h @@ -0,0 +1,96 @@ +/*-----------------------------------------------------------------------/ +/ Low level disk interface modlue include file (C)ChaN, 2019 / +/------------------------------------------------------------------------/ +/ +/ Copyright (C) 2019, ChaN, all right reserved. +/ +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: +/ +/ 1. Redistributions of source code must retain the above copyright notice, +/ this condition and the following disclaimer. +/ +/ This software is provided by the copyright holder and contributors "AS IS" +/ and any warranties related to this software are DISCLAIMED. +/ The copyright owner or contributors be NOT LIABLE for any damages caused +/ by use of this software. +/ +/ SPDX-License-Identifier: BSD-1-Clause +/ +/-----------------------------------------------------------------------*/ + + +#ifndef _DISKIO_DEFINED +#define _DISKIO_DEFINED + +#ifdef __cplusplus +extern "C" { +#endif + +/* Status of Disk Functions */ +typedef BYTE DSTATUS; + +/* Results of Disk Functions */ +typedef enum { + RES_OK = 0, /* 0: Successful */ + RES_ERROR, /* 1: R/W Error */ + RES_WRPRT, /* 2: Write Protected */ + RES_NOTRDY, /* 3: Not Ready */ + RES_PARERR /* 4: Invalid Parameter */ +} DRESULT; + + +/*---------------------------------------*/ +/* Prototypes for disk control functions */ + + +DSTATUS disk_initialize (BYTE pdrv); +DSTATUS disk_status (BYTE pdrv); +DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count); +DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count); +DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); + + +/* Disk Status Bits (DSTATUS) */ + +#define STA_NOINIT 0x01 /* Drive not initialized */ +#define STA_NODISK 0x02 /* No medium in the drive */ +#define STA_PROTECT 0x04 /* Write protected */ + + +/* Command code for disk_ioctrl fucntion */ + +/* Generic command (Used by FatFs) */ +#define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */ +#define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */ +#define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */ +#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */ +#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */ + +/* Generic command (Not used by FatFs) */ +#define CTRL_POWER 5 /* Get/Set power status */ +#define CTRL_LOCK 6 /* Lock/Unlock media removal */ +#define CTRL_EJECT 7 /* Eject media */ +#define CTRL_FORMAT 8 /* Create physical format on the media */ + +/* MMC/SDC specific ioctl command */ +#define MMC_GET_TYPE 10 /* Get card type */ +#define MMC_GET_CSD 11 /* Get CSD */ +#define MMC_GET_CID 12 /* Get CID */ +#define MMC_GET_OCR 13 /* Get OCR */ +#define MMC_GET_SDSTAT 14 /* Get SD status */ +#define ISDIO_READ 55 /* Read data form SD iSDIO register */ +#define ISDIO_WRITE 56 /* Write data to SD iSDIO register */ +#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */ + +/* ATA/CF specific ioctl command */ +#define ATA_GET_REV 20 /* Get F/W revision */ +#define ATA_GET_MODEL 21 /* Get model name */ +#define ATA_GET_SN 22 /* Get serial number */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/fatfs/ff.c b/fatfs/ff.c new file mode 100644 index 0000000..9281ec8 --- /dev/null +++ b/fatfs/ff.c @@ -0,0 +1,6875 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - Generic FAT Filesystem Module R0.14a / +/-----------------------------------------------------------------------------/ +/ +/ Copyright (C) 2020, ChaN, all right reserved. +/ +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: +/ +/ 1. Redistributions of source code must retain the above copyright notice, +/ this condition and the following disclaimer. +/ +/ This software is provided by the copyright holder and contributors "AS IS" +/ and any warranties related to this software are DISCLAIMED. +/ The copyright owner or contributors be NOT LIABLE for any damages caused +/ by use of this software. +/ +/ SPDX-License-Identifier: BSD-1-Clause +/ +/----------------------------------------------------------------------------*/ + + +#include "ff.h" /* Declarations of FatFs API */ +#include "diskio.h" /* Declarations of device I/O functions */ + + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + +#if FF_DEFINED != 80196 /* Revision ID */ +#error Wrong include file (ff.h). +#endif + + +/* Limits and boundaries */ +#define MAX_DIR 0x200000 /* Max size of FAT directory */ +#define MAX_DIR_EX 0x10000000 /* Max size of exFAT directory */ +#define MAX_FAT12 0xFF5 /* Max FAT12 clusters (differs from specs, but right for real DOS/Windows behavior) */ +#define MAX_FAT16 0xFFF5 /* Max FAT16 clusters (differs from specs, but right for real DOS/Windows behavior) */ +#define MAX_FAT32 0x0FFFFFF5 /* Max FAT32 clusters (not specified, practical limit) */ +#define MAX_EXFAT 0x7FFFFFFD /* Max exFAT clusters (differs from specs, implementation limit) */ + + +/* Character code support macros */ +#define IsUpper(c) ((c) >= 'A' && (c) <= 'Z') +#define IsLower(c) ((c) >= 'a' && (c) <= 'z') +#define IsDigit(c) ((c) >= '0' && (c) <= '9') +#define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF) +#define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF) +#define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF) + + +/* Additional file access control and file status flags for internal use */ +#define FA_SEEKEND 0x20 /* Seek to end of the file on file open */ +#define FA_MODIFIED 0x40 /* File has been modified */ +#define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */ + + +/* Additional file attribute bits for internal use */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_MASK 0x3F /* Mask of defined bits */ + + +/* Name status flags in fn[11] */ +#define NSFLAG 11 /* Index of the name status byte */ +#define NS_LOSS 0x01 /* Out of 8.3 format */ +#define NS_LFN 0x02 /* Force to create LFN entry */ +#define NS_LAST 0x04 /* Last segment */ +#define NS_BODY 0x08 /* Lower case flag (body) */ +#define NS_EXT 0x10 /* Lower case flag (ext) */ +#define NS_DOT 0x20 /* Dot entry */ +#define NS_NOLFN 0x40 /* Do not find LFN */ +#define NS_NONAME 0x80 /* Not followed */ + + +/* exFAT directory entry types */ +#define ET_BITMAP 0x81 /* Allocation bitmap */ +#define ET_UPCASE 0x82 /* Up-case table */ +#define ET_VLABEL 0x83 /* Volume label */ +#define ET_FILEDIR 0x85 /* File and directory */ +#define ET_STREAM 0xC0 /* Stream extension */ +#define ET_FILENAME 0xC1 /* Name extension */ + + +/* FatFs refers the FAT structure as simple byte array instead of structure member +/ because the C structure is not binary compatible between different platforms */ + +#define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */ +#define BS_OEMName 3 /* OEM name (8-byte) */ +#define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */ +#define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */ +#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */ +#define BPB_NumFATs 16 /* Number of FATs (BYTE) */ +#define BPB_RootEntCnt 17 /* Size of root directory area for FAT [entry] (WORD) */ +#define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */ +#define BPB_Media 21 /* Media descriptor byte (BYTE) */ +#define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */ +#define BPB_SecPerTrk 24 /* Number of sectors per track for int13h [sector] (WORD) */ +#define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */ +#define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */ +#define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */ +#define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */ +#define BS_NTres 37 /* WindowsNT error flag (BYTE) */ +#define BS_BootSig 38 /* Extended boot signature (BYTE) */ +#define BS_VolID 39 /* Volume serial number (DWORD) */ +#define BS_VolLab 43 /* Volume label string (8-byte) */ +#define BS_FilSysType 54 /* Filesystem type string (8-byte) */ +#define BS_BootCode 62 /* Boot code (448-byte) */ +#define BS_55AA 510 /* Signature word (WORD) */ + +#define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */ +#define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */ +#define BPB_FSVer32 42 /* FAT32: Filesystem version (WORD) */ +#define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */ +#define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */ +#define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */ +#define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */ +#define BS_NTres32 65 /* FAT32: Error flag (BYTE) */ +#define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */ +#define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */ +#define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */ +#define BS_FilSysType32 82 /* FAT32: Filesystem type string (8-byte) */ +#define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */ + +#define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */ +#define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */ +#define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */ +#define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */ +#define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */ +#define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */ +#define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */ +#define BPB_RootClusEx 96 /* exFAT: Root directory start cluster (DWORD) */ +#define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */ +#define BPB_FSVerEx 104 /* exFAT: Filesystem version (WORD) */ +#define BPB_VolFlagEx 106 /* exFAT: Volume flags (WORD) */ +#define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in unit of byte (BYTE) */ +#define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in unit of sector (BYTE) */ +#define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */ +#define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */ +#define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */ +#define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */ +#define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */ + +#define DIR_Name 0 /* Short file name (11-byte) */ +#define DIR_Attr 11 /* Attribute (BYTE) */ +#define DIR_NTres 12 /* Lower case flag (BYTE) */ +#define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */ +#define DIR_CrtTime 14 /* Created time (DWORD) */ +#define DIR_LstAccDate 18 /* Last accessed date (WORD) */ +#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */ +#define DIR_ModTime 22 /* Modified time (DWORD) */ +#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */ +#define DIR_FileSize 28 /* File size (DWORD) */ +#define LDIR_Ord 0 /* LFN: LFN order and LLE flag (BYTE) */ +#define LDIR_Attr 11 /* LFN: LFN attribute (BYTE) */ +#define LDIR_Type 12 /* LFN: Entry type (BYTE) */ +#define LDIR_Chksum 13 /* LFN: Checksum of the SFN (BYTE) */ +#define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD) */ +#define XDIR_Type 0 /* exFAT: Type of exFAT directory entry (BYTE) */ +#define XDIR_NumLabel 1 /* exFAT: Number of volume label characters (BYTE) */ +#define XDIR_Label 2 /* exFAT: Volume label (11-WORD) */ +#define XDIR_CaseSum 4 /* exFAT: Sum of case conversion table (DWORD) */ +#define XDIR_NumSec 1 /* exFAT: Number of secondary entries (BYTE) */ +#define XDIR_SetSum 2 /* exFAT: Sum of the set of directory entries (WORD) */ +#define XDIR_Attr 4 /* exFAT: File attribute (WORD) */ +#define XDIR_CrtTime 8 /* exFAT: Created time (DWORD) */ +#define XDIR_ModTime 12 /* exFAT: Modified time (DWORD) */ +#define XDIR_AccTime 16 /* exFAT: Last accessed time (DWORD) */ +#define XDIR_CrtTime10 20 /* exFAT: Created time subsecond (BYTE) */ +#define XDIR_ModTime10 21 /* exFAT: Modified time subsecond (BYTE) */ +#define XDIR_CrtTZ 22 /* exFAT: Created timezone (BYTE) */ +#define XDIR_ModTZ 23 /* exFAT: Modified timezone (BYTE) */ +#define XDIR_AccTZ 24 /* exFAT: Last accessed timezone (BYTE) */ +#define XDIR_GenFlags 33 /* exFAT: General secondary flags (BYTE) */ +#define XDIR_NumName 35 /* exFAT: Number of file name characters (BYTE) */ +#define XDIR_NameHash 36 /* exFAT: Hash of file name (WORD) */ +#define XDIR_ValidFileSize 40 /* exFAT: Valid file size (QWORD) */ +#define XDIR_FstClus 52 /* exFAT: First cluster of the file data (DWORD) */ +#define XDIR_FileSize 56 /* exFAT: File/Directory size (QWORD) */ + +#define SZDIRE 32 /* Size of a directory entry */ +#define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */ +#define RDDEM 0x05 /* Replacement of the character collides with DDEM */ +#define LLEF 0x40 /* Last long entry flag in LDIR_Ord */ + +#define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */ +#define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */ +#define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */ +#define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */ + +#define MBR_Table 446 /* MBR: Offset of partition table in the MBR */ +#define SZ_PTE 16 /* MBR: Size of a partition table entry */ +#define PTE_Boot 0 /* MBR PTE: Boot indicator */ +#define PTE_StHead 1 /* MBR PTE: Start head */ +#define PTE_StSec 2 /* MBR PTE: Start sector */ +#define PTE_StCyl 3 /* MBR PTE: Start cylinder */ +#define PTE_System 4 /* MBR PTE: System ID */ +#define PTE_EdHead 5 /* MBR PTE: End head */ +#define PTE_EdSec 6 /* MBR PTE: End sector */ +#define PTE_EdCyl 7 /* MBR PTE: End cylinder */ +#define PTE_StLba 8 /* MBR PTE: Start in LBA */ +#define PTE_SizLba 12 /* MBR PTE: Size in LBA */ + +#define GPTH_Sign 0 /* GPT: Header signature (8-byte) */ +#define GPTH_Rev 8 /* GPT: Revision (DWORD) */ +#define GPTH_Size 12 /* GPT: Header size (DWORD) */ +#define GPTH_Bcc 16 /* GPT: Header BCC (DWORD) */ +#define GPTH_CurLba 24 /* GPT: Main header LBA (QWORD) */ +#define GPTH_BakLba 32 /* GPT: Backup header LBA (QWORD) */ +#define GPTH_FstLba 40 /* GPT: First LBA for partitions (QWORD) */ +#define GPTH_LstLba 48 /* GPT: Last LBA for partitions (QWORD) */ +#define GPTH_DskGuid 56 /* GPT: Disk GUID (16-byte) */ +#define GPTH_PtOfs 72 /* GPT: Partation table LBA (QWORD) */ +#define GPTH_PtNum 80 /* GPT: Number of table entries (DWORD) */ +#define GPTH_PteSize 84 /* GPT: Size of table entry (DWORD) */ +#define GPTH_PtBcc 88 /* GPT: Partation table BCC (DWORD) */ +#define SZ_GPTE 128 /* GPT: Size of partition table entry */ +#define GPTE_PtGuid 0 /* GPT PTE: Partition type GUID (16-byte) */ +#define GPTE_UpGuid 16 /* GPT PTE: Partition unique GUID (16-byte) */ +#define GPTE_FstLba 32 /* GPT PTE: First LBA (QWORD) */ +#define GPTE_LstLba 40 /* GPT PTE: Last LBA inclusive (QWORD) */ +#define GPTE_Flags 48 /* GPT PTE: Flags (QWORD) */ +#define GPTE_Name 56 /* GPT PTE: Name */ + + +/* Post process on fatal error in the file operations */ +#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); } + + +/* Re-entrancy related */ +#if FF_FS_REENTRANT +#if FF_USE_LFN == 1 +#error Static LFN work area cannot be used at thread-safe configuration +#endif +#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } +#else +#define LEAVE_FF(fs, res) return res +#endif + + +/* Definitions of logical drive - physical location conversion */ +#if FF_MULTI_PARTITION +#define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number */ +#define LD2PT(vol) VolToPart[vol].pt /* Get partition index */ +#else +#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is associated with the same physical drive number */ +#define LD2PT(vol) 0 /* Find first valid partition or in SFD */ +#endif + + +/* Definitions of sector size */ +#if (FF_MAX_SS < FF_MIN_SS) || (FF_MAX_SS != 512 && FF_MAX_SS != 1024 && FF_MAX_SS != 2048 && FF_MAX_SS != 4096) || (FF_MIN_SS != 512 && FF_MIN_SS != 1024 && FF_MIN_SS != 2048 && FF_MIN_SS != 4096) +#error Wrong sector size configuration +#endif +#if FF_MAX_SS == FF_MIN_SS +#define SS(fs) ((UINT)FF_MAX_SS) /* Fixed sector size */ +#else +#define SS(fs) ((fs)->ssize) /* Variable sector size */ +#endif + + +/* Timestamp */ +#if FF_FS_NORTC == 1 +#if FF_NORTC_YEAR < 1980 || FF_NORTC_YEAR > 2107 || FF_NORTC_MON < 1 || FF_NORTC_MON > 12 || FF_NORTC_MDAY < 1 || FF_NORTC_MDAY > 31 +#error Invalid FF_FS_NORTC settings +#endif +#define GET_FATTIME() ((DWORD)(FF_NORTC_YEAR - 1980) << 25 | (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16) +#else +#define GET_FATTIME() get_fattime() +#endif + + +/* File lock controls */ +#if FF_FS_LOCK != 0 +#if FF_FS_READONLY +#error FF_FS_LOCK must be 0 at read-only configuration +#endif +typedef struct { + FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */ + DWORD clu; /* Object ID 2, containing directory (0:root) */ + DWORD ofs; /* Object ID 3, offset in the directory */ + WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */ +} FILESEM; +#endif + + +/* SBCS up-case tables (\x80-\xFF) */ +#define TBL_CT437 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT720 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT737 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT771 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF} +#define TBL_CT775 {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT850 {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \ + 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \ + 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT852 {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, \ + 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} +#define TBL_CT855 {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, \ + 0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ + 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ + 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, \ + 0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT857 {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT860 {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \ + 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT861 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT862 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT863 {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \ + 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \ + 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT864 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT865 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT866 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT869 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, \ + 0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \ + 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \ + 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF} + + +/* DBCS code range |----- 1st byte -----| |----------- 2nd byte -----------| */ +/* <------> <------> <------> <------> <------> */ +#define TBL_DC932 {0x81, 0x9F, 0xE0, 0xFC, 0x40, 0x7E, 0x80, 0xFC, 0x00, 0x00} +#define TBL_DC936 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0x80, 0xFE, 0x00, 0x00} +#define TBL_DC949 {0x81, 0xFE, 0x00, 0x00, 0x41, 0x5A, 0x61, 0x7A, 0x81, 0xFE} +#define TBL_DC950 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0xA1, 0xFE, 0x00, 0x00} + + +/* Macros for table definitions */ +#define MERGE_2STR(a, b) a ## b +#define MKCVTBL(hd, cp) MERGE_2STR(hd, cp) + + + + +/*-------------------------------------------------------------------------- + + Module Private Work Area + +---------------------------------------------------------------------------*/ +/* Remark: Variables defined here without initial value shall be guaranteed +/ zero/null at start-up. If not, the linker option or start-up routine is +/ not compliance with C standard. */ + +/*--------------------------------*/ +/* File/Volume controls */ +/*--------------------------------*/ + +#if FF_VOLUMES < 1 || FF_VOLUMES > 10 +#error Wrong FF_VOLUMES setting +#endif +static FATFS* FatFs[FF_VOLUMES]; /* Pointer to the filesystem objects (logical drives) */ +static WORD Fsid; /* Filesystem mount ID */ + +#if FF_FS_RPATH != 0 +static BYTE CurrVol; /* Current drive */ +#endif + +#if FF_FS_LOCK != 0 +static FILESEM Files[FF_FS_LOCK]; /* Open object lock semaphores */ +#endif + +#if FF_STR_VOLUME_ID +#ifdef FF_VOLUME_STRS +static const char* const VolumeStr[FF_VOLUMES] = {FF_VOLUME_STRS}; /* Pre-defined volume ID */ +#endif +#endif + +#if FF_LBA64 +#if FF_MIN_GPT > 0x100000000 +#error Wrong FF_MIN_GPT setting +#endif +static const BYTE GUID_MS_Basic[16] = {0xA2,0xA0,0xD0,0xEB,0xE5,0xB9,0x33,0x44,0x87,0xC0,0x68,0xB6,0xB7,0x26,0x99,0xC7}; +#endif + + + +/*--------------------------------*/ +/* LFN/Directory working buffer */ +/*--------------------------------*/ + +#if FF_USE_LFN == 0 /* Non-LFN configuration */ +#if FF_FS_EXFAT +#error LFN must be enabled when enable exFAT +#endif +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) +#define FREE_NAMBUF() +#define LEAVE_MKFS(res) return res + +#else /* LFN configurations */ +#if FF_MAX_LFN < 12 || FF_MAX_LFN > 255 +#error Wrong setting of FF_MAX_LFN +#endif +#if FF_LFN_BUF < FF_SFN_BUF || FF_SFN_BUF < 12 +#error Wrong setting of FF_LFN_BUF or FF_SFN_BUF +#endif +#if FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3 +#error Wrong setting of FF_LFN_UNICODE +#endif +static const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* FAT: Offset of LFN characters in the directory entry */ +#define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) /* exFAT: Size of directory entry block scratchpad buffer needed for the name length */ + +#if FF_USE_LFN == 1 /* LFN enabled with static working buffer */ +#if FF_FS_EXFAT +static BYTE DirBuf[MAXDIRB(FF_MAX_LFN)]; /* Directory entry block scratchpad buffer */ +#endif +static WCHAR LfnBuf[FF_MAX_LFN + 1]; /* LFN working buffer */ +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) +#define FREE_NAMBUF() +#define LEAVE_MKFS(res) return res + +#elif FF_USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */ +#if FF_FS_EXFAT +#define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; BYTE dbuf[MAXDIRB(FF_MAX_LFN)]; /* LFN working buffer and directory entry block scratchpad buffer */ +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; } +#define FREE_NAMBUF() +#else +#define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; /* LFN working buffer */ +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; } +#define FREE_NAMBUF() +#endif +#define LEAVE_MKFS(res) return res + +#elif FF_USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */ +#if FF_FS_EXFAT +#define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer and directory entry block scratchpad buffer */ +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2 + MAXDIRB(FF_MAX_LFN)); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+FF_MAX_LFN+1); } +#define FREE_NAMBUF() ff_memfree(lfn) +#else +#define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer */ +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; } +#define FREE_NAMBUF() ff_memfree(lfn) +#endif +#define LEAVE_MKFS(res) { if (!work) ff_memfree(buf); return res; } +#define MAX_MALLOC 0x8000 /* Must be >=FF_MAX_SS */ + +#else +#error Wrong setting of FF_USE_LFN + +#endif /* FF_USE_LFN == 1 */ +#endif /* FF_USE_LFN == 0 */ + + + +/*--------------------------------*/ +/* Code conversion tables */ +/*--------------------------------*/ + +#if FF_CODE_PAGE == 0 /* Run-time code page configuration */ +#define CODEPAGE CodePage +static WORD CodePage; /* Current code page */ +static const BYTE *ExCvt, *DbcTbl; /* Pointer to current SBCS up-case table and DBCS code range table below */ + +static const BYTE Ct437[] = TBL_CT437; +static const BYTE Ct720[] = TBL_CT720; +static const BYTE Ct737[] = TBL_CT737; +static const BYTE Ct771[] = TBL_CT771; +static const BYTE Ct775[] = TBL_CT775; +static const BYTE Ct850[] = TBL_CT850; +static const BYTE Ct852[] = TBL_CT852; +static const BYTE Ct855[] = TBL_CT855; +static const BYTE Ct857[] = TBL_CT857; +static const BYTE Ct860[] = TBL_CT860; +static const BYTE Ct861[] = TBL_CT861; +static const BYTE Ct862[] = TBL_CT862; +static const BYTE Ct863[] = TBL_CT863; +static const BYTE Ct864[] = TBL_CT864; +static const BYTE Ct865[] = TBL_CT865; +static const BYTE Ct866[] = TBL_CT866; +static const BYTE Ct869[] = TBL_CT869; +static const BYTE Dc932[] = TBL_DC932; +static const BYTE Dc936[] = TBL_DC936; +static const BYTE Dc949[] = TBL_DC949; +static const BYTE Dc950[] = TBL_DC950; + +#elif FF_CODE_PAGE < 900 /* Static code page configuration (SBCS) */ +#define CODEPAGE FF_CODE_PAGE +static const BYTE ExCvt[] = MKCVTBL(TBL_CT, FF_CODE_PAGE); + +#else /* Static code page configuration (DBCS) */ +#define CODEPAGE FF_CODE_PAGE +static const BYTE DbcTbl[] = MKCVTBL(TBL_DC, FF_CODE_PAGE); + +#endif + + + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* Load/Store multi-byte word in the FAT structure */ +/*-----------------------------------------------------------------------*/ + +static WORD ld_word (const BYTE* ptr) /* Load a 2-byte little-endian word */ +{ + WORD rv; + + rv = ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} + +static DWORD ld_dword (const BYTE* ptr) /* Load a 4-byte little-endian word */ +{ + DWORD rv; + + rv = ptr[3]; + rv = rv << 8 | ptr[2]; + rv = rv << 8 | ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} + +#if FF_FS_EXFAT +static QWORD ld_qword (const BYTE* ptr) /* Load an 8-byte little-endian word */ +{ + QWORD rv; + + rv = ptr[7]; + rv = rv << 8 | ptr[6]; + rv = rv << 8 | ptr[5]; + rv = rv << 8 | ptr[4]; + rv = rv << 8 | ptr[3]; + rv = rv << 8 | ptr[2]; + rv = rv << 8 | ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} +#endif + +#if !FF_FS_READONLY +static void st_word (BYTE* ptr, WORD val) /* Store a 2-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} + +static void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} + +#if FF_FS_EXFAT +static void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} +#endif +#endif /* !FF_FS_READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* String functions */ +/*-----------------------------------------------------------------------*/ + +/* Copy memory to memory */ +static void mem_cpy (void* dst, const void* src, UINT cnt) +{ + BYTE *d = (BYTE*)dst; + const BYTE *s = (const BYTE*)src; + + if (cnt != 0) { + do { + *d++ = *s++; + } while (--cnt); + } +} + + +/* Fill memory block */ +static void mem_set (void* dst, int val, UINT cnt) +{ + BYTE *d = (BYTE*)dst; + + do { + *d++ = (BYTE)val; + } while (--cnt); +} + + +/* Compare memory block */ +static int mem_cmp (const void* dst, const void* src, UINT cnt) /* ZR:same, NZ:different */ +{ + const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src; + int r = 0; + + do { + r = *d++ - *s++; + } while (--cnt && r == 0); + + return r; +} + + +/* Check if chr is contained in the string */ +static int chk_chr (const char* str, int chr) /* NZ:contained, ZR:not contained */ +{ + while (*str && *str != chr) str++; + return *str; +} + + +/* Test if the byte is DBC 1st byte */ +static int dbc_1st (BYTE c) +{ +#if FF_CODE_PAGE == 0 /* Variable code page */ + if (DbcTbl && c >= DbcTbl[0]) { + if (c <= DbcTbl[1]) return 1; /* 1st byte range 1 */ + if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1; /* 1st byte range 2 */ + } +#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */ + if (c >= DbcTbl[0]) { + if (c <= DbcTbl[1]) return 1; + if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1; + } +#else /* SBCS fixed code page */ + if (c != 0) return 0; /* Always false */ +#endif + return 0; +} + + +/* Test if the byte is DBC 2nd byte */ +static int dbc_2nd (BYTE c) +{ +#if FF_CODE_PAGE == 0 /* Variable code page */ + if (DbcTbl && c >= DbcTbl[4]) { + if (c <= DbcTbl[5]) return 1; /* 2nd byte range 1 */ + if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1; /* 2nd byte range 2 */ + if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1; /* 2nd byte range 3 */ + } +#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */ + if (c >= DbcTbl[4]) { + if (c <= DbcTbl[5]) return 1; + if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1; + if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1; + } +#else /* SBCS fixed code page */ + if (c != 0) return 0; /* Always false */ +#endif + return 0; +} + + +#if FF_USE_LFN + +/* Get a Unicode code point from the TCHAR string in defined API encodeing */ +static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on surrogate pair, 0xFFFFFFFF on decode error) */ + const TCHAR** str /* Pointer to pointer to TCHAR string in configured encoding */ +) +{ + DWORD uc; + const TCHAR *p = *str; + +#if FF_LFN_UNICODE == 1 /* UTF-16 input */ + WCHAR wc; + + uc = *p++; /* Get a unit */ + if (IsSurrogate(uc)) { /* Surrogate? */ + wc = *p++; /* Get low surrogate */ + if (!IsSurrogateH(uc) || !IsSurrogateL(wc)) return 0xFFFFFFFF; /* Wrong surrogate? */ + uc = uc << 16 | wc; + } + +#elif FF_LFN_UNICODE == 2 /* UTF-8 input */ + BYTE b; + int nf; + + uc = (BYTE)*p++; /* Get an encoding unit */ + if (uc & 0x80) { /* Multiple byte code? */ + if ((uc & 0xE0) == 0xC0) { /* 2-byte sequence? */ + uc &= 0x1F; nf = 1; + } else { + if ((uc & 0xF0) == 0xE0) { /* 3-byte sequence? */ + uc &= 0x0F; nf = 2; + } else { + if ((uc & 0xF8) == 0xF0) { /* 4-byte sequence? */ + uc &= 0x07; nf = 3; + } else { /* Wrong sequence */ + return 0xFFFFFFFF; + } + } + } + do { /* Get trailing bytes */ + b = (BYTE)*p++; + if ((b & 0xC0) != 0x80) return 0xFFFFFFFF; /* Wrong sequence? */ + uc = uc << 6 | (b & 0x3F); + } while (--nf != 0); + if (uc < 0x80 || IsSurrogate(uc) || uc >= 0x110000) return 0xFFFFFFFF; /* Wrong code? */ + if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */ + } + +#elif FF_LFN_UNICODE == 3 /* UTF-32 input */ + uc = (TCHAR)*p++; /* Get a unit */ + if (uc >= 0x110000 || IsSurrogate(uc)) return 0xFFFFFFFF; /* Wrong code? */ + if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */ + +#else /* ANSI/OEM input */ + BYTE b; + WCHAR wc; + + wc = (BYTE)*p++; /* Get a byte */ + if (dbc_1st((BYTE)wc)) { /* Is it a DBC 1st byte? */ + b = (BYTE)*p++; /* Get 2nd byte */ + if (!dbc_2nd(b)) return 0xFFFFFFFF; /* Invalid code? */ + wc = (wc << 8) + b; /* Make a DBC */ + } + if (wc != 0) { + wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM ==> Unicode */ + if (wc == 0) return 0xFFFFFFFF; /* Invalid code? */ + } + uc = wc; + +#endif + *str = p; /* Next read pointer */ + return uc; +} + + +/* Output a TCHAR string in defined API encoding */ +static BYTE put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */ + DWORD chr, /* UTF-16 encoded character (Surrogate pair if >=0x10000) */ + TCHAR* buf, /* Output buffer */ + UINT szb /* Size of the buffer */ +) +{ +#if FF_LFN_UNICODE == 1 /* UTF-16 output */ + WCHAR hs, wc; + + hs = (WCHAR)(chr >> 16); + wc = (WCHAR)chr; + if (hs == 0) { /* Single encoding unit? */ + if (szb < 1 || IsSurrogate(wc)) return 0; /* Buffer overflow or wrong code? */ + *buf = wc; + return 1; + } + if (szb < 2 || !IsSurrogateH(hs) || !IsSurrogateL(wc)) return 0; /* Buffer overflow or wrong surrogate? */ + *buf++ = hs; + *buf++ = wc; + return 2; + +#elif FF_LFN_UNICODE == 2 /* UTF-8 output */ + DWORD hc; + + if (chr < 0x80) { /* Single byte code? */ + if (szb < 1) return 0; /* Buffer overflow? */ + *buf = (TCHAR)chr; + return 1; + } + if (chr < 0x800) { /* 2-byte sequence? */ + if (szb < 2) return 0; /* Buffer overflow? */ + *buf++ = (TCHAR)(0xC0 | (chr >> 6 & 0x1F)); + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); + return 2; + } + if (chr < 0x10000) { /* 3-byte sequence? */ + if (szb < 3 || IsSurrogate(chr)) return 0; /* Buffer overflow or wrong code? */ + *buf++ = (TCHAR)(0xE0 | (chr >> 12 & 0x0F)); + *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F)); + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); + return 3; + } + /* 4-byte sequence */ + if (szb < 4) return 0; /* Buffer overflow? */ + hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high 10 bits */ + chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 bits */ + if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong surrogate? */ + chr = (hc | chr) + 0x10000; + *buf++ = (TCHAR)(0xF0 | (chr >> 18 & 0x07)); + *buf++ = (TCHAR)(0x80 | (chr >> 12 & 0x3F)); + *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F)); + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); + return 4; + +#elif FF_LFN_UNICODE == 3 /* UTF-32 output */ + DWORD hc; + + if (szb < 1) return 0; /* Buffer overflow? */ + if (chr >= 0x10000) { /* Out of BMP? */ + hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high 10 bits */ + chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 bits */ + if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong surrogate? */ + chr = (hc | chr) + 0x10000; + } + *buf++ = (TCHAR)chr; + return 1; + +#else /* ANSI/OEM output */ + WCHAR wc; + + wc = ff_uni2oem(chr, CODEPAGE); + if (wc >= 0x100) { /* Is this a DBC? */ + if (szb < 2) return 0; + *buf++ = (char)(wc >> 8); /* Store DBC 1st byte */ + *buf++ = (TCHAR)wc; /* Store DBC 2nd byte */ + return 2; + } + if (wc == 0 || szb < 1) return 0; /* Invalid char or buffer overflow? */ + *buf++ = (TCHAR)wc; /* Store the character */ + return 1; +#endif +} +#endif /* FF_USE_LFN */ + + +#if FF_FS_REENTRANT +/*-----------------------------------------------------------------------*/ +/* Request/Release grant to access the volume */ +/*-----------------------------------------------------------------------*/ +static int lock_fs ( /* 1:Ok, 0:timeout */ + FATFS* fs /* Filesystem object */ +) +{ + return ff_req_grant(fs->sobj); +} + + +static void unlock_fs ( + FATFS* fs, /* Filesystem object */ + FRESULT res /* Result code to be returned */ +) +{ + if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) { + ff_rel_grant(fs->sobj); + } +} + +#endif + + + +#if FF_FS_LOCK != 0 +/*-----------------------------------------------------------------------*/ +/* File lock control functions */ +/*-----------------------------------------------------------------------*/ + +static FRESULT chk_lock ( /* Check if the file can be accessed */ + DIR* dp, /* Directory object pointing the file to be checked */ + int acc /* Desired access type (0:Read mode open, 1:Write mode open, 2:Delete or rename) */ +) +{ + UINT i, be; + + /* Search open object table for the object */ + be = 0; + for (i = 0; i < FF_FS_LOCK; i++) { + if (Files[i].fs) { /* Existing entry */ + if (Files[i].fs == dp->obj.fs && /* Check if the object matches with an open object */ + Files[i].clu == dp->obj.sclust && + Files[i].ofs == dp->dptr) break; + } else { /* Blank entry */ + be = 1; + } + } + if (i == FF_FS_LOCK) { /* The object has not been opened */ + return (!be && acc != 2) ? FR_TOO_MANY_OPEN_FILES : FR_OK; /* Is there a blank entry for new object? */ + } + + /* The object was opened. Reject any open against writing file and all write mode open */ + return (acc != 0 || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; +} + + +static int enq_lock (void) /* Check if an entry is available for a new object */ +{ + UINT i; + + for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ; + return (i == FF_FS_LOCK) ? 0 : 1; +} + + +static UINT inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */ + DIR* dp, /* Directory object pointing the file to register or increment */ + int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i; + + + for (i = 0; i < FF_FS_LOCK; i++) { /* Find the object */ + if (Files[i].fs == dp->obj.fs + && Files[i].clu == dp->obj.sclust + && Files[i].ofs == dp->dptr) break; + } + + if (i == FF_FS_LOCK) { /* Not opened. Register it as new. */ + for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ; + if (i == FF_FS_LOCK) return 0; /* No free entry to register (int err) */ + Files[i].fs = dp->obj.fs; + Files[i].clu = dp->obj.sclust; + Files[i].ofs = dp->dptr; + Files[i].ctr = 0; + } + + if (acc >= 1 && Files[i].ctr) return 0; /* Access violation (int err) */ + + Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */ + + return i + 1; /* Index number origin from 1 */ +} + + +static FRESULT dec_lock ( /* Decrement object open counter */ + UINT i /* Semaphore index (1..) */ +) +{ + WORD n; + FRESULT res; + + + if (--i < FF_FS_LOCK) { /* Index number origin from 0 */ + n = Files[i].ctr; + if (n == 0x100) n = 0; /* If write mode open, delete the entry */ + if (n > 0) n--; /* Decrement read mode open count */ + Files[i].ctr = n; + if (n == 0) Files[i].fs = 0; /* Delete the entry if open count gets zero */ + res = FR_OK; + } else { + res = FR_INT_ERR; /* Invalid index nunber */ + } + return res; +} + + +static void clear_lock ( /* Clear lock entries of the volume */ + FATFS *fs +) +{ + UINT i; + + for (i = 0; i < FF_FS_LOCK; i++) { + if (Files[i].fs == fs) Files[i].fs = 0; + } +} + +#endif /* FF_FS_LOCK != 0 */ + + + +/*-----------------------------------------------------------------------*/ +/* Move/Flush disk access window in the filesystem object */ +/*-----------------------------------------------------------------------*/ +#if !FF_FS_READONLY +static FRESULT sync_window ( /* Returns FR_OK or FR_DISK_ERR */ + FATFS* fs /* Filesystem object */ +) +{ + FRESULT res = FR_OK; + + + if (fs->wflag) { /* Is the disk access window dirty? */ + if (disk_write(fs->pdrv, fs->win, fs->winsect, 1) == RES_OK) { /* Write it back into the volume */ + fs->wflag = 0; /* Clear window dirty flag */ + if (fs->winsect - fs->fatbase < fs->fsize) { /* Is it in the 1st FAT? */ + if (fs->n_fats == 2) disk_write(fs->pdrv, fs->win, fs->winsect + fs->fsize, 1); /* Reflect it to 2nd FAT if needed */ + } + } else { + res = FR_DISK_ERR; + } + } + return res; +} +#endif + + +static FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERR */ + FATFS* fs, /* Filesystem object */ + LBA_t sect /* Sector LBA to make appearance in the fs->win[] */ +) +{ + FRESULT res = FR_OK; + + + if (sect != fs->winsect) { /* Window offset changed? */ +#if !FF_FS_READONLY + res = sync_window(fs); /* Flush the window */ +#endif + if (res == FR_OK) { /* Fill sector window with new data */ + if (disk_read(fs->pdrv, fs->win, sect, 1) != RES_OK) { + sect = (LBA_t)0 - 1; /* Invalidate window if read data is not valid */ + res = FR_DISK_ERR; + } + fs->winsect = sect; + } + } + return res; +} + + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Synchronize filesystem and data on the storage */ +/*-----------------------------------------------------------------------*/ + +static FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */ + FATFS* fs /* Filesystem object */ +) +{ + FRESULT res; + + + res = sync_window(fs); + if (res == FR_OK) { + if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { /* FAT32: Update FSInfo sector if needed */ + /* Create FSInfo structure */ + mem_set(fs->win, 0, sizeof fs->win); + st_word(fs->win + BS_55AA, 0xAA55); /* Boot signature */ + st_dword(fs->win + FSI_LeadSig, 0x41615252); /* Leading signature */ + st_dword(fs->win + FSI_StrucSig, 0x61417272); /* Structure signature */ + st_dword(fs->win + FSI_Free_Count, fs->free_clst); /* Number of free clusters */ + st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); /* Last allocated culuster */ + fs->winsect = fs->volbase + 1; /* Write it into the FSInfo sector (Next to VBR) */ + disk_write(fs->pdrv, fs->win, fs->winsect, 1); + fs->fsi_flag = 0; + } + /* Make sure that no pending write process in the lower layer */ + if (disk_ioctl(fs->pdrv, CTRL_SYNC, 0) != RES_OK) res = FR_DISK_ERR; + } + + return res; +} + +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Get physical sector number from cluster number */ +/*-----------------------------------------------------------------------*/ + +static LBA_t clst2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */ + FATFS* fs, /* Filesystem object */ + DWORD clst /* Cluster# to be converted */ +) +{ + clst -= 2; /* Cluster number is origin from 2 */ + if (clst >= fs->n_fatent - 2) return 0; /* Is it invalid cluster number? */ + return fs->database + (LBA_t)fs->csize * clst; /* Start sector number of the cluster */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Read value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + +static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */ + FFOBJID* obj, /* Corresponding object */ + DWORD clst /* Cluster number to get the value */ +) +{ + UINT wc, bc; + DWORD val; + FATFS *fs = obj->fs; + + + if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid range */ + val = 1; /* Internal error */ + + } else { + val = 0xFFFFFFFF; /* Default value falls on disk error */ + + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; + wc = fs->win[bc++ % SS(fs)]; /* Get 1st byte of the entry */ + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; + wc |= fs->win[bc % SS(fs)] << 8; /* Merge 2nd byte of the entry */ + val = (clst & 1) ? (wc >> 4) : (wc & 0xFFF); /* Adjust bit position */ + break; + + case FS_FAT16 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break; + val = ld_word(fs->win + clst * 2 % SS(fs)); /* Simple WORD array */ + break; + + case FS_FAT32 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x0FFFFFFF; /* Simple DWORD array but mask out upper 4 bits */ + break; +#if FF_FS_EXFAT + case FS_EXFAT : + if ((obj->objsize != 0 && obj->sclust != 0) || obj->stat == 0) { /* Object except root dir must have valid data length */ + DWORD cofs = clst - obj->sclust; /* Offset from start cluster */ + DWORD clen = (DWORD)((LBA_t)((obj->objsize - 1) / SS(fs)) / fs->csize); /* Number of clusters - 1 */ + + if (obj->stat == 2 && cofs <= clen) { /* Is it a contiguous chain? */ + val = (cofs == clen) ? 0x7FFFFFFF : clst + 1; /* No data on the FAT, generate the value */ + break; + } + if (obj->stat == 3 && cofs < obj->n_cont) { /* Is it in the 1st fragment? */ + val = clst + 1; /* Generate the value */ + break; + } + if (obj->stat != 2) { /* Get value from FAT if FAT chain is valid */ + if (obj->n_frag != 0) { /* Is it on the growing edge? */ + val = 0x7FFFFFFF; /* Generate EOC */ + } else { + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x7FFFFFFF; + } + break; + } + } + val = 1; /* Internal error */ + break; +#endif + default: + val = 1; /* Internal error */ + } + } + + return val; +} + + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT access - Change value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + +static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ + FATFS* fs, /* Corresponding filesystem object */ + DWORD clst, /* FAT index number (cluster number) to be changed */ + DWORD val /* New value to be set to the entry */ +) +{ + UINT bc; + BYTE *p; + FRESULT res = FR_INT_ERR; + + + if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */ + switch (fs->fs_type) { + case FS_FAT12: + bc = (UINT)clst; bc += bc / 2; /* bc: byte offset of the entry */ + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = fs->win + bc++ % SS(fs); + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; /* Update 1st byte */ + fs->wflag = 1; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = fs->win + bc % SS(fs); + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); /* Update 2nd byte */ + fs->wflag = 1; + break; + + case FS_FAT16: + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); + if (res != FR_OK) break; + st_word(fs->win + clst * 2 % SS(fs), (WORD)val); /* Simple WORD array */ + fs->wflag = 1; + break; + + case FS_FAT32: +#if FF_FS_EXFAT + case FS_EXFAT: +#endif + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); + if (res != FR_OK) break; + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { + val = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst * 4 % SS(fs)) & 0xF0000000); + } + st_dword(fs->win + clst * 4 % SS(fs), val); + fs->wflag = 1; + break; + } + } + return res; +} + +#endif /* !FF_FS_READONLY */ + + + + +#if FF_FS_EXFAT && !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* exFAT: Accessing FAT and Allocation Bitmap */ +/*-----------------------------------------------------------------------*/ + +/*--------------------------------------*/ +/* Find a contiguous free cluster block */ +/*--------------------------------------*/ + +static DWORD find_bitmap ( /* 0:Not found, 2..:Cluster block found, 0xFFFFFFFF:Disk error */ + FATFS* fs, /* Filesystem object */ + DWORD clst, /* Cluster number to scan from */ + DWORD ncl /* Number of contiguous clusters to find (1..) */ +) +{ + BYTE bm, bv; + UINT i; + DWORD val, scl, ctr; + + + clst -= 2; /* The first bit in the bitmap corresponds to cluster #2 */ + if (clst >= fs->n_fatent - 2) clst = 0; + scl = val = clst; ctr = 0; + for (;;) { + if (move_window(fs, fs->bitbase + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF; + i = val / 8 % SS(fs); bm = 1 << (val % 8); + do { + do { + bv = fs->win[i] & bm; bm <<= 1; /* Get bit value */ + if (++val >= fs->n_fatent - 2) { /* Next cluster (with wrap-around) */ + val = 0; bm = 0; i = SS(fs); + } + if (bv == 0) { /* Is it a free cluster? */ + if (++ctr == ncl) return scl + 2; /* Check if run length is sufficient for required */ + } else { + scl = val; ctr = 0; /* Encountered a cluster in-use, restart to scan */ + } + if (val == clst) return 0; /* All cluster scanned? */ + } while (bm != 0); + bm = 1; + } while (++i < SS(fs)); + } +} + + +/*----------------------------------------*/ +/* Set/Clear a block of allocation bitmap */ +/*----------------------------------------*/ + +static FRESULT change_bitmap ( + FATFS* fs, /* Filesystem object */ + DWORD clst, /* Cluster number to change from */ + DWORD ncl, /* Number of clusters to be changed */ + int bv /* bit value to be set (0 or 1) */ +) +{ + BYTE bm; + UINT i; + LBA_t sect; + + + clst -= 2; /* The first bit corresponds to cluster #2 */ + sect = fs->bitbase + clst / 8 / SS(fs); /* Sector address */ + i = clst / 8 % SS(fs); /* Byte offset in the sector */ + bm = 1 << (clst % 8); /* Bit mask in the byte */ + for (;;) { + if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR; + do { + do { + if (bv == (int)((fs->win[i] & bm) != 0)) return FR_INT_ERR; /* Is the bit expected value? */ + fs->win[i] ^= bm; /* Flip the bit */ + fs->wflag = 1; + if (--ncl == 0) return FR_OK; /* All bits processed? */ + } while (bm <<= 1); /* Next bit */ + bm = 1; + } while (++i < SS(fs)); /* Next byte */ + i = 0; + } +} + + +/*---------------------------------------------*/ +/* Fill the first fragment of the FAT chain */ +/*---------------------------------------------*/ + +static FRESULT fill_first_frag ( + FFOBJID* obj /* Pointer to the corresponding object */ +) +{ + FRESULT res; + DWORD cl, n; + + + if (obj->stat == 3) { /* Has the object been changed 'fragmented' in this session? */ + for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) { /* Create cluster chain on the FAT */ + res = put_fat(obj->fs, cl, cl + 1); + if (res != FR_OK) return res; + } + obj->stat = 0; /* Change status 'FAT chain is valid' */ + } + return FR_OK; +} + + +/*---------------------------------------------*/ +/* Fill the last fragment of the FAT chain */ +/*---------------------------------------------*/ + +static FRESULT fill_last_frag ( + FFOBJID* obj, /* Pointer to the corresponding object */ + DWORD lcl, /* Last cluster of the fragment */ + DWORD term /* Value to set the last FAT entry */ +) +{ + FRESULT res; + + + while (obj->n_frag > 0) { /* Create the chain of last fragment */ + res = put_fat(obj->fs, lcl - obj->n_frag + 1, (obj->n_frag > 1) ? lcl - obj->n_frag + 2 : term); + if (res != FR_OK) return res; + obj->n_frag--; + } + return FR_OK; +} + +#endif /* FF_FS_EXFAT && !FF_FS_READONLY */ + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT handling - Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ + +static FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ + FFOBJID* obj, /* Corresponding object */ + DWORD clst, /* Cluster to remove a chain from */ + DWORD pclst /* Previous cluster of clst (0 if entire chain) */ +) +{ + FRESULT res = FR_OK; + DWORD nxt; + FATFS *fs = obj->fs; +#if FF_FS_EXFAT || FF_USE_TRIM + DWORD scl = clst, ecl = clst; +#endif +#if FF_USE_TRIM + LBA_t rt[2]; +#endif + + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Check if in valid range */ + + /* Mark the previous cluster 'EOC' on the FAT if it exists */ + if (pclst != 0 && (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT || obj->stat != 2)) { + res = put_fat(fs, pclst, 0xFFFFFFFF); + if (res != FR_OK) return res; + } + + /* Remove the chain */ + do { + nxt = get_fat(obj, clst); /* Get cluster status */ + if (nxt == 0) break; /* Empty cluster? */ + if (nxt == 1) return FR_INT_ERR; /* Internal error? */ + if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error? */ + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { + res = put_fat(fs, clst, 0); /* Mark the cluster 'free' on the FAT */ + if (res != FR_OK) return res; + } + if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */ + fs->free_clst++; + fs->fsi_flag |= 1; + } +#if FF_FS_EXFAT || FF_USE_TRIM + if (ecl + 1 == nxt) { /* Is next cluster contiguous? */ + ecl = nxt; + } else { /* End of contiguous cluster block */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */ + if (res != FR_OK) return res; + } +#endif +#if FF_USE_TRIM + rt[0] = clst2sect(fs, scl); /* Start of data area to be freed */ + rt[1] = clst2sect(fs, ecl) + fs->csize - 1; /* End of data area to be freed */ + disk_ioctl(fs->pdrv, CTRL_TRIM, rt); /* Inform storage device that the data in the block may be erased */ +#endif + scl = ecl = nxt; + } +#endif + clst = nxt; /* Next cluster */ + } while (clst < fs->n_fatent); /* Repeat while not the last link */ + +#if FF_FS_EXFAT + /* Some post processes for chain status */ + if (fs->fs_type == FS_EXFAT) { + if (pclst == 0) { /* Has the entire chain been removed? */ + obj->stat = 0; /* Change the chain status 'initial' */ + } else { + if (obj->stat == 0) { /* Is it a fragmented chain from the beginning of this session? */ + clst = obj->sclust; /* Follow the chain to check if it gets contiguous */ + while (clst != pclst) { + nxt = get_fat(obj, clst); + if (nxt < 2) return FR_INT_ERR; + if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; + if (nxt != clst + 1) break; /* Not contiguous? */ + clst++; + } + if (clst == pclst) { /* Has the chain got contiguous again? */ + obj->stat = 2; /* Change the chain status 'contiguous' */ + } + } else { + if (obj->stat == 3 && pclst >= obj->sclust && pclst <= obj->sclust + obj->n_cont) { /* Was the chain fragmented in this session and got contiguous again? */ + obj->stat = 2; /* Change the chain status 'contiguous' */ + } + } + } + } +#endif + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Stretch a chain or Create a new chain */ +/*-----------------------------------------------------------------------*/ + +static DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ + FFOBJID* obj, /* Corresponding object */ + DWORD clst /* Cluster# to stretch, 0:Create a new chain */ +) +{ + DWORD cs, ncl, scl; + FRESULT res; + FATFS *fs = obj->fs; + + + if (clst == 0) { /* Create a new chain */ + scl = fs->last_clst; /* Suggested cluster to start to find */ + if (scl == 0 || scl >= fs->n_fatent) scl = 1; + } + else { /* Stretch a chain */ + cs = get_fat(obj, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* Test for insanity */ + if (cs == 0xFFFFFFFF) return cs; /* Test for disk error */ + if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */ + scl = clst; /* Cluster to start to find */ + } + if (fs->free_clst == 0) return 0; /* No free cluster */ + +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + ncl = find_bitmap(fs, scl, 1); /* Find a free cluster */ + if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl; /* No free cluster or hard error? */ + res = change_bitmap(fs, ncl, 1, 1); /* Mark the cluster 'in use' */ + if (res == FR_INT_ERR) return 1; + if (res == FR_DISK_ERR) return 0xFFFFFFFF; + if (clst == 0) { /* Is it a new chain? */ + obj->stat = 2; /* Set status 'contiguous' */ + } else { /* It is a stretched chain */ + if (obj->stat == 2 && ncl != scl + 1) { /* Is the chain got fragmented? */ + obj->n_cont = scl - obj->sclust; /* Set size of the contiguous part */ + obj->stat = 3; /* Change status 'just fragmented' */ + } + } + if (obj->stat != 2) { /* Is the file non-contiguous? */ + if (ncl == clst + 1) { /* Is the cluster next to previous one? */ + obj->n_frag = obj->n_frag ? obj->n_frag + 1 : 2; /* Increment size of last framgent */ + } else { /* New fragment */ + if (obj->n_frag == 0) obj->n_frag = 1; + res = fill_last_frag(obj, clst, ncl); /* Fill last fragment on the FAT and link it to new one */ + if (res == FR_OK) obj->n_frag = 1; + } + } + } else +#endif + { /* On the FAT/FAT32 volume */ + ncl = 0; + if (scl == clst) { /* Stretching an existing chain? */ + ncl = scl + 1; /* Test if next cluster is free */ + if (ncl >= fs->n_fatent) ncl = 2; + cs = get_fat(obj, ncl); /* Get next cluster status */ + if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* Test for error */ + if (cs != 0) { /* Not free? */ + cs = fs->last_clst; /* Start at suggested cluster if it is valid */ + if (cs >= 2 && cs < fs->n_fatent) scl = cs; + ncl = 0; + } + } + if (ncl == 0) { /* The new cluster cannot be contiguous and find another fragment */ + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= fs->n_fatent) { /* Check wrap-around */ + ncl = 2; + if (ncl > scl) return 0; /* No free cluster found? */ + } + cs = get_fat(obj, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster? */ + if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* Test for error */ + if (ncl == scl) return 0; /* No free cluster found? */ + } + } + res = put_fat(fs, ncl, 0xFFFFFFFF); /* Mark the new cluster 'EOC' */ + if (res == FR_OK && clst != 0) { + res = put_fat(fs, clst, ncl); /* Link it from the previous one if needed */ + } + } + + if (res == FR_OK) { /* Update FSINFO if function succeeded. */ + fs->last_clst = ncl; + if (fs->free_clst <= fs->n_fatent - 2) fs->free_clst--; + fs->fsi_flag |= 1; + } else { + ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; /* Failed. Generate error status */ + } + + return ncl; /* Return new cluster number or error status */ +} + +#endif /* !FF_FS_READONLY */ + + + + +#if FF_USE_FASTSEEK +/*-----------------------------------------------------------------------*/ +/* FAT handling - Convert offset into cluster with link map table */ +/*-----------------------------------------------------------------------*/ + +static DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ + FIL* fp, /* Pointer to the file object */ + FSIZE_t ofs /* File offset to be converted to cluster# */ +) +{ + DWORD cl, ncl, *tbl; + FATFS *fs = fp->obj.fs; + + + tbl = fp->cltbl + 1; /* Top of CLMT */ + cl = (DWORD)(ofs / SS(fs) / fs->csize); /* Cluster order from top of the file */ + for (;;) { + ncl = *tbl++; /* Number of cluters in the fragment */ + if (ncl == 0) return 0; /* End of table? (error) */ + if (cl < ncl) break; /* In this fragment? */ + cl -= ncl; tbl++; /* Next fragment */ + } + return cl + *tbl; /* Return the cluster number */ +} + +#endif /* FF_USE_FASTSEEK */ + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Fill a cluster with zeros */ +/*-----------------------------------------------------------------------*/ + +#if !FF_FS_READONLY +static FRESULT dir_clear ( /* Returns FR_OK or FR_DISK_ERR */ + FATFS *fs, /* Filesystem object */ + DWORD clst /* Directory table to clear */ +) +{ + LBA_t sect; + UINT n, szb; + BYTE *ibuf; + + + if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush disk access window */ + sect = clst2sect(fs, clst); /* Top of the cluster */ + fs->winsect = sect; /* Set window to top of the cluster */ + mem_set(fs->win, 0, sizeof fs->win); /* Clear window buffer */ +#if FF_USE_LFN == 3 /* Quick table clear by using multi-secter write */ + /* Allocate a temporary buffer */ + for (szb = ((DWORD)fs->csize * SS(fs) >= MAX_MALLOC) ? MAX_MALLOC : fs->csize * SS(fs), ibuf = 0; szb > SS(fs) && (ibuf = ff_memalloc(szb)) == 0; szb /= 2) ; + if (szb > SS(fs)) { /* Buffer allocated? */ + mem_set(ibuf, 0, szb); + szb /= SS(fs); /* Bytes -> Sectors */ + for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */ + ff_memfree(ibuf); + } else +#endif + { + ibuf = fs->win; szb = 1; /* Use window buffer (many single-sector writes may take a time) */ + for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */ + } + return (n == fs->csize) ? FR_OK : FR_DISK_ERR; +} +#endif /* !FF_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Set directory index */ +/*-----------------------------------------------------------------------*/ + +static FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp, /* Pointer to directory object */ + DWORD ofs /* Offset of directory table */ +) +{ + DWORD csz, clst; + FATFS *fs = dp->obj.fs; + + + if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) { /* Check range of offset and alignment */ + return FR_INT_ERR; + } + dp->dptr = ofs; /* Set current offset */ + clst = dp->obj.sclust; /* Table start cluster (0:root) */ + if (clst == 0 && fs->fs_type >= FS_FAT32) { /* Replace cluster# 0 with root cluster# */ + clst = (DWORD)fs->dirbase; + if (FF_FS_EXFAT) dp->obj.stat = 0; /* exFAT: Root dir has an FAT chain */ + } + + if (clst == 0) { /* Static table (root-directory on the FAT volume) */ + if (ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR; /* Is index out of range? */ + dp->sect = fs->dirbase; + + } else { /* Dynamic table (sub-directory or root-directory on the FAT32/exFAT volume) */ + csz = (DWORD)fs->csize * SS(fs); /* Bytes per cluster */ + while (ofs >= csz) { /* Follow cluster chain */ + clst = get_fat(&dp->obj, clst); /* Get next cluster */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Reached to end of table or internal error */ + ofs -= csz; + } + dp->sect = clst2sect(fs, clst); + } + dp->clust = clst; /* Current cluster# */ + if (dp->sect == 0) return FR_INT_ERR; + dp->sect += ofs / SS(fs); /* Sector# of the directory entry */ + dp->dir = fs->win + (ofs % SS(fs)); /* Pointer to the entry in the win[] */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Move directory table index next */ +/*-----------------------------------------------------------------------*/ + +static FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */ + DIR* dp, /* Pointer to the directory object */ + int stretch /* 0: Do not stretch table, 1: Stretch table if needed */ +) +{ + DWORD ofs, clst; + FATFS *fs = dp->obj.fs; + + + ofs = dp->dptr + SZDIRE; /* Next entry */ + if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR)) dp->sect = 0; /* Disable it if the offset reached the max value */ + if (dp->sect == 0) return FR_NO_FILE; /* Report EOT if it has been disabled */ + + if (ofs % SS(fs) == 0) { /* Sector changed? */ + dp->sect++; /* Next sector */ + + if (dp->clust == 0) { /* Static table */ + if (ofs / SZDIRE >= fs->n_rootdir) { /* Report EOT if it reached end of static table */ + dp->sect = 0; return FR_NO_FILE; + } + } + else { /* Dynamic table */ + if ((ofs / SS(fs) & (fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(&dp->obj, dp->clust); /* Get next cluster */ + if (clst <= 1) return FR_INT_ERR; /* Internal error */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst >= fs->n_fatent) { /* It reached end of dynamic table */ +#if !FF_FS_READONLY + if (!stretch) { /* If no stretch, report EOT */ + dp->sect = 0; return FR_NO_FILE; + } + clst = create_chain(&dp->obj, dp->clust); /* Allocate a cluster */ + if (clst == 0) return FR_DENIED; /* No free cluster */ + if (clst == 1) return FR_INT_ERR; /* Internal error */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (dir_clear(fs, clst) != FR_OK) return FR_DISK_ERR; /* Clean up the stretched table */ + if (FF_FS_EXFAT) dp->obj.stat |= 4; /* exFAT: The directory has been stretched */ +#else + if (!stretch) dp->sect = 0; /* (this line is to suppress compiler warning) */ + dp->sect = 0; return FR_NO_FILE; /* Report EOT */ +#endif + } + dp->clust = clst; /* Initialize data for new cluster */ + dp->sect = clst2sect(fs, clst); + } + } + } + dp->dptr = ofs; /* Current entry */ + dp->dir = fs->win + ofs % SS(fs); /* Pointer to the entry in the win[] */ + + return FR_OK; +} + + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Directory handling - Reserve a block of directory entries */ +/*-----------------------------------------------------------------------*/ + +static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp, /* Pointer to the directory object */ + UINT n_ent /* Number of contiguous entries to allocate */ +) +{ + FRESULT res; + UINT n; + FATFS *fs = dp->obj.fs; + + + res = dir_sdi(dp, 0); + if (res == FR_OK) { + n = 0; + do { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; +#if FF_FS_EXFAT + if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) { /* Is the entry free? */ +#else + if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { /* Is the entry free? */ +#endif + if (++n == n_ent) break; /* Is a block of contiguous free entries found? */ + } else { + n = 0; /* Not a free entry, restart to search */ + } + res = dir_next(dp, 1); /* Next entry with table stretch enabled */ + } while (res == FR_OK); + } + + if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */ + return res; +} + +#endif /* !FF_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* FAT: Directory handling - Load/Store start cluster number */ +/*-----------------------------------------------------------------------*/ + +static DWORD ld_clust ( /* Returns the top cluster value of the SFN entry */ + FATFS* fs, /* Pointer to the fs object */ + const BYTE* dir /* Pointer to the key entry */ +) +{ + DWORD cl; + + cl = ld_word(dir + DIR_FstClusLO); + if (fs->fs_type == FS_FAT32) { + cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16; + } + + return cl; +} + + +#if !FF_FS_READONLY +static void st_clust ( + FATFS* fs, /* Pointer to the fs object */ + BYTE* dir, /* Pointer to the key entry */ + DWORD cl /* Value to be set */ +) +{ + st_word(dir + DIR_FstClusLO, (WORD)cl); + if (fs->fs_type == FS_FAT32) { + st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16)); + } +} +#endif + + + +#if FF_USE_LFN +/*--------------------------------------------------------*/ +/* FAT-LFN: Compare a part of file name with an LFN entry */ +/*--------------------------------------------------------*/ + +static int cmp_lfn ( /* 1:matched, 0:not matched */ + const WCHAR* lfnbuf, /* Pointer to the LFN working buffer to be compared */ + BYTE* dir /* Pointer to the directory entry containing the part of LFN */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ + if (wc != 0) { + if (i >= FF_MAX_LFN + 1 || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) { /* Compare it */ + return 0; /* Not matched */ + } + wc = uc; + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } + + if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) return 0; /* Last segment matched but different length */ + + return 1; /* The part of LFN matched */ +} + + +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT +/*-----------------------------------------------------*/ +/* FAT-LFN: Pick a part of file name from an LFN entry */ +/*-----------------------------------------------------*/ + +static int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */ + WCHAR* lfnbuf, /* Pointer to the LFN working buffer */ + BYTE* dir /* Pointer to the LFN entry */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO is 0 */ + + i = ((dir[LDIR_Ord] & ~LLEF) - 1) * 13; /* Offset in the LFN buffer */ + + for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ + if (wc != 0) { + if (i >= FF_MAX_LFN + 1) return 0; /* Buffer overflow? */ + lfnbuf[i++] = wc = uc; /* Store it */ + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } + + if (dir[LDIR_Ord] & LLEF && wc != 0) { /* Put terminator if it is the last LFN part and not terminated */ + if (i >= FF_MAX_LFN + 1) return 0; /* Buffer overflow? */ + lfnbuf[i] = 0; + } + + return 1; /* The part of LFN is valid */ +} +#endif + + +#if !FF_FS_READONLY +/*-----------------------------------------*/ +/* FAT-LFN: Create an entry of LFN entries */ +/*-----------------------------------------*/ + +static void put_lfn ( + const WCHAR* lfn, /* Pointer to the LFN */ + BYTE* dir, /* Pointer to the LFN entry to be created */ + BYTE ord, /* LFN order (1-20) */ + BYTE sum /* Checksum of the corresponding SFN */ +) +{ + UINT i, s; + WCHAR wc; + + + dir[LDIR_Chksum] = sum; /* Set checksum */ + dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ + dir[LDIR_Type] = 0; + st_word(dir + LDIR_FstClusLO, 0); + + i = (ord - 1) * 13; /* Get offset in the LFN working buffer */ + s = wc = 0; + do { + if (wc != 0xFFFF) wc = lfn[i++]; /* Get an effective character */ + st_word(dir + LfnOfs[s], wc); /* Put it */ + if (wc == 0) wc = 0xFFFF; /* Padding characters for following items */ + } while (++s < 13); + if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part is the start of LFN sequence */ + dir[LDIR_Ord] = ord; /* Set the LFN order */ +} + +#endif /* !FF_FS_READONLY */ +#endif /* FF_USE_LFN */ + + + +#if FF_USE_LFN && !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT-LFN: Create a Numbered SFN */ +/*-----------------------------------------------------------------------*/ + +static void gen_numname ( + BYTE* dst, /* Pointer to the buffer to store numbered SFN */ + const BYTE* src, /* Pointer to SFN */ + const WCHAR* lfn, /* Pointer to LFN */ + UINT seq /* Sequence number */ +) +{ + BYTE ns[8], c; + UINT i, j; + WCHAR wc; + DWORD sreg; + + + mem_cpy(dst, src, 11); + + if (seq > 5) { /* In case of many collisions, generate a hash number instead of sequential number */ + sreg = seq; + while (*lfn) { /* Create a CRC as hash value */ + wc = *lfn++; + for (i = 0; i < 16; i++) { + sreg = (sreg << 1) + (wc & 1); + wc >>= 1; + if (sreg & 0x10000) sreg ^= 0x11021; + } + } + seq = (UINT)sreg; + } + + /* itoa (hexdecimal) */ + i = 7; + do { + c = (BYTE)((seq % 16) + '0'); + if (c > '9') c += 7; + ns[i--] = c; + seq /= 16; + } while (seq); + ns[i] = '~'; + + /* Append the number to the SFN body */ + for (j = 0; j < i && dst[j] != ' '; j++) { + if (dbc_1st(dst[j])) { + if (j == i - 1) break; + j++; + } + } + do { + dst[j++] = (i < 8) ? ns[i++] : ' '; + } while (j < 8); +} +#endif /* FF_USE_LFN && !FF_FS_READONLY */ + + + +#if FF_USE_LFN +/*-----------------------------------------------------------------------*/ +/* FAT-LFN: Calculate checksum of an SFN entry */ +/*-----------------------------------------------------------------------*/ + +static BYTE sum_sfn ( + const BYTE* dir /* Pointer to the SFN entry */ +) +{ + BYTE sum = 0; + UINT n = 11; + + do { + sum = (sum >> 1) + (sum << 7) + *dir++; + } while (--n); + return sum; +} + +#endif /* FF_USE_LFN */ + + + +#if FF_FS_EXFAT +/*-----------------------------------------------------------------------*/ +/* exFAT: Checksum */ +/*-----------------------------------------------------------------------*/ + +static WORD xdir_sum ( /* Get checksum of the directoly entry block */ + const BYTE* dir /* Directory entry block to be calculated */ +) +{ + UINT i, szblk; + WORD sum; + + + szblk = (dir[XDIR_NumSec] + 1) * SZDIRE; /* Number of bytes of the entry block */ + for (i = sum = 0; i < szblk; i++) { + if (i == XDIR_SetSum) { /* Skip 2-byte sum field */ + i++; + } else { + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i]; + } + } + return sum; +} + + + +static WORD xname_sum ( /* Get check sum (to be used as hash) of the file name */ + const WCHAR* name /* File name to be calculated */ +) +{ + WCHAR chr; + WORD sum = 0; + + + while ((chr = *name++) != 0) { + chr = (WCHAR)ff_wtoupper(chr); /* File name needs to be up-case converted */ + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF); + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8); + } + return sum; +} + + +#if !FF_FS_READONLY && FF_USE_MKFS +static DWORD xsum32 ( /* Returns 32-bit checksum */ + BYTE dat, /* Byte to be calculated (byte-by-byte processing) */ + DWORD sum /* Previous sum value */ +) +{ + sum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat; + return sum; +} +#endif + + +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 +/*------------------------------------------------------*/ +/* exFAT: Get object information from a directory block */ +/*------------------------------------------------------*/ + +static void get_xfileinfo ( + BYTE* dirb, /* Pointer to the direcotry entry block 85+C0+C1s */ + FILINFO* fno /* Buffer to store the extracted file information */ +) +{ + WCHAR wc, hs; + UINT di, si, nc; + + /* Get file name from the entry block */ + si = SZDIRE * 2; /* 1st C1 entry */ + nc = 0; hs = 0; di = 0; + while (nc < dirb[XDIR_NumName]) { + if (si >= MAXDIRB(FF_MAX_LFN)) { di = 0; break; } /* Truncated directory block? */ + if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ + wc = ld_word(dirb + si); si += 2; nc++; /* Get a character */ + if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ + hs = wc; continue; /* Get low surrogate */ + } + wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in API encoding */ + if (wc == 0) { di = 0; break; } /* Buffer overflow or wrong encoding? */ + di += wc; + hs = 0; + } + if (hs != 0) di = 0; /* Broken surrogate pair? */ + if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object name? */ + fno->fname[di] = 0; /* Terminate the name */ + fno->altname[0] = 0; /* exFAT does not support SFN */ + + fno->fattrib = dirb[XDIR_Attr]; /* Attribute */ + fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + XDIR_FileSize); /* Size */ + fno->ftime = ld_word(dirb + XDIR_ModTime + 0); /* Time */ + fno->fdate = ld_word(dirb + XDIR_ModTime + 2); /* Date */ +} + +#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */ + + +/*-----------------------------------*/ +/* exFAT: Get a directry entry block */ +/*-----------------------------------*/ + +static FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ + DIR* dp /* Reading direcotry object pointing top of the entry block to load */ +) +{ + FRESULT res; + UINT i, sz_ent; + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */ + + + /* Load file-directory entry */ + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != ET_FILEDIR) return FR_INT_ERR; /* Invalid order */ + mem_cpy(dirb + 0 * SZDIRE, dp->dir, SZDIRE); + sz_ent = (dirb[XDIR_NumSec] + 1) * SZDIRE; + if (sz_ent < 3 * SZDIRE || sz_ent > 19 * SZDIRE) return FR_INT_ERR; + + /* Load stream-extension entry */ + res = dir_next(dp, 0); + if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */ + if (res != FR_OK) return res; + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != ET_STREAM) return FR_INT_ERR; /* Invalid order */ + mem_cpy(dirb + 1 * SZDIRE, dp->dir, SZDIRE); + if (MAXDIRB(dirb[XDIR_NumName]) > sz_ent) return FR_INT_ERR; + + /* Load file-name entries */ + i = 2 * SZDIRE; /* Name offset to load */ + do { + res = dir_next(dp, 0); + if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */ + if (res != FR_OK) return res; + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != ET_FILENAME) return FR_INT_ERR; /* Invalid order */ + if (i < MAXDIRB(FF_MAX_LFN)) mem_cpy(dirb + i, dp->dir, SZDIRE); + } while ((i += SZDIRE) < sz_ent); + + /* Sanity check (do it for only accessible object) */ + if (i <= MAXDIRB(FF_MAX_LFN)) { + if (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return FR_INT_ERR; + } + return FR_OK; +} + + +/*------------------------------------------------------------------*/ +/* exFAT: Initialize object allocation info with loaded entry block */ +/*------------------------------------------------------------------*/ + +static void init_alloc_info ( + FATFS* fs, /* Filesystem object */ + FFOBJID* obj /* Object allocation information to be initialized */ +) +{ + obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Start cluster */ + obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); /* Size */ + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; /* Allocation status */ + obj->n_frag = 0; /* No last fragment info */ +} + + + +#if !FF_FS_READONLY || FF_FS_RPATH != 0 +/*------------------------------------------------*/ +/* exFAT: Load the object's directory entry block */ +/*------------------------------------------------*/ + +static FRESULT load_obj_xdir ( + DIR* dp, /* Blank directory object to be used to access containing direcotry */ + const FFOBJID* obj /* Object with its containing directory information */ +) +{ + FRESULT res; + + /* Open object containing directory */ + dp->obj.fs = obj->fs; + dp->obj.sclust = obj->c_scl; + dp->obj.stat = (BYTE)obj->c_size; + dp->obj.objsize = obj->c_size & 0xFFFFFF00; + dp->obj.n_frag = 0; + dp->blk_ofs = obj->c_ofs; + + res = dir_sdi(dp, dp->blk_ofs); /* Goto object's entry block */ + if (res == FR_OK) { + res = load_xdir(dp); /* Load the object's entry block */ + } + return res; +} +#endif + + +#if !FF_FS_READONLY +/*----------------------------------------*/ +/* exFAT: Store the directory entry block */ +/*----------------------------------------*/ + +static FRESULT store_xdir ( + DIR* dp /* Pointer to the direcotry object */ +) +{ + FRESULT res; + UINT nent; + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry entry block 85+C0+C1s */ + + /* Create set sum */ + st_word(dirb + XDIR_SetSum, xdir_sum(dirb)); + nent = dirb[XDIR_NumSec] + 1; + + /* Store the direcotry entry block to the directory */ + res = dir_sdi(dp, dp->blk_ofs); + while (res == FR_OK) { + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) break; + mem_cpy(dp->dir, dirb, SZDIRE); + dp->obj.fs->wflag = 1; + if (--nent == 0) break; + dirb += SZDIRE; + res = dir_next(dp, 0); + } + return (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR; +} + + + +/*-------------------------------------------*/ +/* exFAT: Create a new directory enrty block */ +/*-------------------------------------------*/ + +static void create_xdir ( + BYTE* dirb, /* Pointer to the direcotry entry block buffer */ + const WCHAR* lfn /* Pointer to the object name */ +) +{ + UINT i; + BYTE nc1, nlen; + WCHAR wc; + + + /* Create file-directory and stream-extension entry */ + mem_set(dirb, 0, 2 * SZDIRE); + dirb[0 * SZDIRE + XDIR_Type] = ET_FILEDIR; + dirb[1 * SZDIRE + XDIR_Type] = ET_STREAM; + + /* Create file-name entries */ + i = SZDIRE * 2; /* Top of file_name entries */ + nlen = nc1 = 0; wc = 1; + do { + dirb[i++] = ET_FILENAME; dirb[i++] = 0; + do { /* Fill name field */ + if (wc != 0 && (wc = lfn[nlen]) != 0) nlen++; /* Get a character if exist */ + st_word(dirb + i, wc); /* Store it */ + i += 2; + } while (i % SZDIRE != 0); + nc1++; + } while (lfn[nlen]); /* Fill next entry if any char follows */ + + dirb[XDIR_NumName] = nlen; /* Set name length */ + dirb[XDIR_NumSec] = 1 + nc1; /* Set secondary count (C0 + C1s) */ + st_word(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name hash */ +} + +#endif /* !FF_FS_READONLY */ +#endif /* FF_FS_EXFAT */ + + + +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ + +#define DIR_READ_FILE(dp) dir_read(dp, 0) +#define DIR_READ_LABEL(dp) dir_read(dp, 1) + +static FRESULT dir_read ( + DIR* dp, /* Pointer to the directory object */ + int vol /* Filtered by 0:file/directory or 1:volume label */ +) +{ + FRESULT res = FR_NO_FILE; + FATFS *fs = dp->obj.fs; + BYTE attr, b; +#if FF_USE_LFN + BYTE ord = 0xFF, sum = 0xFF; +#endif + + while (dp->sect) { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + b = dp->dir[DIR_Name]; /* Test for the entry type */ + if (b == 0) { + res = FR_NO_FILE; break; /* Reached to end of the directory */ + } +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + if (FF_USE_LABEL && vol) { + if (b == ET_VLABEL) break; /* Volume label entry? */ + } else { + if (b == ET_FILEDIR) { /* Start of the file entry block? */ + dp->blk_ofs = dp->dptr; /* Get location of the block */ + res = load_xdir(dp); /* Load the entry block */ + if (res == FR_OK) { + dp->obj.attr = fs->dirbuf[XDIR_Attr] & AM_MASK; /* Get attribute */ + } + break; + } + } + } else +#endif + { /* On the FAT/FAT32 volume */ + dp->obj.attr = attr = dp->dir[DIR_Attr] & AM_MASK; /* Get attribute */ +#if FF_USE_LFN /* LFN configuration */ + if (b == DDEM || b == '.' || (int)((attr & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (attr == AM_LFN) { /* An LFN entry is found */ + if (b & LLEF) { /* Is it start of an LFN sequence? */ + sum = dp->dir[LDIR_Chksum]; + b &= (BYTE)~LLEF; ord = b; + dp->blk_ofs = dp->dptr; + } + /* Check LFN validity and capture it */ + ord = (b == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; + } else { /* An SFN entry is found */ + if (ord != 0 || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */ + dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */ + } + break; + } + } +#else /* Non LFN configuration */ + if (b != DDEM && b != '.' && attr != AM_LFN && (int)((attr & ~AM_ARC) == AM_VOL) == vol) { /* Is it a valid entry? */ + break; + } +#endif + } + res = dir_next(dp, 0); /* Next entry */ + if (res != FR_OK) break; + } + + if (res != FR_OK) dp->sect = 0; /* Terminate the read operation on error or EOT */ + return res; +} + +#endif /* FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_RPATH >= 2 */ + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Find an object in the directory */ +/*-----------------------------------------------------------------------*/ + +static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp /* Pointer to the directory object with the file name */ +) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; + BYTE c; +#if FF_USE_LFN + BYTE a, ord, sum; +#endif + + res = dir_sdi(dp, 0); /* Rewind directory object */ + if (res != FR_OK) return res; +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + BYTE nc; + UINT di, ni; + WORD hash = xname_sum(fs->lfnbuf); /* Hash value of the name to find */ + + while ((res = DIR_READ_FILE(dp)) == FR_OK) { /* Read an item */ +#if FF_MAX_LFN < 255 + if (fs->dirbuf[XDIR_NumName] > FF_MAX_LFN) continue; /* Skip comparison if inaccessible object name */ +#endif + if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) continue; /* Skip comparison if hash mismatched */ + for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) { /* Compare the name */ + if ((di % SZDIRE) == 0) di += 2; + if (ff_wtoupper(ld_word(fs->dirbuf + di)) != ff_wtoupper(fs->lfnbuf[ni])) break; + } + if (nc == 0 && !fs->lfnbuf[ni]) break; /* Name matched? */ + } + return res; + } +#endif + /* On the FAT/FAT32 volume */ +#if FF_USE_LFN + ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ +#endif + do { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + c = dp->dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ +#if FF_USE_LFN /* LFN configuration */ + dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; + if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (!(dp->fn[NSFLAG] & NS_NOLFN)) { + if (c & LLEF) { /* Is it start of LFN sequence? */ + sum = dp->dir[LDIR_Chksum]; + c &= (BYTE)~LLEF; ord = c; /* LFN start order */ + dp->blk_ofs = dp->dptr; /* Start offset of LFN */ + } + /* Check validity of the LFN entry and compare it with given name */ + ord = (c == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; + } + } else { /* An SFN entry is found */ + if (ord == 0 && sum == sum_sfn(dp->dir)) break; /* LFN matched? */ + if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */ + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ + } + } +#else /* Non LFN configuration */ + dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK; + if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* Is it a valid entry? */ +#endif + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Register an object to the directory */ +/*-----------------------------------------------------------------------*/ + +static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */ + DIR* dp /* Target directory with object name to be created */ +) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; +#if FF_USE_LFN /* LFN configuration */ + UINT n, len, n_ent; + BYTE sn[12], sum; + + + if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */ + for (len = 0; fs->lfnbuf[len]; len++) ; /* Get lfn length */ + +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + n_ent = (len + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */ + res = dir_alloc(dp, n_ent); /* Allocate directory entries */ + if (res != FR_OK) return res; + dp->blk_ofs = dp->dptr - SZDIRE * (n_ent - 1); /* Set the allocated entry block offset */ + + if (dp->obj.stat & 4) { /* Has the directory been stretched by new allocation? */ + dp->obj.stat &= ~4; + res = fill_first_frag(&dp->obj); /* Fill the first fragment on the FAT if needed */ + if (res != FR_OK) return res; + res = fill_last_frag(&dp->obj, dp->clust, 0xFFFFFFFF); /* Fill the last fragment on the FAT if needed */ + if (res != FR_OK) return res; + if (dp->obj.sclust != 0) { /* Is it a sub-directory? */ + DIR dj; + + res = load_obj_xdir(&dj, &dp->obj); /* Load the object status */ + if (res != FR_OK) return res; + dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase the directory size by cluster size */ + st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize); + st_qword(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize); + fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1; /* Update the allocation status */ + res = store_xdir(&dj); /* Store the object status */ + if (res != FR_OK) return res; + } + } + + create_xdir(fs->dirbuf, fs->lfnbuf); /* Create on-memory directory block to be written later */ + return FR_OK; + } +#endif + /* On the FAT/FAT32 volume */ + mem_cpy(sn, dp->fn, 12); + if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ + dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */ + for (n = 1; n < 100; n++) { + gen_numname(dp->fn, sn, fs->lfnbuf, n); /* Generate a numbered name */ + res = dir_find(dp); /* Check if the name collides with existing SFN */ + if (res != FR_OK) break; + } + if (n == 100) return FR_DENIED; /* Abort if too many collisions */ + if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */ + dp->fn[NSFLAG] = sn[NSFLAG]; + } + + /* Create an SFN with/without LFNs. */ + n_ent = (sn[NSFLAG] & NS_LFN) ? (len + 12) / 13 + 1 : 1; /* Number of entries to allocate */ + res = dir_alloc(dp, n_ent); /* Allocate entries */ + if (res == FR_OK && --n_ent) { /* Set LFN entry if needed */ + res = dir_sdi(dp, dp->dptr - n_ent * SZDIRE); + if (res == FR_OK) { + sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */ + do { /* Store LFN entries in bottom first */ + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + put_lfn(fs->lfnbuf, dp->dir, (BYTE)n_ent, sum); + fs->wflag = 1; + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK && --n_ent); + } + } + +#else /* Non LFN configuration */ + res = dir_alloc(dp, 1); /* Allocate an entry for SFN */ + +#endif + + /* Set SFN entry */ + if (res == FR_OK) { + res = move_window(fs, dp->sect); + if (res == FR_OK) { + mem_set(dp->dir, 0, SZDIRE); /* Clean the entry */ + mem_cpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */ +#if FF_USE_LFN + dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */ +#endif + fs->wflag = 1; + } + } + + return res; +} + +#endif /* !FF_FS_READONLY */ + + + +#if !FF_FS_READONLY && FF_FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Remove an object from the directory */ +/*-----------------------------------------------------------------------*/ + +static FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */ + DIR* dp /* Directory object pointing the entry to be removed */ +) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; +#if FF_USE_LFN /* LFN configuration */ + DWORD last = dp->dptr; + + res = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, dp->blk_ofs); /* Goto top of the entry block if LFN is exist */ + if (res == FR_OK) { + do { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + dp->dir[XDIR_Type] &= 0x7F; /* Clear the entry InUse flag. */ + } else { /* On the FAT/FAT32 volume */ + dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'. */ + } + fs->wflag = 1; + if (dp->dptr >= last) break; /* If reached last entry then all entries of the object has been deleted. */ + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR; + } +#else /* Non LFN configuration */ + + res = move_window(fs, dp->sect); + if (res == FR_OK) { + dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'.*/ + fs->wflag = 1; + } +#endif + + return res; +} + +#endif /* !FF_FS_READONLY && FF_FS_MINIMIZE == 0 */ + + + +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 +/*-----------------------------------------------------------------------*/ +/* Get file information from directory entry */ +/*-----------------------------------------------------------------------*/ + +static void get_fileinfo ( + DIR* dp, /* Pointer to the directory object */ + FILINFO* fno /* Pointer to the file information to be filled */ +) +{ + UINT si, di; +#if FF_USE_LFN + BYTE lcf; + WCHAR wc, hs; + FATFS *fs = dp->obj.fs; +#else + TCHAR c; +#endif + + + fno->fname[0] = 0; /* Invaidate file info */ + if (dp->sect == 0) return; /* Exit if read pointer has reached end of directory */ + +#if FF_USE_LFN /* LFN configuration */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + get_xfileinfo(fs->dirbuf, fno); + return; + } else +#endif + { /* On the FAT/FAT32 volume */ + if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */ + si = di = hs = 0; + while (fs->lfnbuf[si] != 0) { + wc = fs->lfnbuf[si++]; /* Get an LFN character (UTF-16) */ + if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ + hs = wc; continue; /* Get low surrogate */ + } + wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in UTF-16 or UTF-8 encoding */ + if (wc == 0) { di = 0; break; } /* Invalid char or buffer overflow? */ + di += wc; + hs = 0; + } + if (hs != 0) di = 0; /* Broken surrogate pair? */ + fno->fname[di] = 0; /* Terminate the LFN (null string means LFN is invalid) */ + } + } + + si = di = 0; + while (si < 11) { /* Get SFN from SFN entry */ + wc = dp->dir[si++]; /* Get a char */ + if (wc == ' ') continue; /* Skip padding spaces */ + if (wc == RDDEM) wc = DDEM; /* Restore replaced DDEM character */ + if (si == 9 && di < FF_SFN_BUF) fno->altname[di++] = '.'; /* Insert a . if extension is exist */ +#if FF_LFN_UNICODE >= 1 /* Unicode output */ + if (dbc_1st((BYTE)wc) && si != 8 && si != 11 && dbc_2nd(dp->dir[si])) { /* Make a DBC if needed */ + wc = wc << 8 | dp->dir[si++]; + } + wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM -> Unicode */ + if (wc == 0) { di = 0; break; } /* Wrong char in the current code page? */ + wc = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di); /* Store it in Unicode */ + if (wc == 0) { di = 0; break; } /* Buffer overflow? */ + di += wc; +#else /* ANSI/OEM output */ + fno->altname[di++] = (TCHAR)wc; /* Store it without any conversion */ +#endif + } + fno->altname[di] = 0; /* Terminate the SFN (null string means SFN is invalid) */ + + if (fno->fname[0] == 0) { /* If LFN is invalid, altname[] needs to be copied to fname[] */ + if (di == 0) { /* If LFN and SFN both are invalid, this object is inaccesible */ + fno->fname[di++] = '?'; + } else { + for (si = di = 0, lcf = NS_BODY; fno->altname[si]; si++, di++) { /* Copy altname[] to fname[] with case information */ + wc = (WCHAR)fno->altname[si]; + if (wc == '.') lcf = NS_EXT; + if (IsUpper(wc) && (dp->dir[DIR_NTres] & lcf)) wc += 0x20; + fno->fname[di] = (TCHAR)wc; + } + } + fno->fname[di] = 0; /* Terminate the LFN */ + if (!dp->dir[DIR_NTres]) fno->altname[0] = 0; /* Altname is not needed if neither LFN nor case info is exist. */ + } + +#else /* Non-LFN configuration */ + si = di = 0; + while (si < 11) { /* Copy name body and extension */ + c = (TCHAR)dp->dir[si++]; + if (c == ' ') continue; /* Skip padding spaces */ + if (c == RDDEM) c = DDEM; /* Restore replaced DDEM character */ + if (si == 9) fno->fname[di++] = '.';/* Insert a . if extension is exist */ + fno->fname[di++] = c; + } + fno->fname[di] = 0; +#endif + + fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */ + fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ + fno->ftime = ld_word(dp->dir + DIR_ModTime + 0); /* Time */ + fno->fdate = ld_word(dp->dir + DIR_ModTime + 2); /* Date */ +} + +#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */ + + + +#if FF_USE_FIND && FF_FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Pattern matching */ +/*-----------------------------------------------------------------------*/ + +#define FIND_RECURS 4 /* Maximum number of wildcard terms in the pattern to limit recursion */ + + +static DWORD get_achar ( /* Get a character and advance ptr */ + const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */ +) +{ + DWORD chr; + + +#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode input */ + chr = tchar2uni(ptr); + if (chr == 0xFFFFFFFF) chr = 0; /* Wrong UTF encoding is recognized as end of the string */ + chr = ff_wtoupper(chr); + +#else /* ANSI/OEM input */ + chr = (BYTE)*(*ptr)++; /* Get a byte */ + if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */ +#if FF_CODE_PAGE == 0 + if (ExCvt && chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */ +#elif FF_CODE_PAGE < 900 + if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */ +#endif +#if FF_CODE_PAGE == 0 || FF_CODE_PAGE >= 900 + if (dbc_1st((BYTE)chr)) { /* Get DBC 2nd byte if needed */ + chr = dbc_2nd((BYTE)**ptr) ? chr << 8 | (BYTE)*(*ptr)++ : 0; + } +#endif + +#endif + return chr; +} + + +static int pattern_match ( /* 0:mismatched, 1:matched */ + const TCHAR* pat, /* Matching pattern */ + const TCHAR* nam, /* String to be tested */ + UINT skip, /* Number of pre-skip chars (number of ?s, b8:infinite (* specified)) */ + UINT recur /* Recursion count */ +) +{ + const TCHAR *pptr, *nptr; + DWORD pchr, nchr; + UINT sk; + + + while ((skip & 0xFF) != 0) { /* Pre-skip name chars */ + if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */ + skip--; + } + if (*pat == 0 && skip) return 1; /* Matched? (short circuit) */ + + do { + pptr = pat; nptr = nam; /* Top of pattern and name to match */ + for (;;) { + if (*pptr == '?' || *pptr == '*') { /* Wildcard term? */ + if (recur == 0) return 0; /* Too many wildcard terms? */ + sk = 0; + do { /* Analyze the wildcard term */ + if (*pptr++ == '?') sk++; else sk |= 0x100; + } while (*pptr == '?' || *pptr == '*'); + if (pattern_match(pptr, nptr, sk, recur - 1)) return 1; /* Test new branch (recursive call) */ + nchr = *nptr; break; /* Branch mismatched */ + } + pchr = get_achar(&pptr); /* Get a pattern char */ + nchr = get_achar(&nptr); /* Get a name char */ + if (pchr != nchr) break; /* Branch mismatched? */ + if (pchr == 0) return 1; /* Branch matched? (matched at end of both strings) */ + } + get_achar(&nam); /* nam++ */ + } while (skip && nchr); /* Retry until end of name if infinite search is specified */ + + return 0; +} + +#endif /* FF_USE_FIND && FF_FS_MINIMIZE <= 1 */ + + + +/*-----------------------------------------------------------------------*/ +/* Pick a top segment and create the object name in directory form */ +/*-----------------------------------------------------------------------*/ + +static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ + DIR* dp, /* Pointer to the directory object */ + const TCHAR** path /* Pointer to pointer to the segment in the path string */ +) +{ +#if FF_USE_LFN /* LFN configuration */ + BYTE b, cf; + WCHAR wc, *lfn; + DWORD uc; + UINT i, ni, si, di; + const TCHAR *p; + + + /* Create LFN into LFN working buffer */ + p = *path; lfn = dp->obj.fs->lfnbuf; di = 0; + for (;;) { + uc = tchar2uni(&p); /* Get a character */ + if (uc == 0xFFFFFFFF) return FR_INVALID_NAME; /* Invalid code or UTF decode error */ + if (uc >= 0x10000) lfn[di++] = (WCHAR)(uc >> 16); /* Store high surrogate if needed */ + wc = (WCHAR)uc; + if (wc < ' ' || wc == '/' || wc == '\\') break; /* Break if end of the path or a separator is found */ + if (wc < 0x80 && chk_chr("\"*:<>\?|\x7F", wc)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */ + if (di >= FF_MAX_LFN) return FR_INVALID_NAME; /* Reject too long name */ + lfn[di++] = wc; /* Store the Unicode character */ + } + if (wc < ' ') { /* End of path? */ + cf = NS_LAST; /* Set last segment flag */ + } else { + cf = 0; /* Next segment follows */ + while (*p == '/' || *p == '\\') p++; /* Skip duplicated separators if exist */ + } + *path = p; /* Return pointer to the next segment */ + +#if FF_FS_RPATH != 0 + if ((di == 1 && lfn[di - 1] == '.') || + (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot name? */ + lfn[di] = 0; + for (i = 0; i < 11; i++) { /* Create dot name for SFN entry */ + dp->fn[i] = (i < di) ? '.' : ' '; + } + dp->fn[i] = cf | NS_DOT; /* This is a dot entry */ + return FR_OK; + } +#endif + while (di) { /* Snip off trailing spaces and dots if exist */ + wc = lfn[di - 1]; + if (wc != ' ' && wc != '.') break; + di--; + } + lfn[di] = 0; /* LFN is created into the working buffer */ + if (di == 0) return FR_INVALID_NAME; /* Reject null name */ + + /* Create SFN in directory form */ + for (si = 0; lfn[si] == ' '; si++) ; /* Remove leading spaces */ + if (si > 0 || lfn[si] == '.') cf |= NS_LOSS | NS_LFN; /* Is there any leading space or dot? */ + while (di > 0 && lfn[di - 1] != '.') di--; /* Find last dot (di<=si: no extension) */ + + mem_set(dp->fn, ' ', 11); + i = b = 0; ni = 8; + for (;;) { + wc = lfn[si++]; /* Get an LFN character */ + if (wc == 0) break; /* Break on end of the LFN */ + if (wc == ' ' || (wc == '.' && si != di)) { /* Remove embedded spaces and dots */ + cf |= NS_LOSS | NS_LFN; + continue; + } + + if (i >= ni || si == di) { /* End of field? */ + if (ni == 11) { /* Name extension overflow? */ + cf |= NS_LOSS | NS_LFN; + break; + } + if (si != di) cf |= NS_LOSS | NS_LFN; /* Name body overflow? */ + if (si > di) break; /* No name extension? */ + si = di; i = 8; ni = 11; b <<= 2; /* Enter name extension */ + continue; + } + + if (wc >= 0x80) { /* Is this a non-ASCII character? */ + cf |= NS_LFN; /* LFN entry needs to be created */ +#if FF_CODE_PAGE == 0 + if (ExCvt) { /* At SBCS */ + wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */ + if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */ + } else { /* At DBCS */ + wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Upper convert ==> ANSI/OEM code */ + } +#elif FF_CODE_PAGE < 900 /* SBCS cfg */ + wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */ + if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */ +#else /* DBCS cfg */ + wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Upper convert ==> ANSI/OEM code */ +#endif + } + + if (wc >= 0x100) { /* Is this a DBC? */ + if (i >= ni - 1) { /* Field overflow? */ + cf |= NS_LOSS | NS_LFN; + i = ni; continue; /* Next field */ + } + dp->fn[i++] = (BYTE)(wc >> 8); /* Put 1st byte */ + } else { /* SBC */ + if (wc == 0 || chk_chr("+,;=[]", wc)) { /* Replace illegal characters for SFN if needed */ + wc = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ + } else { + if (IsUpper(wc)) { /* ASCII upper case? */ + b |= 2; + } + if (IsLower(wc)) { /* ASCII lower case? */ + b |= 1; wc -= 0x20; + } + } + } + dp->fn[i++] = (BYTE)wc; + } + + if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ + + if (ni == 8) b <<= 2; /* Shift capital flags if no extension */ + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; /* LFN entry needs to be created if composite capitals */ + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */ + if (b & 0x01) cf |= NS_EXT; /* NT flag (Extension has small capital letters only) */ + if (b & 0x04) cf |= NS_BODY; /* NT flag (Body has small capital letters only) */ + } + + dp->fn[NSFLAG] = cf; /* SFN is created into dp->fn[] */ + + return FR_OK; + + +#else /* FF_USE_LFN : Non-LFN configuration */ + BYTE c, d, *sfn; + UINT ni, si, i; + const char *p; + + /* Create file name in directory form */ + p = *path; sfn = dp->fn; + mem_set(sfn, ' ', 11); + si = i = 0; ni = 8; +#if FF_FS_RPATH != 0 + if (p[si] == '.') { /* Is this a dot entry? */ + for (;;) { + c = (BYTE)p[si++]; + if (c != '.' || si >= 3) break; + sfn[i++] = c; + } + if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; + *path = p + si; /* Return pointer to the next segment */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of the path */ + return FR_OK; + } +#endif + for (;;) { + c = (BYTE)p[si++]; /* Get a byte */ + if (c <= ' ') break; /* Break if end of the path name */ + if (c == '/' || c == '\\') { /* Break if a separator is found */ + while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ + break; + } + if (c == '.' || i >= ni) { /* End of body or field overflow? */ + if (ni == 11 || c != '.') return FR_INVALID_NAME; /* Field overflow or invalid dot? */ + i = 8; ni = 11; /* Enter file extension field */ + continue; + } +#if FF_CODE_PAGE == 0 + if (ExCvt && c >= 0x80) { /* Is SBC extended character? */ + c = ExCvt[c & 0x7F]; /* To upper SBC extended character */ + } +#elif FF_CODE_PAGE < 900 + if (c >= 0x80) { /* Is SBC extended character? */ + c = ExCvt[c & 0x7F]; /* To upper SBC extended character */ + } +#endif + if (dbc_1st(c)) { /* Check if it is a DBC 1st byte */ + d = (BYTE)p[si++]; /* Get 2nd byte */ + if (!dbc_2nd(d) || i >= ni - 1) return FR_INVALID_NAME; /* Reject invalid DBC */ + sfn[i++] = c; + sfn[i++] = d; + } else { /* SBC */ + if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */ + if (IsLower(c)) c -= 0x20; /* To upper */ + sfn[i++] = c; + } + } + *path = p + si; /* Return pointer to the next segment */ + if (i == 0) return FR_INVALID_NAME; /* Reject nul string */ + + if (sfn[0] == DDEM) sfn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ + + return FR_OK; +#endif /* FF_USE_LFN */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Follow a file path */ +/*-----------------------------------------------------------------------*/ + +static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ + DIR* dp, /* Directory object to return last directory and found object */ + const TCHAR* path /* Full-path string to find a file or directory */ +) +{ + FRESULT res; + BYTE ns; + FATFS *fs = dp->obj.fs; + + +#if FF_FS_RPATH != 0 + if (*path != '/' && *path != '\\') { /* Without heading separator */ + dp->obj.sclust = fs->cdir; /* Start from current directory */ + } else +#endif + { /* With heading separator */ + while (*path == '/' || *path == '\\') path++; /* Strip heading separator */ + dp->obj.sclust = 0; /* Start from root directory */ + } +#if FF_FS_EXFAT + dp->obj.n_frag = 0; /* Invalidate last fragment counter of the object */ +#if FF_FS_RPATH != 0 + if (fs->fs_type == FS_EXFAT && dp->obj.sclust) { /* exFAT: Retrieve the sub-directory's status */ + DIR dj; + + dp->obj.c_scl = fs->cdc_scl; + dp->obj.c_size = fs->cdc_size; + dp->obj.c_ofs = fs->cdc_ofs; + res = load_obj_xdir(&dj, &dp->obj); + if (res != FR_OK) return res; + dp->obj.objsize = ld_dword(fs->dirbuf + XDIR_FileSize); + dp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + } +#endif +#endif + + if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */ + dp->fn[NSFLAG] = NS_NONAME; + res = dir_sdi(dp, 0); + + } else { /* Follow path */ + for (;;) { + res = create_name(dp, &path); /* Get a segment name of the path */ + if (res != FR_OK) break; + res = dir_find(dp); /* Find an object with the segment name */ + ns = dp->fn[NSFLAG]; + if (res != FR_OK) { /* Failed to find the object */ + if (res == FR_NO_FILE) { /* Object is not found */ + if (FF_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, stay there */ + if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */ + dp->fn[NSFLAG] = NS_NONAME; + res = FR_OK; + } else { /* Could not find the object */ + if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */ + } + } + break; + } + if (ns & NS_LAST) break; /* Last segment matched. Function completed. */ + /* Get into the sub-directory */ + if (!(dp->obj.attr & AM_DIR)) { /* It is not a sub-directory and cannot follow */ + res = FR_NO_PATH; break; + } +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* Save containing directory information for next dir */ + dp->obj.c_scl = dp->obj.sclust; + dp->obj.c_size = ((DWORD)dp->obj.objsize & 0xFFFFFF00) | dp->obj.stat; + dp->obj.c_ofs = dp->blk_ofs; + init_alloc_info(fs, &dp->obj); /* Open next directory */ + } else +#endif + { + dp->obj.sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs)); /* Open next directory */ + } + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get logical drive number from path name */ +/*-----------------------------------------------------------------------*/ + +static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive number or null pointer) */ + const TCHAR** path /* Pointer to pointer to the path name */ +) +{ + const TCHAR *tp, *tt; + TCHAR tc; + int i, vol = -1; +#if FF_STR_VOLUME_ID /* Find string volume ID */ + const char *sp; + char c; +#endif + + tt = tp = *path; + if (!tp) return vol; /* Invalid path name? */ + do tc = *tt++; while ((UINT)tc >= (FF_USE_LFN ? ' ' : '!') && tc != ':'); /* Find a colon in the path */ + + if (tc == ':') { /* DOS/Windows style volume ID? */ + i = FF_VOLUMES; + if (IsDigit(*tp) && tp + 2 == tt) { /* Is there a numeric volume ID + colon? */ + i = (int)*tp - '0'; /* Get the LD number */ + } +#if FF_STR_VOLUME_ID == 1 /* Arbitrary string is enabled */ + else { + i = 0; + do { + sp = VolumeStr[i]; tp = *path; /* This string volume ID and path name */ + do { /* Compare the volume ID with path name */ + c = *sp++; tc = *tp++; + if (IsLower(c)) c -= 0x20; + if (IsLower(tc)) tc -= 0x20; + } while (c && (TCHAR)c == tc); + } while ((c || tp != tt) && ++i < FF_VOLUMES); /* Repeat for each id until pattern match */ + } +#endif + if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */ + vol = i; /* Drive number */ + *path = tt; /* Snip the drive prefix off */ + } + return vol; + } +#if FF_STR_VOLUME_ID == 2 /* Unix style volume ID is enabled */ + if (*tp == '/') { + i = 0; + do { + sp = VolumeStr[i]; tp = *path; /* This string volume ID and path name */ + do { /* Compare the volume ID with path name */ + c = *sp++; tc = *(++tp); + if (IsLower(c)) c -= 0x20; + if (IsLower(tc)) tc -= 0x20; + } while (c && (TCHAR)c == tc); + } while ((c || (tc != '/' && (UINT)tc >= (FF_USE_LFN ? ' ' : '!'))) && ++i < FF_VOLUMES); /* Repeat for each ID until pattern match */ + if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */ + vol = i; /* Drive number */ + *path = tp; /* Snip the drive prefix off */ + return vol; + } + } +#endif + /* No drive prefix is found */ +#if FF_FS_RPATH != 0 + vol = CurrVol; /* Default drive is current drive */ +#else + vol = 0; /* Default drive is 0 */ +#endif + return vol; /* Return the default drive */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* GPT support functions */ +/*-----------------------------------------------------------------------*/ + +#if FF_LBA64 + +/* Calculate CRC32 in byte-by-byte */ + +static DWORD crc32 ( /* Returns next CRC value */ + DWORD crc, /* Current CRC value */ + BYTE d /* A byte to be processed */ +) +{ + BYTE b; + + + for (b = 1; b; b <<= 1) { + crc ^= (d & b) ? 1 : 0; + crc = (crc & 1) ? crc >> 1 ^ 0xEDB88320 : crc >> 1; + } + return crc; +} + + +/* Check validity of GPT header */ + +static int test_gpt_header ( /* 0:Invalid, 1:Valid */ + const BYTE* gpth /* Pointer to the GPT header */ +) +{ + UINT i; + DWORD bcc; + + + if (mem_cmp(gpth + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", 16)) return 0; /* Check sign, version (1.0) and length (92) */ + for (i = 0, bcc = 0xFFFFFFFF; i < 92; i++) { /* Check header BCC */ + bcc = crc32(bcc, i - GPTH_Bcc < 4 ? 0 : gpth[i]); + } + if (~bcc != ld_dword(gpth + GPTH_Bcc)) return 0; + if (ld_dword(gpth + GPTH_PteSize) != SZ_GPTE) return 0; /* Table entry size (must be SZ_GPTE bytes) */ + if (ld_dword(gpth + GPTH_PtNum) > 128) return 0; /* Table size (must be 128 entries or less) */ + + return 1; +} + +#if !FF_FS_READONLY && FF_USE_MKFS + +/* Generate random value */ +static DWORD make_rand ( + DWORD seed, /* Seed value */ + BYTE* buff, /* Output buffer */ + UINT n /* Data length */ +) +{ + UINT r; + + + if (seed == 0) seed = 1; + do { + for (r = 0; r < 8; r++) seed = seed & 1 ? seed >> 1 ^ 0xA3000000 : seed >> 1; /* Shift 8 bits the 32-bit LFSR */ + *buff++ = (BYTE)seed; + } while (--n); + return seed; +} + +#endif +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Load a sector and check if it is an FAT VBR */ +/*-----------------------------------------------------------------------*/ + +/* Check what the sector is */ + +static UINT check_fs ( /* 0:FAT VBR, 1:exFAT VBR, 2:Not FAT and valid BS, 3:Not FAT and invalid BS, 4:Disk error */ + FATFS* fs, /* Filesystem object */ + LBA_t sect /* Sector to load and check if it is an FAT-VBR or not */ +) +{ + WORD w, sign; + BYTE b; + + + fs->wflag = 0; fs->winsect = (LBA_t)0 - 1; /* Invaidate window */ + if (move_window(fs, sect) != FR_OK) return 4; /* Load the boot sector */ + sign = ld_word(fs->win + BS_55AA); +#if FF_FS_EXFAT + if (sign == 0xAA55 && !mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* It is an exFAT VBR */ +#endif + b = fs->win[BS_JmpBoot]; + if (b == 0xEB || b == 0xE9 || b == 0xE8) { /* Valid JumpBoot code? (short jump, near jump or near call) */ + if (sign == 0xAA55 && !mem_cmp(fs->win + BS_FilSysType32, "FAT32 ", 8)) return 0; /* It is an FAT32 VBR */ + /* FAT volumes formatted with early MS-DOS lack boot signature and FAT string, so that we need to identify the FAT VBR without them. */ + w = ld_word(fs->win + BPB_BytsPerSec); + if ((w & (w - 1)) == 0 && w >= FF_MIN_SS && w <= FF_MAX_SS) { /* Properness of sector size */ + b = fs->win[BPB_SecPerClus]; + if (b != 0 && (b & (b - 1)) == 0 /* Properness of cluster size */ + && (fs->win[BPB_NumFATs] == 1 || fs->win[BPB_NumFATs] == 2) /* Properness of number of FATs */ + && ld_word(fs->win + BPB_RootEntCnt) != 0 /* Properness of root entry count */ + && ld_word(fs->win + BPB_FATSz16) != 0) { /* Properness of FAT size */ + return 0; /* Sector can be presumed an FAT VBR */ + } + } + } + return sign == 0xAA55 ? 2 : 3; /* Not an FAT VBR (valid or invalid BS) */ +} + + +/* Find an FAT volume */ +/* (It supports only generic partitioning rules, MBR, GPT and SFD) */ + +static UINT find_volume ( /* Returns BS status found in the hosting drive */ + FATFS* fs, /* Filesystem object */ + UINT part /* Partition to fined = 0:auto, 1..:forced */ +) +{ + UINT fmt, i; + DWORD mbr_pt[4]; + + + fmt = check_fs(fs, 0); /* Load sector 0 and check if it is an FAT VBR as SFD */ + if (fmt != 2 && (fmt >= 3 || part == 0)) return fmt; /* Returns if it is a FAT VBR as auto scan, not a BS or disk error */ + + /* Sector 0 is not an FAT VBR or forced partition number wants a partition */ + +#if FF_LBA64 + if (fs->win[MBR_Table + PTE_System] == 0xEE) { /* GPT protective MBR? */ + DWORD n_ent, v_ent, ofs; + QWORD pt_lba; + + if (move_window(fs, 1) != FR_OK) return 4; /* Load GPT header sector (next to MBR) */ + if (!test_gpt_header(fs->win)) return 3; /* Check if GPT header is valid */ + n_ent = ld_dword(fs->win + GPTH_PtNum); /* Number of entries */ + pt_lba = ld_qword(fs->win + GPTH_PtOfs); /* Table location */ + for (v_ent = i = 0; i < n_ent; i++) { /* Find FAT partition */ + if (move_window(fs, pt_lba + i * SZ_GPTE / SS(fs)) != FR_OK) return 4; /* PT sector */ + ofs = i * SZ_GPTE % SS(fs); /* Offset in the sector */ + if (!mem_cmp(fs->win + ofs + GPTE_PtGuid, GUID_MS_Basic, 16)) { /* MS basic data partition? */ + v_ent++; + fmt = check_fs(fs, ld_qword(fs->win + ofs + GPTE_FstLba)); /* Load VBR and check status */ + if (part == 0 && fmt <= 1) return fmt; /* Auto search (valid FAT volume found first) */ + if (part != 0 && v_ent == part) return fmt; /* Forced partition order (regardless of it is valid or not) */ + } + } + return 3; /* Not found */ + } +#endif + if (FF_MULTI_PARTITION && part > 4) return 3; /* MBR has 4 partitions max */ + for (i = 0; i < 4; i++) { /* Load partition offset in the MBR */ + mbr_pt[i] = ld_dword(fs->win + MBR_Table + i * SZ_PTE + PTE_StLba); + } + i = part ? part - 1 : 0; /* Table index to find first */ + do { /* Find an FAT volume */ + fmt = mbr_pt[i] ? check_fs(fs, mbr_pt[i]) : 3; /* Check if the partition is FAT */ + } while (part == 0 && fmt >= 2 && ++i < 4); + return fmt; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Determine logical drive number and mount the volume if needed */ +/*-----------------------------------------------------------------------*/ + +static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ + const TCHAR** path, /* Pointer to pointer to the path name (drive number) */ + FATFS** rfs, /* Pointer to pointer to the found filesystem object */ + BYTE mode /* !=0: Check write protection for write access */ +) +{ + int vol; + DSTATUS stat; + LBA_t bsect; + DWORD tsect, sysect, fasize, nclst, szbfat; + WORD nrsv; + FATFS *fs; + UINT fmt; + + + /* Get logical drive number */ + *rfs = 0; + vol = get_ldnumber(path); + if (vol < 0) return FR_INVALID_DRIVE; + + /* Check if the filesystem object is valid or not */ + fs = FatFs[vol]; /* Get pointer to the filesystem object */ + if (!fs) return FR_NOT_ENABLED; /* Is the filesystem object available? */ +#if FF_FS_REENTRANT + if (!lock_fs(fs)) return FR_TIMEOUT; /* Lock the volume */ +#endif + *rfs = fs; /* Return pointer to the filesystem object */ + + mode &= (BYTE)~FA_READ; /* Desired access mode, write access or not */ + if (fs->fs_type != 0) { /* If the volume has been mounted */ + stat = disk_status(fs->pdrv); + if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */ + if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check write protection if needed */ + return FR_WRITE_PROTECTED; + } + return FR_OK; /* The filesystem object is already valid */ + } + } + + /* The filesystem object is not valid. */ + /* Following code attempts to mount the volume. (find a FAT volume, analyze the BPB and initialize the filesystem object) */ + + fs->fs_type = 0; /* Clear the filesystem object */ + fs->pdrv = LD2PD(vol); /* Volume hosting physical drive */ + stat = disk_initialize(fs->pdrv); /* Initialize the physical drive */ + if (stat & STA_NOINIT) { /* Check if the initialization succeeded */ + return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */ + } + if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check disk write protection if needed */ + return FR_WRITE_PROTECTED; + } +#if FF_MAX_SS != FF_MIN_SS /* Get sector size (multiple sector size cfg only) */ + if (disk_ioctl(fs->pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) return FR_DISK_ERR; + if (SS(fs) > FF_MAX_SS || SS(fs) < FF_MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR; +#endif + + /* Find an FAT volume on the drive */ + fmt = find_volume(fs, LD2PT(vol)); + if (fmt == 4) return FR_DISK_ERR; /* An error occured in the disk I/O layer */ + if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is found */ + bsect = fs->winsect; /* Volume location */ + + /* An FAT volume is found (bsect). Following code initializes the filesystem object */ + +#if FF_FS_EXFAT + if (fmt == 1) { + QWORD maxlba; + DWORD so, cv, bcl, i; + + for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */ + if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM; + + if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT version (must be version 1.0) */ + + if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */ + return FR_NO_FILESYSTEM; + } + + maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA + 1 of the volume */ + if (!FF_LBA64 && maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */ + + fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */ + + fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of FATs */ + if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* (Supports only 1 FAT) */ + + fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */ + if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768) */ + + nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */ + if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */ + fs->n_fatent = nclst + 2; + + /* Boundaries and Limits */ + fs->volbase = bsect; + fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx); + fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx); + if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */ + fs->dirbase = ld_dword(fs->win + BPB_RootClusEx); + + /* Get bitmap location and check if it is contiguous (implementation assumption) */ + so = i = 0; + for (;;) { /* Find the bitmap entry in the root directory (in only first cluster) */ + if (i == 0) { + if (so >= fs->csize) return FR_NO_FILESYSTEM; /* Not found? */ + if (move_window(fs, clst2sect(fs, (DWORD)fs->dirbase) + so) != FR_OK) return FR_DISK_ERR; + so++; + } + if (fs->win[i] == ET_BITMAP) break; /* Is it a bitmap entry? */ + i = (i + SZDIRE) % SS(fs); /* Next entry */ + } + bcl = ld_dword(fs->win + i + 20); /* Bitmap cluster */ + if (bcl < 2 || bcl >= fs->n_fatent) return FR_NO_FILESYSTEM; + fs->bitbase = fs->database + fs->csize * (bcl - 2); /* Bitmap sector */ + for (;;) { /* Check if bitmap is contiguous */ + if (move_window(fs, fs->fatbase + bcl / (SS(fs) / 4)) != FR_OK) return FR_DISK_ERR; + cv = ld_dword(fs->win + bcl % (SS(fs) / 4) * 4); + if (cv == 0xFFFFFFFF) break; /* Last link? */ + if (cv != ++bcl) return FR_NO_FILESYSTEM; /* Fragmented? */ + } + +#if !FF_FS_READONLY + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ +#endif + fmt = FS_EXFAT; /* FAT sub-type */ + } else +#endif /* FF_FS_EXFAT */ + { + if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */ + + fasize = ld_word(fs->win + BPB_FATSz16); /* Number of sectors per FAT */ + if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32); + fs->fsize = fasize; + + fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FATs */ + if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */ + fasize *= fs->n_fats; /* Number of sectors for FAT area */ + + fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */ + if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */ + + fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* Number of root directory entries */ + if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */ + + tsect = ld_word(fs->win + BPB_TotSec16); /* Number of sectors on the volume */ + if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32); + + nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */ + if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must not be 0) */ + + /* Determine the FAT sub type */ + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* RSV + FAT + DIR */ + if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ + if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + fmt = 0; + if (nclst <= MAX_FAT32) fmt = FS_FAT32; + if (nclst <= MAX_FAT16) fmt = FS_FAT16; + if (nclst <= MAX_FAT12) fmt = FS_FAT12; + if (fmt == 0) return FR_NO_FILESYSTEM; + + /* Boundaries and Limits */ + fs->n_fatent = nclst + 2; /* Number of FAT entries */ + fs->volbase = bsect; /* Volume start sector */ + fs->fatbase = bsect + nrsv; /* FAT start sector */ + fs->database = bsect + sysect; /* Data start sector */ + if (fmt == FS_FAT32) { + if (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */ + if (fs->n_rootdir != 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ + fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* Root directory start cluster */ + szbfat = fs->n_fatent * 4; /* (Needed FAT size) */ + } else { + if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */ + fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */ + szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */ + fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); + } + if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size needed) */ + +#if !FF_FS_READONLY + /* Get FSInfo if available */ + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ + fs->fsi_flag = 0x80; +#if (FF_FS_NOFSINFO & 3) != 3 + if (fmt == FS_FAT32 /* Allow to update FSInfo only if BPB_FSInfo32 == 1 */ + && ld_word(fs->win + BPB_FSInfo32) == 1 + && move_window(fs, bsect + 1) == FR_OK) + { + fs->fsi_flag = 0; + if (ld_word(fs->win + BS_55AA) == 0xAA55 /* Load FSInfo data if available */ + && ld_dword(fs->win + FSI_LeadSig) == 0x41615252 + && ld_dword(fs->win + FSI_StrucSig) == 0x61417272) + { +#if (FF_FS_NOFSINFO & 1) == 0 + fs->free_clst = ld_dword(fs->win + FSI_Free_Count); +#endif +#if (FF_FS_NOFSINFO & 2) == 0 + fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free); +#endif + } + } +#endif /* (FF_FS_NOFSINFO & 3) != 3 */ +#endif /* !FF_FS_READONLY */ + } + + fs->fs_type = (BYTE)fmt;/* FAT sub-type */ + fs->id = ++Fsid; /* Volume mount ID */ +#if FF_USE_LFN == 1 + fs->lfnbuf = LfnBuf; /* Static LFN working buffer */ +#if FF_FS_EXFAT + fs->dirbuf = DirBuf; /* Static directory block scratchpad buuffer */ +#endif +#endif +#if FF_FS_RPATH != 0 + fs->cdir = 0; /* Initialize current directory */ +#endif +#if FF_FS_LOCK != 0 /* Clear file lock semaphores */ + clear_lock(fs); +#endif + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file/directory object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ + FFOBJID* obj, /* Pointer to the FFOBJID, the 1st member in the FIL/DIR object, to check validity */ + FATFS** rfs /* Pointer to pointer to the owner filesystem object to return */ +) +{ + FRESULT res = FR_INVALID_OBJECT; + + + if (obj && obj->fs && obj->fs->fs_type && obj->id == obj->fs->id) { /* Test if the object is valid */ +#if FF_FS_REENTRANT + if (lock_fs(obj->fs)) { /* Obtain the filesystem object */ + if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */ + res = FR_OK; + } else { + unlock_fs(obj->fs, FR_OK); + } + } else { + res = FR_TIMEOUT; + } +#else + if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */ + res = FR_OK; + } +#endif + } + *rfs = (res == FR_OK) ? obj->fs : 0; /* Corresponding filesystem object */ + return res; +} + + + + +/*--------------------------------------------------------------------------- + + Public Functions (FatFs API) + +----------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Logical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mount ( + FATFS* fs, /* Pointer to the filesystem object (NULL:unmount)*/ + const TCHAR* path, /* Logical drive number to be mounted/unmounted */ + BYTE opt /* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */ +) +{ + FATFS *cfs; + int vol; + FRESULT res; + const TCHAR *rp = path; + + + /* Get logical drive number */ + vol = get_ldnumber(&rp); + if (vol < 0) return FR_INVALID_DRIVE; + cfs = FatFs[vol]; /* Pointer to fs object */ + + if (cfs) { +#if FF_FS_LOCK != 0 + clear_lock(cfs); +#endif +#if FF_FS_REENTRANT /* Discard sync object of the current volume */ + if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR; +#endif + cfs->fs_type = 0; /* Clear old fs object */ + } + + if (fs) { + fs->fs_type = 0; /* Clear new fs object */ +#if FF_FS_REENTRANT /* Create sync object for the new volume */ + if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR; +#endif + } + FatFs[vol] = fs; /* Register new fs object */ + + if (opt == 0) return FR_OK; /* Do not mount now, it will be mounted later */ + + res = mount_volume(&path, &fs, 0); /* Force mounted the volume */ + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Open or Create a File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_open ( + FIL* fp, /* Pointer to the blank file object */ + const TCHAR* path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; +#if !FF_FS_READONLY + DWORD cl, bcs, clst, tm; + LBA_t sc; + FSIZE_t ofs; +#endif + DEF_NAMBUF + + + if (!fp) return FR_INVALID_OBJECT; + + /* Get logical drive number */ + mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND; + res = mount_volume(&path, &fs, mode); + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ +#if !FF_FS_READONLY /* Read/Write configuration */ + if (res == FR_OK) { + if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */ + res = FR_INVALID_NAME; + } +#if FF_FS_LOCK != 0 + else { + res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); /* Check if the file can be used */ + } +#endif + } + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { + if (res != FR_OK) { /* No file, create new */ + if (res == FR_NO_FILE) { /* There is no file to open, create a new entry */ +#if FF_FS_LOCK != 0 + res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES; +#else + res = dir_register(&dj); +#endif + } + mode |= FA_CREATE_ALWAYS; /* File is created */ + } + else { /* Any object with the same name is already existing */ + if (dj.obj.attr & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */ + res = FR_DENIED; + } else { + if (mode & FA_CREATE_NEW) res = FR_EXIST; /* Cannot create as new file */ + } + } + if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate the file if overwrite mode */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + /* Get current allocation info */ + fp->obj.fs = fs; + init_alloc_info(fs, &fp->obj); + /* Set directory entry block initial state */ + mem_set(fs->dirbuf + 2, 0, 30); /* Clear 85 entry except for NumSec */ + mem_set(fs->dirbuf + 38, 0, 26); /* Clear C0 entry except for NumName and NameHash */ + fs->dirbuf[XDIR_Attr] = AM_ARC; + st_dword(fs->dirbuf + XDIR_CrtTime, GET_FATTIME()); + fs->dirbuf[XDIR_GenFlags] = 1; + res = store_xdir(&dj); + if (res == FR_OK && fp->obj.sclust != 0) { /* Remove the cluster chain if exist */ + res = remove_chain(&fp->obj, fp->obj.sclust, 0); + fs->last_clst = fp->obj.sclust - 1; /* Reuse the cluster hole */ + } + } else +#endif + { + /* Set directory entry initial state */ + tm = GET_FATTIME(); /* Set created time */ + st_dword(dj.dir + DIR_CrtTime, tm); + st_dword(dj.dir + DIR_ModTime, tm); + cl = ld_clust(fs, dj.dir); /* Get current cluster chain */ + dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */ + st_clust(fs, dj.dir, 0); /* Reset file allocation info */ + st_dword(dj.dir + DIR_FileSize, 0); + fs->wflag = 1; + if (cl != 0) { /* Remove the cluster chain if exist */ + sc = fs->winsect; + res = remove_chain(&dj.obj, cl, 0); + if (res == FR_OK) { + res = move_window(fs, sc); + fs->last_clst = cl - 1; /* Reuse the cluster hole */ + } + } + } + } + } + else { /* Open an existing file */ + if (res == FR_OK) { /* Is the object exsiting? */ + if (dj.obj.attr & AM_DIR) { /* File open against a directory */ + res = FR_NO_FILE; + } else { + if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) { /* Write mode open against R/O file */ + res = FR_DENIED; + } + } + } + } + if (res == FR_OK) { + if (mode & FA_CREATE_ALWAYS) mode |= FA_MODIFIED; /* Set file change flag if created or overwritten */ + fp->dir_sect = fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dj.dir; +#if FF_FS_LOCK != 0 + fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); /* Lock the file for this session */ + if (fp->obj.lockid == 0) res = FR_INT_ERR; +#endif + } +#else /* R/O configuration */ + if (res == FR_OK) { + if (dj.fn[NSFLAG] & NS_NONAME) { /* Is it origin directory itself? */ + res = FR_INVALID_NAME; + } else { + if (dj.obj.attr & AM_DIR) { /* Is it a directory? */ + res = FR_NO_FILE; + } + } + } +#endif + + if (res == FR_OK) { +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fp->obj.c_scl = dj.obj.sclust; /* Get containing directory info */ + fp->obj.c_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; + fp->obj.c_ofs = dj.blk_ofs; + init_alloc_info(fs, &fp->obj); + } else +#endif + { + fp->obj.sclust = ld_clust(fs, dj.dir); /* Get object allocation info */ + fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize); + } +#if FF_USE_FASTSEEK + fp->cltbl = 0; /* Disable fast seek mode */ +#endif + fp->obj.fs = fs; /* Validate the file object */ + fp->obj.id = fs->id; + fp->flag = mode; /* Set file access mode */ + fp->err = 0; /* Clear error flag */ + fp->sect = 0; /* Invalidate current data sector */ + fp->fptr = 0; /* Set file pointer top of the file */ +#if !FF_FS_READONLY +#if !FF_FS_TINY + mem_set(fp->buf, 0, sizeof fp->buf); /* Clear sector buffer */ +#endif + if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* Seek to end of file if FA_OPEN_APPEND is specified */ + fp->fptr = fp->obj.objsize; /* Offset to seek */ + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size in byte */ + clst = fp->obj.sclust; /* Follow the cluster chain */ + for (ofs = fp->obj.objsize; res == FR_OK && ofs > bcs; ofs -= bcs) { + clst = get_fat(&fp->obj, clst); + if (clst <= 1) res = FR_INT_ERR; + if (clst == 0xFFFFFFFF) res = FR_DISK_ERR; + } + fp->clust = clst; + if (res == FR_OK && ofs % SS(fs)) { /* Fill sector buffer if not on the sector boundary */ + sc = clst2sect(fs, clst); + if (sc == 0) { + res = FR_INT_ERR; + } else { + fp->sect = sc + (DWORD)(ofs / SS(fs)); +#if !FF_FS_TINY + if (disk_read(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) res = FR_DISK_ERR; +#endif + } + } + } +#endif + } + + FREE_NAMBUF(); + } + + if (res != FR_OK) fp->obj.fs = 0; /* Invalidate file object on error */ + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_read ( + FIL* fp, /* Pointer to the file object */ + void* buff, /* Pointer to data buffer */ + UINT btr, /* Number of bytes to read */ + UINT* br /* Pointer to number of bytes read */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst; + LBA_t sect; + FSIZE_t remain; + UINT rcnt, cc, csect; + BYTE *rbuff = (BYTE*)buff; + + + *br = 0; /* Clear read byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + remain = fp->obj.objsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr; /* Repeat until btr bytes read */ + btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) { + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ + if (csect == 0) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->obj.sclust; /* Follow cluster chain from the origin */ + } else { /* Middle or end of the file */ +#if FF_USE_FASTSEEK + if (fp->cltbl) { + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + } else +#endif + { + clst = get_fat(&fp->obj, fp->clust); /* Follow cluster chain on the FAT */ + } + } + if (clst < 2) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + sect = clst2sect(fs, fp->clust); /* Get current sector */ + if (sect == 0) ABORT(fs, FR_INT_ERR); + sect += csect; + cc = btr / SS(fs); /* When remaining bytes >= sector size, */ + if (cc > 0) { /* Read maximum contiguous sectors directly */ + if (csect + cc > fs->csize) { /* Clip at cluster boundary */ + cc = fs->csize - csect; + } + if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); +#if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ +#if FF_FS_TINY + if (fs->wflag && fs->winsect - sect < cc) { + mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs)); + } +#else + if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) { + mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs)); + } +#endif +#endif + rcnt = SS(fs) * cc; /* Number of bytes transferred */ + continue; + } +#if !FF_FS_TINY + if (fp->sect != sect) { /* Load data sector if not in cache */ +#if !FF_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ + } +#endif + fp->sect = sect; + } + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes remains in the sector */ + if (rcnt > btr) rcnt = btr; /* Clip it by btr if needed */ +#if FF_FS_TINY + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ + mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ +#else + mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ +#endif + } + + LEAVE_FF(fs, FR_OK); +} + + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Write File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_write ( + FIL* fp, /* Pointer to the file object */ + const void* buff, /* Pointer to the data to be written */ + UINT btw, /* Number of bytes to write */ + UINT* bw /* Pointer to number of bytes written */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst; + LBA_t sect; + UINT wcnt, cc, csect; + const BYTE *wbuff = (const BYTE*)buff; + + + *bw = 0; /* Clear write byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + /* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */ + if ((!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) { + btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr); + } + + for ( ; btw; /* Repeat until all data written */ + btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize) { + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster */ + if (csect == 0) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->obj.sclust; /* Follow from the origin */ + if (clst == 0) { /* If no cluster is allocated, */ + clst = create_chain(&fp->obj, 0); /* create a new cluster chain */ + } + } else { /* On the middle or end of the file */ +#if FF_USE_FASTSEEK + if (fp->cltbl) { + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + } else +#endif + { + clst = create_chain(&fp->obj, fp->clust); /* Follow or stretch cluster chain on the FAT */ + } + } + if (clst == 0) break; /* Could not allocate a new cluster (disk full) */ + if (clst == 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* Set start cluster if the first write */ + } +#if FF_FS_TINY + if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */ +#else + if (fp->flag & FA_DIRTY) { /* Write-back sector cache */ + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + sect = clst2sect(fs, fp->clust); /* Get current sector */ + if (sect == 0) ABORT(fs, FR_INT_ERR); + sect += csect; + cc = btw / SS(fs); /* When remaining bytes >= sector size, */ + if (cc > 0) { /* Write maximum contiguous sectors directly */ + if (csect + cc > fs->csize) { /* Clip at cluster boundary */ + cc = fs->csize - csect; + } + if (disk_write(fs->pdrv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); +#if FF_FS_MINIMIZE <= 2 +#if FF_FS_TINY + if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs)); + fs->wflag = 0; + } +#else + if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs)); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif +#endif + wcnt = SS(fs) * cc; /* Number of bytes transferred */ + continue; + } +#if FF_FS_TINY + if (fp->fptr >= fp->obj.objsize) { /* Avoid silly cache filling on the growing edge */ + if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); + fs->winsect = sect; + } +#else + if (fp->sect != sect && /* Fill sector cache with file data */ + fp->fptr < fp->obj.objsize && + disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) { + ABORT(fs, FR_DISK_ERR); + } +#endif + fp->sect = sect; + } + wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes remains in the sector */ + if (wcnt > btw) wcnt = btw; /* Clip it by btw if needed */ +#if FF_FS_TINY + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ + mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + fs->wflag = 1; +#else + mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + fp->flag |= FA_DIRTY; +#endif + } + + fp->flag |= FA_MODIFIED; /* Set file change flag */ + + LEAVE_FF(fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize the File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_sync ( + FIL* fp /* Pointer to the file object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD tm; + BYTE *dir; + + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res == FR_OK) { + if (fp->flag & FA_MODIFIED) { /* Is there any change to the file? */ +#if !FF_FS_TINY + if (fp->flag & FA_DIRTY) { /* Write-back cached data if needed */ + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) LEAVE_FF(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + /* Update the directory entry */ + tm = GET_FATTIME(); /* Modified time */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + res = fill_first_frag(&fp->obj); /* Fill first fragment on the FAT if needed */ + if (res == FR_OK) { + res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */ + } + if (res == FR_OK) { + DIR dj; + DEF_NAMBUF + + INIT_NAMBUF(fs); + res = load_obj_xdir(&dj, &fp->obj); /* Load directory entry block */ + if (res == FR_OK) { + fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute to indicate that the file has been changed */ + fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | 1; /* Update file allocation information */ + st_dword(fs->dirbuf + XDIR_FstClus, fp->obj.sclust); /* Update start cluster */ + st_qword(fs->dirbuf + XDIR_FileSize, fp->obj.objsize); /* Update file size */ + st_qword(fs->dirbuf + XDIR_ValidFileSize, fp->obj.objsize); /* (FatFs does not support Valid File Size feature) */ + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Update modified time */ + fs->dirbuf[XDIR_ModTime10] = 0; + st_dword(fs->dirbuf + XDIR_AccTime, 0); + res = store_xdir(&dj); /* Restore it to the directory */ + if (res == FR_OK) { + res = sync_fs(fs); + fp->flag &= (BYTE)~FA_MODIFIED; + } + } + FREE_NAMBUF(); + } + } else +#endif + { + res = move_window(fs, fp->dir_sect); + if (res == FR_OK) { + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; /* Set archive attribute to indicate that the file has been changed */ + st_clust(fp->obj.fs, dir, fp->obj.sclust); /* Update file allocation information */ + st_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize); /* Update file size */ + st_dword(dir + DIR_ModTime, tm); /* Update modified time */ + st_word(dir + DIR_LstAccDate, 0); + fs->wflag = 1; + res = sync_fs(fs); /* Restore it to the directory */ + fp->flag &= (BYTE)~FA_MODIFIED; + } + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* !FF_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Close File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_close ( + FIL* fp /* Pointer to the file object to be closed */ +) +{ + FRESULT res; + FATFS *fs; + +#if !FF_FS_READONLY + res = f_sync(fp); /* Flush cached data */ + if (res == FR_OK) +#endif + { + res = validate(&fp->obj, &fs); /* Lock volume */ + if (res == FR_OK) { +#if FF_FS_LOCK != 0 + res = dec_lock(fp->obj.lockid); /* Decrement file open counter */ + if (res == FR_OK) fp->obj.fs = 0; /* Invalidate file object */ +#else + fp->obj.fs = 0; /* Invalidate file object */ +#endif +#if FF_FS_REENTRANT + unlock_fs(fs, FR_OK); /* Unlock volume */ +#endif + } + } + return res; +} + + + + +#if FF_FS_RPATH >= 1 +/*-----------------------------------------------------------------------*/ +/* Change Current Directory or Current Drive, Get Current Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chdrive ( + const TCHAR* path /* Drive number to set */ +) +{ + int vol; + + + /* Get logical drive number */ + vol = get_ldnumber(&path); + if (vol < 0) return FR_INVALID_DRIVE; + CurrVol = (BYTE)vol; /* Set it as current volume */ + + return FR_OK; +} + + + +FRESULT f_chdir ( + const TCHAR* path /* Pointer to the directory path */ +) +{ +#if FF_STR_VOLUME_ID == 2 + UINT i; +#endif + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF + + + /* Get logical drive */ + res = mount_volume(&path, &fs, 0); + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the path */ + if (res == FR_OK) { /* Follow completed */ + if (dj.fn[NSFLAG] & NS_NONAME) { /* Is it the start directory itself? */ + fs->cdir = dj.obj.sclust; +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->cdc_scl = dj.obj.c_scl; + fs->cdc_size = dj.obj.c_size; + fs->cdc_ofs = dj.obj.c_ofs; + } +#endif + } else { + if (dj.obj.attr & AM_DIR) { /* It is a sub-directory */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->cdir = ld_dword(fs->dirbuf + XDIR_FstClus); /* Sub-directory cluster */ + fs->cdc_scl = dj.obj.sclust; /* Save containing directory information */ + fs->cdc_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; + fs->cdc_ofs = dj.blk_ofs; + } else +#endif + { + fs->cdir = ld_clust(fs, dj.dir); /* Sub-directory cluster */ + } + } else { + res = FR_NO_PATH; /* Reached but a file */ + } + } + } + FREE_NAMBUF(); + if (res == FR_NO_FILE) res = FR_NO_PATH; +#if FF_STR_VOLUME_ID == 2 /* Also current drive is changed at Unix style volume ID */ + if (res == FR_OK) { + for (i = FF_VOLUMES - 1; i && fs != FatFs[i]; i--) ; /* Set current drive */ + CurrVol = (BYTE)i; + } +#endif + } + + LEAVE_FF(fs, res); +} + + +#if FF_FS_RPATH >= 2 +FRESULT f_getcwd ( + TCHAR* buff, /* Pointer to the directory path */ + UINT len /* Size of buff in unit of TCHAR */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + UINT i, n; + DWORD ccl; + TCHAR *tp = buff; +#if FF_VOLUMES >= 2 + UINT vl; +#if FF_STR_VOLUME_ID + const char *vp; +#endif +#endif + FILINFO fno; + DEF_NAMBUF + + + /* Get logical drive */ + buff[0] = 0; /* Set null string to get current volume */ + res = mount_volume((const TCHAR**)&buff, &fs, 0); /* Get current volume */ + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + + /* Follow parent directories and create the path */ + i = len; /* Bottom of buffer (directory stack base) */ + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* (Cannot do getcwd on exFAT and returns root path) */ + dj.obj.sclust = fs->cdir; /* Start to follow upper directory from current directory */ + while ((ccl = dj.obj.sclust) != 0) { /* Repeat while current directory is a sub-directory */ + res = dir_sdi(&dj, 1 * SZDIRE); /* Get parent directory */ + if (res != FR_OK) break; + res = move_window(fs, dj.sect); + if (res != FR_OK) break; + dj.obj.sclust = ld_clust(fs, dj.dir); /* Goto parent directory */ + res = dir_sdi(&dj, 0); + if (res != FR_OK) break; + do { /* Find the entry links to the child directory */ + res = DIR_READ_FILE(&dj); + if (res != FR_OK) break; + if (ccl == ld_clust(fs, dj.dir)) break; /* Found the entry */ + res = dir_next(&dj, 0); + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */ + if (res != FR_OK) break; + get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */ + for (n = 0; fno.fname[n]; n++) ; /* Name length */ + if (i < n + 1) { /* Insufficient space to store the path name? */ + res = FR_NOT_ENOUGH_CORE; break; + } + while (n) buff[--i] = fno.fname[--n]; /* Stack the name */ + buff[--i] = '/'; + } + } + if (res == FR_OK) { + if (i == len) buff[--i] = '/'; /* Is it the root-directory? */ +#if FF_VOLUMES >= 2 /* Put drive prefix */ + vl = 0; +#if FF_STR_VOLUME_ID >= 1 /* String volume ID */ + for (n = 0, vp = (const char*)VolumeStr[CurrVol]; vp[n]; n++) ; + if (i >= n + 2) { + if (FF_STR_VOLUME_ID == 2) *tp++ = (TCHAR)'/'; + for (vl = 0; vl < n; *tp++ = (TCHAR)vp[vl], vl++) ; + if (FF_STR_VOLUME_ID == 1) *tp++ = (TCHAR)':'; + vl++; + } +#else /* Numeric volume ID */ + if (i >= 3) { + *tp++ = (TCHAR)'0' + CurrVol; + *tp++ = (TCHAR)':'; + vl = 2; + } +#endif + if (vl == 0) res = FR_NOT_ENOUGH_CORE; +#endif + /* Add current directory path */ + if (res == FR_OK) { + do *tp++ = buff[i++]; while (i < len); /* Copy stacked path string */ + } + } + FREE_NAMBUF(); + } + + *tp = 0; + LEAVE_FF(fs, res); +} + +#endif /* FF_FS_RPATH >= 2 */ +#endif /* FF_FS_RPATH >= 1 */ + + + +#if FF_FS_MINIMIZE <= 2 +/*-----------------------------------------------------------------------*/ +/* Seek File Read/Write Pointer */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_lseek ( + FIL* fp, /* Pointer to the file object */ + FSIZE_t ofs /* File pointer from top of file */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, bcs; + LBA_t nsect; + FSIZE_t ifptr; +#if FF_USE_FASTSEEK + DWORD cl, pcl, ncl, tcl, tlen, ulen, *tbl; + LBA_t dsc; +#endif + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res == FR_OK) res = (FRESULT)fp->err; +#if FF_FS_EXFAT && !FF_FS_READONLY + if (res == FR_OK && fs->fs_type == FS_EXFAT) { + res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */ + } +#endif + if (res != FR_OK) LEAVE_FF(fs, res); + +#if FF_USE_FASTSEEK + if (fp->cltbl) { /* Fast seek */ + if (ofs == CREATE_LINKMAP) { /* Create CLMT */ + tbl = fp->cltbl; + tlen = *tbl++; ulen = 2; /* Given table size and required table size */ + cl = fp->obj.sclust; /* Origin of the chain */ + if (cl != 0) { + do { + /* Get a fragment */ + tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */ + do { + pcl = cl; ncl++; + cl = get_fat(&fp->obj, cl); + if (cl <= 1) ABORT(fs, FR_INT_ERR); + if (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + } while (cl == pcl + 1); + if (ulen <= tlen) { /* Store the length and top of the fragment */ + *tbl++ = ncl; *tbl++ = tcl; + } + } while (cl < fs->n_fatent); /* Repeat until end of chain */ + } + *fp->cltbl = ulen; /* Number of items used */ + if (ulen <= tlen) { + *tbl = 0; /* Terminate table */ + } else { + res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */ + } + } else { /* Fast seek */ + if (ofs > fp->obj.objsize) ofs = fp->obj.objsize; /* Clip offset at the file size */ + fp->fptr = ofs; /* Set file pointer */ + if (ofs > 0) { + fp->clust = clmt_clust(fp, ofs - 1); + dsc = clst2sect(fs, fp->clust); + if (dsc == 0) ABORT(fs, FR_INT_ERR); + dsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1); + if (fp->fptr % SS(fs) && dsc != fp->sect) { /* Refill sector cache if needed */ +#if !FF_FS_TINY +#if !FF_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->pdrv, fp->buf, dsc, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Load current sector */ +#endif + fp->sect = dsc; + } + } + } + } else +#endif + + /* Normal Seek */ + { +#if FF_FS_EXFAT + if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = 0xFFFFFFFF; /* Clip at 4 GiB - 1 if at FATxx */ +#endif + if (ofs > fp->obj.objsize && (FF_FS_READONLY || !(fp->flag & FA_WRITE))) { /* In read-only mode, clip offset with the file size */ + ofs = fp->obj.objsize; + } + ifptr = fp->fptr; + fp->fptr = nsect = 0; + if (ofs > 0) { + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size (byte) */ + if (ifptr > 0 && + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ + fp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1); /* start from the current cluster */ + ofs -= fp->fptr; + clst = fp->clust; + } else { /* When seek to back cluster, */ + clst = fp->obj.sclust; /* start from the first cluster */ +#if !FF_FS_READONLY + if (clst == 0) { /* If no cluster chain, create a new chain */ + clst = create_chain(&fp->obj, 0); + if (clst == 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->obj.sclust = clst; + } +#endif + fp->clust = clst; + } + if (clst != 0) { + while (ofs > bcs) { /* Cluster following loop */ + ofs -= bcs; fp->fptr += bcs; +#if !FF_FS_READONLY + if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ + if (FF_FS_EXFAT && fp->fptr > fp->obj.objsize) { /* No FAT chain object needs correct objsize to generate FAT value */ + fp->obj.objsize = fp->fptr; + fp->flag |= FA_MODIFIED; + } + clst = create_chain(&fp->obj, clst); /* Follow chain with forceed stretch */ + if (clst == 0) { /* Clip file size in case of disk full */ + ofs = 0; break; + } + } else +#endif + { + clst = get_fat(&fp->obj, clst); /* Follow cluster chain if not in write mode */ + } + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR); + fp->clust = clst; + } + fp->fptr += ofs; + if (ofs % SS(fs)) { + nsect = clst2sect(fs, clst); /* Current sector */ + if (nsect == 0) ABORT(fs, FR_INT_ERR); + nsect += (DWORD)(ofs / SS(fs)); + } + } + } + if (!FF_FS_READONLY && fp->fptr > fp->obj.objsize) { /* Set file change flag if the file size is extended */ + fp->obj.objsize = fp->fptr; + fp->flag |= FA_MODIFIED; + } + if (fp->fptr % SS(fs) && nsect != fp->sect) { /* Fill sector cache if needed */ +#if !FF_FS_TINY +#if !FF_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->pdrv, fp->buf, nsect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ +#endif + fp->sect = nsect; + } + } + + LEAVE_FF(fs, res); +} + + + +#if FF_FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Create a Directory Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_opendir ( + DIR* dp, /* Pointer to directory object to create */ + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + FATFS *fs; + DEF_NAMBUF + + + if (!dp) return FR_INVALID_OBJECT; + + /* Get logical drive */ + res = mount_volume(&path, &fs, 0); + if (res == FR_OK) { + dp->obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(dp, path); /* Follow the path to the directory */ + if (res == FR_OK) { /* Follow completed */ + if (!(dp->fn[NSFLAG] & NS_NONAME)) { /* It is not the origin directory itself */ + if (dp->obj.attr & AM_DIR) { /* This object is a sub-directory */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + dp->obj.c_scl = dp->obj.sclust; /* Get containing directory inforamation */ + dp->obj.c_size = ((DWORD)dp->obj.objsize & 0xFFFFFF00) | dp->obj.stat; + dp->obj.c_ofs = dp->blk_ofs; + init_alloc_info(fs, &dp->obj); /* Get object allocation info */ + } else +#endif + { + dp->obj.sclust = ld_clust(fs, dp->dir); /* Get object allocation info */ + } + } else { /* This object is a file */ + res = FR_NO_PATH; + } + } + if (res == FR_OK) { + dp->obj.id = fs->id; + res = dir_sdi(dp, 0); /* Rewind directory */ +#if FF_FS_LOCK != 0 + if (res == FR_OK) { + if (dp->obj.sclust != 0) { + dp->obj.lockid = inc_lock(dp, 0); /* Lock the sub directory */ + if (!dp->obj.lockid) res = FR_TOO_MANY_OPEN_FILES; + } else { + dp->obj.lockid = 0; /* Root directory need not to be locked */ + } + } +#endif + } + } + FREE_NAMBUF(); + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + if (res != FR_OK) dp->obj.fs = 0; /* Invalidate the directory object if function faild */ + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Close Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_closedir ( + DIR *dp /* Pointer to the directory object to be closed */ +) +{ + FRESULT res; + FATFS *fs; + + + res = validate(&dp->obj, &fs); /* Check validity of the file object */ + if (res == FR_OK) { +#if FF_FS_LOCK != 0 + if (dp->obj.lockid) res = dec_lock(dp->obj.lockid); /* Decrement sub-directory open counter */ + if (res == FR_OK) dp->obj.fs = 0; /* Invalidate directory object */ +#else + dp->obj.fs = 0; /* Invalidate directory object */ +#endif +#if FF_FS_REENTRANT + unlock_fs(fs, FR_OK); /* Unlock volume */ +#endif + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read Directory Entries in Sequence */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_readdir ( + DIR* dp, /* Pointer to the open directory object */ + FILINFO* fno /* Pointer to file information to return */ +) +{ + FRESULT res; + FATFS *fs; + DEF_NAMBUF + + + res = validate(&dp->obj, &fs); /* Check validity of the directory object */ + if (res == FR_OK) { + if (!fno) { + res = dir_sdi(dp, 0); /* Rewind the directory object */ + } else { + INIT_NAMBUF(fs); + res = DIR_READ_FILE(dp); /* Read an item */ + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory */ + if (res == FR_OK) { /* A valid entry is found */ + get_fileinfo(dp, fno); /* Get the object information */ + res = dir_next(dp, 0); /* Increment index for next */ + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory now */ + } + FREE_NAMBUF(); + } + } + LEAVE_FF(fs, res); +} + + + +#if FF_USE_FIND +/*-----------------------------------------------------------------------*/ +/* Find Next File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_findnext ( + DIR* dp, /* Pointer to the open directory object */ + FILINFO* fno /* Pointer to the file information structure */ +) +{ + FRESULT res; + + + for (;;) { + res = f_readdir(dp, fno); /* Get a directory item */ + if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */ + if (pattern_match(dp->pat, fno->fname, 0, FIND_RECURS)) break; /* Test for the file name */ +#if FF_USE_LFN && FF_USE_FIND == 2 + if (pattern_match(dp->pat, fno->altname, 0, FIND_RECURS)) break; /* Test for alternative name if exist */ +#endif + } + return res; +} + + + +/*-----------------------------------------------------------------------*/ +/* Find First File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_findfirst ( + DIR* dp, /* Pointer to the blank directory object */ + FILINFO* fno, /* Pointer to the file information structure */ + const TCHAR* path, /* Pointer to the directory to open */ + const TCHAR* pattern /* Pointer to the matching pattern */ +) +{ + FRESULT res; + + + dp->pat = pattern; /* Save pointer to pattern string */ + res = f_opendir(dp, path); /* Open the target directory */ + if (res == FR_OK) { + res = f_findnext(dp, fno); /* Find the first item */ + } + return res; +} + +#endif /* FF_USE_FIND */ + + + +#if FF_FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( + const TCHAR* path, /* Pointer to the file path */ + FILINFO* fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DIR dj; + DEF_NAMBUF + + + /* Get logical drive */ + res = mount_volume(&path, &dj.obj.fs, 0); + if (res == FR_OK) { + INIT_NAMBUF(dj.obj.fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follow completed */ + if (dj.fn[NSFLAG] & NS_NONAME) { /* It is origin directory */ + res = FR_INVALID_NAME; + } else { /* Found an object */ + if (fno) get_fileinfo(&dj, fno); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(dj.obj.fs, res); +} + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getfree ( + const TCHAR* path, /* Logical drive number */ + DWORD* nclst, /* Pointer to a variable to return number of free clusters */ + FATFS** fatfs /* Pointer to return pointer to corresponding filesystem object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD nfree, clst, stat; + LBA_t sect; + UINT i; + FFOBJID obj; + + + /* Get logical drive */ + res = mount_volume(&path, &fs, 0); + if (res == FR_OK) { + *fatfs = fs; /* Return ptr to the fs object */ + /* If free_clst is valid, return it without full FAT scan */ + if (fs->free_clst <= fs->n_fatent - 2) { + *nclst = fs->free_clst; + } else { + /* Scan FAT to obtain number of free clusters */ + nfree = 0; + if (fs->fs_type == FS_FAT12) { /* FAT12: Scan bit field FAT entries */ + clst = 2; obj.fs = fs; + do { + stat = get_fat(&obj, clst); + if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (stat == 1) { res = FR_INT_ERR; break; } + if (stat == 0) nfree++; + } while (++clst < fs->n_fatent); + } else { +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* exFAT: Scan allocation bitmap */ + BYTE bm; + UINT b; + + clst = fs->n_fatent - 2; /* Number of clusters */ + sect = fs->bitbase; /* Bitmap sector */ + i = 0; /* Offset in the sector */ + do { /* Counts numbuer of bits with zero in the bitmap */ + if (i == 0) { + res = move_window(fs, sect++); + if (res != FR_OK) break; + } + for (b = 8, bm = fs->win[i]; b && clst; b--, clst--) { + if (!(bm & 1)) nfree++; + bm >>= 1; + } + i = (i + 1) % SS(fs); + } while (clst); + } else +#endif + { /* FAT16/32: Scan WORD/DWORD FAT entries */ + clst = fs->n_fatent; /* Number of entries */ + sect = fs->fatbase; /* Top of the FAT */ + i = 0; /* Offset in the sector */ + do { /* Counts numbuer of entries with zero in the FAT */ + if (i == 0) { + res = move_window(fs, sect++); + if (res != FR_OK) break; + } + if (fs->fs_type == FS_FAT16) { + if (ld_word(fs->win + i) == 0) nfree++; + i += 2; + } else { + if ((ld_dword(fs->win + i) & 0x0FFFFFFF) == 0) nfree++; + i += 4; + } + i %= SS(fs); + } while (--clst); + } + } + *nclst = nfree; /* Return the free clusters */ + fs->free_clst = nfree; /* Now free_clst is valid */ + fs->fsi_flag |= 1; /* FAT32: FSInfo is to be updated */ + } + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Truncate File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_truncate ( + FIL* fp /* Pointer to the file object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD ncl; + + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + if (fp->fptr < fp->obj.objsize) { /* Process when fptr is not on the eof */ + if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ + res = remove_chain(&fp->obj, fp->obj.sclust, 0); + fp->obj.sclust = 0; + } else { /* When truncate a part of the file, remove remaining clusters */ + ncl = get_fat(&fp->obj, fp->clust); + res = FR_OK; + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (ncl == 1) res = FR_INT_ERR; + if (res == FR_OK && ncl < fs->n_fatent) { + res = remove_chain(&fp->obj, ncl, fp->clust); + } + } + fp->obj.objsize = fp->fptr; /* Set file size to current read/write point */ + fp->flag |= FA_MODIFIED; +#if !FF_FS_TINY + if (res == FR_OK && (fp->flag & FA_DIRTY)) { + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) { + res = FR_DISK_ERR; + } else { + fp->flag &= (BYTE)~FA_DIRTY; + } + } +#endif + if (res != FR_OK) ABORT(fs, res); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Delete a File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_unlink ( + const TCHAR* path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + DIR dj, sdj; + DWORD dclst = 0; + FATFS *fs; +#if FF_FS_EXFAT + FFOBJID obj; +#endif + DEF_NAMBUF + + + /* Get logical drive */ + res = mount_volume(&path, &fs, FA_WRITE); + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (FF_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) { + res = FR_INVALID_NAME; /* Cannot remove dot entry */ + } +#if FF_FS_LOCK != 0 + if (res == FR_OK) res = chk_lock(&dj, 2); /* Check if it is an open object */ +#endif + if (res == FR_OK) { /* The object is accessible */ + if (dj.fn[NSFLAG] & NS_NONAME) { + res = FR_INVALID_NAME; /* Cannot remove the origin directory */ + } else { + if (dj.obj.attr & AM_RDO) { + res = FR_DENIED; /* Cannot remove R/O object */ + } + } + if (res == FR_OK) { +#if FF_FS_EXFAT + obj.fs = fs; + if (fs->fs_type == FS_EXFAT) { + init_alloc_info(fs, &obj); + dclst = obj.sclust; + } else +#endif + { + dclst = ld_clust(fs, dj.dir); + } + if (dj.obj.attr & AM_DIR) { /* Is it a sub-directory? */ +#if FF_FS_RPATH != 0 + if (dclst == fs->cdir) { /* Is it the current directory? */ + res = FR_DENIED; + } else +#endif + { + sdj.obj.fs = fs; /* Open the sub-directory */ + sdj.obj.sclust = dclst; +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + sdj.obj.objsize = obj.objsize; + sdj.obj.stat = obj.stat; + } +#endif + res = dir_sdi(&sdj, 0); + if (res == FR_OK) { + res = DIR_READ_FILE(&sdj); /* Test if the directory is empty */ + if (res == FR_OK) res = FR_DENIED; /* Not empty? */ + if (res == FR_NO_FILE) res = FR_OK; /* Empty? */ + } + } + } + } + if (res == FR_OK) { + res = dir_remove(&dj); /* Remove the directory entry */ + if (res == FR_OK && dclst != 0) { /* Remove the cluster chain if exist */ +#if FF_FS_EXFAT + res = remove_chain(&obj, dclst, 0); +#else + res = remove_chain(&dj.obj, dclst, 0); +#endif + } + if (res == FR_OK) res = sync_fs(fs); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + FFOBJID sobj; + FATFS *fs; + DWORD dcl, pcl, tm; + DEF_NAMBUF + + + res = mount_volume(&path, &fs, FA_WRITE); /* Get logical drive */ + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) res = FR_EXIST; /* Name collision? */ + if (FF_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) { /* Invalid name? */ + res = FR_INVALID_NAME; + } + if (res == FR_NO_FILE) { /* It is clear to create a new directory */ + sobj.fs = fs; /* New object id to create a new chain */ + dcl = create_chain(&sobj, 0); /* Allocate a cluster for the new directory */ + res = FR_OK; + if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster? */ + if (dcl == 1) res = FR_INT_ERR; /* Any insanity? */ + if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; /* Disk error? */ + tm = GET_FATTIME(); + if (res == FR_OK) { + res = dir_clear(fs, dcl); /* Clean up the new table */ + if (res == FR_OK) { + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* Create dot entries (FAT only) */ + mem_set(fs->win + DIR_Name, ' ', 11); /* Create "." entry */ + fs->win[DIR_Name] = '.'; + fs->win[DIR_Attr] = AM_DIR; + st_dword(fs->win + DIR_ModTime, tm); + st_clust(fs, fs->win, dcl); + mem_cpy(fs->win + SZDIRE, fs->win, SZDIRE); /* Create ".." entry */ + fs->win[SZDIRE + 1] = '.'; pcl = dj.obj.sclust; + st_clust(fs, fs->win + SZDIRE, pcl); + fs->wflag = 1; + } + res = dir_register(&dj); /* Register the object to the parent directoy */ + } + } + if (res == FR_OK) { +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* Initialize directory entry block */ + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Created time */ + st_dword(fs->dirbuf + XDIR_FstClus, dcl); /* Table start cluster */ + st_dword(fs->dirbuf + XDIR_FileSize, (DWORD)fs->csize * SS(fs)); /* Directory size needs to be valid */ + st_dword(fs->dirbuf + XDIR_ValidFileSize, (DWORD)fs->csize * SS(fs)); + fs->dirbuf[XDIR_GenFlags] = 3; /* Initialize the object flag */ + fs->dirbuf[XDIR_Attr] = AM_DIR; /* Attribute */ + res = store_xdir(&dj); + } else +#endif + { + st_dword(dj.dir + DIR_ModTime, tm); /* Created time */ + st_clust(fs, dj.dir, dcl); /* Table start cluster */ + dj.dir[DIR_Attr] = AM_DIR; /* Attribute */ + fs->wflag = 1; + } + if (res == FR_OK) { + res = sync_fs(fs); + } + } else { + remove_chain(&sobj, dcl, 0); /* Could not register, remove the allocated cluster */ + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Rename a File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_rename ( + const TCHAR* path_old, /* Pointer to the object name to be renamed */ + const TCHAR* path_new /* Pointer to the new name */ +) +{ + FRESULT res; + DIR djo, djn; + FATFS *fs; + BYTE buf[FF_FS_EXFAT ? SZDIRE * 2 : SZDIRE], *dir; + LBA_t sect; + DEF_NAMBUF + + + get_ldnumber(&path_new); /* Snip the drive number of new name off */ + res = mount_volume(&path_old, &fs, FA_WRITE); /* Get logical drive of the old object */ + if (res == FR_OK) { + djo.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&djo, path_old); /* Check old object */ + if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check validity of name */ +#if FF_FS_LOCK != 0 + if (res == FR_OK) { + res = chk_lock(&djo, 2); + } +#endif + if (res == FR_OK) { /* Object to be renamed is found */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* At exFAT volume */ + BYTE nf, nn; + WORD nh; + + mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */ + mem_cpy(&djn, &djo, sizeof djo); + res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ + if (res == FR_OK) { /* Is new name already in use by any other object? */ + res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; + } + if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { + nf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName]; + nh = ld_word(fs->dirbuf + XDIR_NameHash); + mem_cpy(fs->dirbuf, buf, SZDIRE * 2); /* Restore 85+C0 entry */ + fs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn; + st_word(fs->dirbuf + XDIR_NameHash, nh); + if (!(fs->dirbuf[XDIR_Attr] & AM_DIR)) fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */ +/* Start of critical section where an interruption can cause a cross-link */ + res = store_xdir(&djn); + } + } + } else +#endif + { /* At FAT/FAT32 volume */ + mem_cpy(buf, djo.dir, SZDIRE); /* Save directory entry of the object */ + mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ + res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ + if (res == FR_OK) { /* Is new name already in use by any other object? */ + res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; + } + if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { + dir = djn.dir; /* Copy directory entry of the object except name */ + mem_cpy(dir + 13, buf + 13, SZDIRE - 13); + dir[DIR_Attr] = buf[DIR_Attr]; + if (!(dir[DIR_Attr] & AM_DIR)) dir[DIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */ + fs->wflag = 1; + if ((dir[DIR_Attr] & AM_DIR) && djo.obj.sclust != djn.obj.sclust) { /* Update .. entry in the sub-directory if needed */ + sect = clst2sect(fs, ld_clust(fs, dir)); + if (sect == 0) { + res = FR_INT_ERR; + } else { +/* Start of critical section where an interruption can cause a cross-link */ + res = move_window(fs, sect); + dir = fs->win + SZDIRE * 1; /* Ptr to .. entry */ + if (res == FR_OK && dir[1] == '.') { + st_clust(fs, dir, djn.obj.sclust); + fs->wflag = 1; + } + } + } + } + } + } + if (res == FR_OK) { + res = dir_remove(&djo); /* Remove old entry */ + if (res == FR_OK) { + res = sync_fs(fs); + } + } +/* End of the critical section */ + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + +#endif /* !FF_FS_READONLY */ +#endif /* FF_FS_MINIMIZE == 0 */ +#endif /* FF_FS_MINIMIZE <= 1 */ +#endif /* FF_FS_MINIMIZE <= 2 */ + + + +#if FF_USE_CHMOD && !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Change Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const TCHAR* path, /* Pointer to the file path */ + BYTE attr, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF + + + res = mount_volume(&path, &fs, FA_WRITE); /* Get logical drive */ + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ + if (res == FR_OK) { + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->dirbuf[XDIR_Attr] = (attr & mask) | (fs->dirbuf[XDIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + res = store_xdir(&dj); + } else +#endif + { + dj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + fs->wflag = 1; + } + if (res == FR_OK) { + res = sync_fs(fs); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Timestamp */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_utime ( + const TCHAR* path, /* Pointer to the file/directory name */ + const FILINFO* fno /* Pointer to the timestamp to be set */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF + + + res = mount_volume(&path, &fs, FA_WRITE); /* Get logical drive */ + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ + if (res == FR_OK) { +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + st_dword(fs->dirbuf + XDIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); + res = store_xdir(&dj); + } else +#endif + { + st_dword(dj.dir + DIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); + fs->wflag = 1; + } + if (res == FR_OK) { + res = sync_fs(fs); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + +#endif /* FF_USE_CHMOD && !FF_FS_READONLY */ + + + +#if FF_USE_LABEL +/*-----------------------------------------------------------------------*/ +/* Get Volume Label */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getlabel ( + const TCHAR* path, /* Logical drive number */ + TCHAR* label, /* Buffer to store the volume label */ + DWORD* vsn /* Variable to store the volume serial number */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + UINT si, di; + WCHAR wc; + + /* Get logical drive */ + res = mount_volume(&path, &fs, 0); + + /* Get volume label */ + if (res == FR_OK && label) { + dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */ + res = dir_sdi(&dj, 0); + if (res == FR_OK) { + res = DIR_READ_LABEL(&dj); /* Find a volume label entry */ + if (res == FR_OK) { +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + WCHAR hs; + + for (si = di = hs = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */ + wc = ld_word(dj.dir + XDIR_Label + si * 2); + if (hs == 0 && IsSurrogate(wc)) { /* Is the code a surrogate? */ + hs = wc; continue; + } + wc = put_utf((DWORD)hs << 16 | wc, &label[di], 4); + if (wc == 0) { di = 0; break; } + di += wc; + hs = 0; + } + if (hs != 0) di = 0; /* Broken surrogate pair? */ + label[di] = 0; + } else +#endif + { + si = di = 0; /* Extract volume label from AM_VOL entry */ + while (si < 11) { + wc = dj.dir[si++]; +#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode output */ + if (dbc_1st((BYTE)wc) && si < 11) wc = wc << 8 | dj.dir[si++]; /* Is it a DBC? */ + wc = ff_oem2uni(wc, CODEPAGE); /* Convert it into Unicode */ + if (wc != 0) wc = put_utf(wc, &label[di], 4); /* Put it in Unicode */ + if (wc == 0) { di = 0; break; } + di += wc; +#else /* ANSI/OEM output */ + label[di++] = (TCHAR)wc; +#endif + } + do { /* Truncate trailing spaces */ + label[di] = 0; + if (di == 0) break; + } while (label[--di] == ' '); + } + } + } + if (res == FR_NO_FILE) { /* No label entry and return nul string */ + label[0] = 0; + res = FR_OK; + } + } + + /* Get volume serial number */ + if (res == FR_OK && vsn) { + res = move_window(fs, fs->volbase); + if (res == FR_OK) { + switch (fs->fs_type) { + case FS_EXFAT: + di = BPB_VolIDEx; + break; + + case FS_FAT32: + di = BS_VolID32; + break; + + default: + di = BS_VolID; + } + *vsn = ld_dword(fs->win + di); + } + } + + LEAVE_FF(fs, res); +} + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Set Volume Label */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_setlabel ( + const TCHAR* label /* Volume label to set with heading logical drive number */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + BYTE dirvn[22]; + UINT di; + WCHAR wc; + static const char badchr[] = "+.,;=[]/\\\"*:<>\?|\x7F"; /* [0..] for FAT, [7..] for exFAT */ +#if FF_USE_LFN + DWORD dc; +#endif + + /* Get logical drive */ + res = mount_volume(&label, &fs, FA_WRITE); + if (res != FR_OK) LEAVE_FF(fs, res); + +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + mem_set(dirvn, 0, 22); + di = 0; + while ((UINT)*label >= ' ') { /* Create volume label */ + dc = tchar2uni(&label); /* Get a Unicode character */ + if (dc >= 0x10000) { + if (dc == 0xFFFFFFFF || di >= 10) { /* Wrong surrogate or buffer overflow */ + dc = 0; + } else { + st_word(dirvn + di * 2, (WCHAR)(dc >> 16)); di++; + } + } + if (dc == 0 || chk_chr(badchr + 7, (int)dc) || di >= 11) { /* Check validity of the volume label */ + LEAVE_FF(fs, FR_INVALID_NAME); + } + st_word(dirvn + di * 2, (WCHAR)dc); di++; + } + } else +#endif + { /* On the FAT/FAT32 volume */ + mem_set(dirvn, ' ', 11); + di = 0; + while ((UINT)*label >= ' ') { /* Create volume label */ +#if FF_USE_LFN + dc = tchar2uni(&label); + wc = (dc < 0x10000) ? ff_uni2oem(ff_wtoupper(dc), CODEPAGE) : 0; +#else /* ANSI/OEM input */ + wc = (BYTE)*label++; + if (dbc_1st((BYTE)wc)) wc = dbc_2nd((BYTE)*label) ? wc << 8 | (BYTE)*label++ : 0; + if (IsLower(wc)) wc -= 0x20; /* To upper ASCII characters */ +#if FF_CODE_PAGE == 0 + if (ExCvt && wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */ +#elif FF_CODE_PAGE < 900 + if (wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */ +#endif +#endif + if (wc == 0 || chk_chr(badchr + 0, (int)wc) || di >= (UINT)((wc >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */ + LEAVE_FF(fs, FR_INVALID_NAME); + } + if (wc >= 0x100) dirvn[di++] = (BYTE)(wc >> 8); + dirvn[di++] = (BYTE)wc; + } + if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */ + while (di && dirvn[di - 1] == ' ') di--; /* Snip trailing spaces */ + } + + /* Set volume label */ + dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */ + res = dir_sdi(&dj, 0); + if (res == FR_OK) { + res = DIR_READ_LABEL(&dj); /* Get volume label entry */ + if (res == FR_OK) { + if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { + dj.dir[XDIR_NumLabel] = (BYTE)di; /* Change the volume label */ + mem_cpy(dj.dir + XDIR_Label, dirvn, 22); + } else { + if (di != 0) { + mem_cpy(dj.dir, dirvn, 11); /* Change the volume label */ + } else { + dj.dir[DIR_Name] = DDEM; /* Remove the volume label */ + } + } + fs->wflag = 1; + res = sync_fs(fs); + } else { /* No volume label entry or an error */ + if (res == FR_NO_FILE) { + res = FR_OK; + if (di != 0) { /* Create a volume label entry */ + res = dir_alloc(&dj, 1); /* Allocate an entry */ + if (res == FR_OK) { + mem_set(dj.dir, 0, SZDIRE); /* Clean the entry */ + if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { + dj.dir[XDIR_Type] = ET_VLABEL; /* Create volume label entry */ + dj.dir[XDIR_NumLabel] = (BYTE)di; + mem_cpy(dj.dir + XDIR_Label, dirvn, 22); + } else { + dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */ + mem_cpy(dj.dir, dirvn, 11); + } + fs->wflag = 1; + res = sync_fs(fs); + } + } + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* !FF_FS_READONLY */ +#endif /* FF_USE_LABEL */ + + + +#if FF_USE_EXPAND && !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Allocate a Contiguous Blocks to the File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_expand ( + FIL* fp, /* Pointer to the file object */ + FSIZE_t fsz, /* File size to be expanded to */ + BYTE opt /* Operation mode 0:Find and prepare or 1:Find and allocate */ +) +{ + FRESULT res; + FATFS *fs; + DWORD n, clst, stcl, scl, ncl, tcl, lclst; + + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); +#if FF_FS_EXFAT + if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, FR_DENIED); /* Check if in size limit */ +#endif + n = (DWORD)fs->csize * SS(fs); /* Cluster size */ + tcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0); /* Number of clusters required */ + stcl = fs->last_clst; lclst = 0; + if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2; + +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + scl = find_bitmap(fs, stcl, tcl); /* Find a contiguous cluster block */ + if (scl == 0) res = FR_DENIED; /* No contiguous cluster block was found */ + if (scl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) { /* A contiguous free area is found */ + if (opt) { /* Allocate it now */ + res = change_bitmap(fs, scl, tcl, 1); /* Mark the cluster block 'in use' */ + lclst = scl + tcl - 1; + } else { /* Set it as suggested point for next allocation */ + lclst = scl - 1; + } + } + } else +#endif + { + scl = clst = stcl; ncl = 0; + for (;;) { /* Find a contiguous cluster block */ + n = get_fat(&fp->obj, clst); + if (++clst >= fs->n_fatent) clst = 2; + if (n == 1) { res = FR_INT_ERR; break; } + if (n == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (n == 0) { /* Is it a free cluster? */ + if (++ncl == tcl) break; /* Break if a contiguous cluster block is found */ + } else { + scl = clst; ncl = 0; /* Not a free cluster */ + } + if (clst == stcl) { res = FR_DENIED; break; } /* No contiguous cluster? */ + } + if (res == FR_OK) { /* A contiguous free area is found */ + if (opt) { /* Allocate it now */ + for (clst = scl, n = tcl; n; clst++, n--) { /* Create a cluster chain on the FAT */ + res = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : clst + 1); + if (res != FR_OK) break; + lclst = clst; + } + } else { /* Set it as suggested point for next allocation */ + lclst = scl - 1; + } + } + } + + if (res == FR_OK) { + fs->last_clst = lclst; /* Set suggested start cluster to start next */ + if (opt) { /* Is it allocated now? */ + fp->obj.sclust = scl; /* Update object allocation information */ + fp->obj.objsize = fsz; + if (FF_FS_EXFAT) fp->obj.stat = 2; /* Set status 'contiguous chain' */ + fp->flag |= FA_MODIFIED; + if (fs->free_clst <= fs->n_fatent - 2) { /* Update FSINFO */ + fs->free_clst -= tcl; + fs->fsi_flag |= 1; + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* FF_USE_EXPAND && !FF_FS_READONLY */ + + + +#if FF_USE_FORWARD +/*-----------------------------------------------------------------------*/ +/* Forward Data to the Stream Directly */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_forward ( + FIL* fp, /* Pointer to the file object */ + UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */ + UINT btf, /* Number of bytes to forward */ + UINT* bf /* Pointer to number of bytes forwarded */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst; + LBA_t sect; + FSIZE_t remain; + UINT rcnt, csect; + BYTE *dbuf; + + + *bf = 0; /* Clear transfer byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + remain = fp->obj.objsize - fp->fptr; + if (btf > remain) btf = (UINT)remain; /* Truncate btf by remaining bytes */ + + for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream goes busy */ + fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + if (csect == 0) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->obj.sclust : get_fat(&fp->obj, fp->clust); + if (clst <= 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + } + sect = clst2sect(fs, fp->clust); /* Get current data sector */ + if (sect == 0) ABORT(fs, FR_INT_ERR); + sect += csect; +#if FF_FS_TINY + if (move_window(fs, sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window to the file data */ + dbuf = fs->win; +#else + if (fp->sect != sect) { /* Fill sector cache with file data */ +#if !FF_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + } + dbuf = fp->buf; +#endif + fp->sect = sect; + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes remains in the sector */ + if (rcnt > btf) rcnt = btf; /* Clip it by btr if needed */ + rcnt = (*func)(dbuf + ((UINT)fp->fptr % SS(fs)), rcnt); /* Forward the file data */ + if (rcnt == 0) ABORT(fs, FR_INT_ERR); + } + + LEAVE_FF(fs, FR_OK); +} +#endif /* FF_USE_FORWARD */ + + + +#if !FF_FS_READONLY && FF_USE_MKFS +/*-----------------------------------------------------------------------*/ +/* Create FAT/exFAT volume */ +/*-----------------------------------------------------------------------*/ + +#define N_SEC_TRACK 63 /* Sectors per track for determination of drive CHS */ +#define GPT_ALIGN 0x100000 /* Alignment of partitions in GPT [byte] (>=128KB) */ +#define GPT_ITEMS 128 /* Number of GPT table size (>=128, sector aligned) */ + + +/* Create partitions on the physical drive in format of MBR or GPT */ + +static FRESULT create_partition ( + BYTE drv, /* Physical drive number */ + const LBA_t plst[], /* Partition list */ + BYTE sys, /* System ID (for only MBR, temp setting) */ + BYTE* buf /* Working buffer for a sector */ +) +{ + UINT i, cy; + LBA_t sz_drv; + DWORD sz_drv32, s_lba32, n_lba32; + BYTE *pte, hd, n_hd, sc, n_sc; + + /* Get drive size */ + if (disk_ioctl(drv, GET_SECTOR_COUNT, &sz_drv) != RES_OK) return FR_DISK_ERR; + +#if FF_LBA64 + if (sz_drv >= FF_MIN_GPT) { /* Create partitions in GPT */ + WORD ss; + UINT sz_pt, pi, si, ofs; + DWORD bcc, rnd, align; + QWORD s_lba64, n_lba64, sz_pool, s_bpt; + static const BYTE gpt_mbr[16] = {0x00, 0x00, 0x02, 0x00, 0xEE, 0xFE, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; + +#if FF_MAX_SS != FF_MIN_SS + if (disk_ioctl(drv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR; /* Get sector size */ + if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR; +#else + ss = FF_MAX_SS; +#endif + rnd = GET_FATTIME(); /* Random seed */ + align = GPT_ALIGN / ss; /* Partition alignment [sector] */ + sz_pt = GPT_ITEMS * SZ_GPTE / ss; /* Size of PT [sector] */ + s_bpt = sz_drv - sz_pt - 1; /* Backup PT start sector */ + s_lba64 = 2 + sz_pt; /* First allocatable sector */ + sz_pool = s_bpt - s_lba64; /* Size of allocatable area */ + bcc = 0xFFFFFFFF; n_lba64 = 1; + pi = si = 0; /* partition table index, size table index */ + do { + if (pi * SZ_GPTE % ss == 0) mem_set(buf, 0, ss); /* Clean the buffer if needed */ + if (n_lba64 != 0) { /* Is the size table not termintated? */ + s_lba64 = (s_lba64 + align - 1) & ((QWORD)0 - align); /* Align partition start */ + n_lba64 = plst[si++]; /* Get a partition size */ + if (n_lba64 <= 100) { /* Is the size in percentage? */ + n_lba64 = sz_pool * n_lba64 / 100; + n_lba64 = (n_lba64 + align - 1) & ((QWORD)0 - align); /* Align partition end (only if in percentage) */ + } + if (s_lba64 + n_lba64 > s_bpt) { /* Clip at end of the pool */ + n_lba64 = (s_lba64 < s_bpt) ? s_bpt - s_lba64 : 0; + } + } + if (n_lba64 != 0) { /* Add a partition? */ + ofs = pi * SZ_GPTE % ss; + mem_cpy(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, 16); /* Partition GUID (Microsoft Basic Data) */ + rnd = make_rand(rnd, buf + ofs + GPTE_UpGuid, 16); /* Unique partition GUID */ + st_qword(buf + ofs + GPTE_FstLba, s_lba64); /* Partition start LBA */ + st_qword(buf + ofs + GPTE_LstLba, s_lba64 + n_lba64 - 1); /* Partition end LBA */ + s_lba64 += n_lba64; /* Next partition LBA */ + } + if ((pi + 1) * SZ_GPTE % ss == 0) { /* Write the buffer if it is filled up */ + for (i = 0; i < ss; bcc = crc32(bcc, buf[i++])) ; /* Calculate table check sum */ + if (disk_write(drv, buf, 2 + pi * SZ_GPTE / ss, 1) != RES_OK) return FR_DISK_ERR; /* Primary table */ + if (disk_write(drv, buf, s_bpt + pi * SZ_GPTE / ss, 1) != RES_OK) return FR_DISK_ERR; /* Secondary table */ + } + } while (++pi < GPT_ITEMS); + + /* Create primary GPT header */ + mem_set(buf, 0, ss); + mem_cpy(buf + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", 16); /* Signature, version (1.0) and size (92) */ + st_dword(buf + GPTH_PtBcc, ~bcc); /* Table check sum */ + st_qword(buf + GPTH_CurLba, 1); /* LBA of this header */ + st_qword(buf + GPTH_BakLba, sz_drv - 1); /* LBA of another header */ + st_qword(buf + GPTH_FstLba, 2 + sz_pt); /* LBA of first allocatable sector */ + st_qword(buf + GPTH_LstLba, s_bpt - 1); /* LBA of last allocatable sector */ + st_dword(buf + GPTH_PteSize, SZ_GPTE); /* Size of a table entry */ + st_dword(buf + GPTH_PtNum, GPT_ITEMS); /* Number of table entries */ + st_dword(buf + GPTH_PtOfs, 2); /* LBA of this table */ + rnd = make_rand(rnd, buf + GPTH_DskGuid, 16); /* Disk GUID */ + for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, buf[i++])) ; /* Calculate header check sum */ + st_dword(buf + GPTH_Bcc, ~bcc); /* Header check sum */ + if (disk_write(drv, buf, 1, 1) != RES_OK) return FR_DISK_ERR; + + /* Create secondary GPT header */ + st_qword(buf + GPTH_CurLba, sz_drv - 1); /* LBA of this header */ + st_qword(buf + GPTH_BakLba, 1); /* LBA of another header */ + st_qword(buf + GPTH_PtOfs, s_bpt); /* LBA of this table */ + st_dword(buf + GPTH_Bcc, 0); + for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, buf[i++])) ; /* Calculate header check sum */ + st_dword(buf + GPTH_Bcc, ~bcc); /* Header check sum */ + if (disk_write(drv, buf, sz_drv - 1, 1) != RES_OK) return FR_DISK_ERR; + + /* Create protective MBR */ + mem_set(buf, 0, ss); + mem_cpy(buf + MBR_Table, gpt_mbr, 16); /* Create a GPT partition */ + st_word(buf + BS_55AA, 0xAA55); + if (disk_write(drv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; + + } else +#endif + { /* Create partitions in MBR */ + sz_drv32 = (DWORD)sz_drv; + n_sc = N_SEC_TRACK; /* Determine drive CHS without any consideration of the drive geometry */ + for (n_hd = 8; n_hd != 0 && sz_drv32 / n_hd / n_sc > 1024; n_hd *= 2) ; + if (n_hd == 0) n_hd = 255; /* Number of heads needs to be <256 */ + + mem_set(buf, 0, FF_MAX_SS); /* Clear MBR */ + pte = buf + MBR_Table; /* Partition table in the MBR */ + for (i = 0, s_lba32 = n_sc; i < 4 && s_lba32 != 0 && s_lba32 < sz_drv32; i++, s_lba32 += n_lba32) { + n_lba32 = (DWORD)plst[i]; /* Get partition size */ + if (n_lba32 <= 100) n_lba32 = (n_lba32 == 100) ? sz_drv32 : sz_drv32 / 100 * n_lba32; /* Size in percentage? */ + if (s_lba32 + n_lba32 > sz_drv32 || s_lba32 + n_lba32 < s_lba32) n_lba32 = sz_drv32 - s_lba32; /* Clip at drive size */ + if (n_lba32 == 0) break; /* End of table or no sector to allocate? */ + + st_dword(pte + PTE_StLba, s_lba32); /* Start LBA */ + st_dword(pte + PTE_SizLba, n_lba32); /* Number of sectors */ + pte[PTE_System] = sys; /* System type */ + + cy = (UINT)(s_lba32 / n_sc / n_hd); /* Start cylinder */ + hd = (BYTE)(s_lba32 / n_sc % n_hd); /* Start head */ + sc = (BYTE)(s_lba32 % n_sc + 1); /* Start sector */ + pte[PTE_StHead] = hd; + pte[PTE_StSec] = (BYTE)((cy >> 2 & 0xC0) | sc); + pte[PTE_StCyl] = (BYTE)cy; + + cy = (UINT)((s_lba32 + n_lba32 - 1) / n_sc / n_hd); /* End cylinder */ + hd = (BYTE)((s_lba32 + n_lba32 - 1) / n_sc % n_hd); /* End head */ + sc = (BYTE)((s_lba32 + n_lba32 - 1) % n_sc + 1); /* End sector */ + pte[PTE_EdHead] = hd; + pte[PTE_EdSec] = (BYTE)((cy >> 2 & 0xC0) | sc); + pte[PTE_EdCyl] = (BYTE)cy; + + pte += SZ_PTE; /* Next entry */ + } + + st_word(buf + BS_55AA, 0xAA55); /* MBR signature */ + if (disk_write(drv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the MBR */ + } + + return FR_OK; +} + + + +FRESULT f_mkfs ( + const TCHAR* path, /* Logical drive number */ + const MKFS_PARM* opt, /* Format options */ + void* work, /* Pointer to working buffer (null: use heap memory) */ + UINT len /* Size of working buffer [byte] */ +) +{ + static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT volume (4Ks unit) */ + static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */ + static const MKFS_PARM defopt = {FM_ANY, 0, 0, 0, 0}; /* Default parameter */ + BYTE fsopt, fsty, sys, *buf, *pte, pdrv, ipart; + WORD ss; /* Sector size */ + DWORD sz_buf, sz_blk, n_clst, pau, nsect, n; + LBA_t sz_vol, b_vol, b_fat, b_data; /* Size of volume, Base LBA of volume, fat, data */ + LBA_t sect, lba[2]; + DWORD sz_rsv, sz_fat, sz_dir, sz_au; /* Size of reserved, fat, dir, data, cluster */ + UINT n_fat, n_root, i; /* Index, Number of FATs and Number of roor dir entries */ + int vol; + DSTATUS ds; + FRESULT fr; + + + /* Check mounted drive and clear work area */ + vol = get_ldnumber(&path); /* Get target logical drive */ + if (vol < 0) return FR_INVALID_DRIVE; + if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear the fs object if mounted */ + pdrv = LD2PD(vol); /* Physical drive */ + ipart = LD2PT(vol); /* Partition (0:create as new, 1..:get from partition table) */ + if (!opt) opt = &defopt; /* Use default parameter if it is not given */ + + /* Get physical drive status (sz_drv, sz_blk, ss) */ + ds = disk_initialize(pdrv); + if (ds & STA_NOINIT) return FR_NOT_READY; + if (ds & STA_PROTECT) return FR_WRITE_PROTECTED; + sz_blk = opt->align; + if (sz_blk == 0 && disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK) sz_blk = 1; + if (sz_blk == 0 || sz_blk > 0x8000 || (sz_blk & (sz_blk - 1))) sz_blk = 1; +#if FF_MAX_SS != FF_MIN_SS + if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR; + if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR; +#else + ss = FF_MAX_SS; +#endif + /* Options for FAT sub-type and FAT parameters */ + fsopt = opt->fmt & (FM_ANY | FM_SFD); + n_fat = (opt->n_fat >= 1 && opt->n_fat <= 2) ? opt->n_fat : 1; + n_root = (opt->n_root >= 1 && opt->n_root <= 32768 && (opt->n_root % (ss / SZDIRE)) == 0) ? opt->n_root : 512; + sz_au = (opt->au_size <= 0x1000000 && (opt->au_size & (opt->au_size - 1)) == 0) ? opt->au_size : 0; + sz_au /= ss; /* Byte --> Sector */ + + /* Get working buffer */ + sz_buf = len / ss; /* Size of working buffer [sector] */ + if (sz_buf == 0) return FR_NOT_ENOUGH_CORE; + buf = (BYTE*)work; /* Working buffer */ +#if FF_USE_LFN == 3 + if (!buf) buf = ff_memalloc(sz_buf * ss); /* Use heap memory for working buffer */ +#endif + if (!buf) return FR_NOT_ENOUGH_CORE; + + /* Determine where the volume to be located (b_vol, sz_vol) */ + b_vol = sz_vol = 0; + if (FF_MULTI_PARTITION && ipart != 0) { /* Is the volume associated with any specific partition? */ + /* Get partition location from the existing partition table */ + if (disk_read(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Load MBR */ + if (ld_word(buf + BS_55AA) != 0xAA55) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if MBR is valid */ +#if FF_LBA64 + if (buf[MBR_Table + PTE_System] == 0xEE) { /* GPT protective MBR? */ + DWORD n_ent, ofs; + QWORD pt_lba; + + /* Get the partition location from GPT */ + if (disk_read(pdrv, buf, 1, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Load GPT header sector (next to MBR) */ + if (!test_gpt_header(buf)) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if GPT header is valid */ + n_ent = ld_dword(buf + GPTH_PtNum); /* Number of entries */ + pt_lba = ld_qword(buf + GPTH_PtOfs); /* Table start sector */ + ofs = i = 0; + while (n_ent) { /* Find MS Basic partition with order of ipart */ + if (ofs == 0 && disk_read(pdrv, buf, pt_lba++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Get PT sector */ + if (!mem_cmp(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, 16) && ++i == ipart) { /* MS basic data partition? */ + b_vol = ld_qword(buf + ofs + GPTE_FstLba); + sz_vol = ld_qword(buf + ofs + GPTE_LstLba) - b_vol + 1; + break; + } + n_ent--; ofs = (ofs + SZ_GPTE) % ss; /* Next entry */ + } + if (n_ent == 0) LEAVE_MKFS(FR_MKFS_ABORTED); /* Partition not found */ + fsopt |= 0x80; /* Partitioning is in GPT */ + } else +#endif + { /* Get the partition location from MBR partition table */ + pte = buf + (MBR_Table + (ipart - 1) * SZ_PTE); + if (ipart > 4 || pte[PTE_System] == 0) LEAVE_MKFS(FR_MKFS_ABORTED); /* No partition? */ + b_vol = ld_dword(pte + PTE_StLba); /* Get volume start sector */ + sz_vol = ld_dword(pte + PTE_SizLba); /* Get volume size */ + } + } else { /* The volume is associated with a physical drive */ + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + if (!(fsopt & FM_SFD)) { /* To be partitioned? */ + /* Create a single-partition on the drive in this function */ +#if FF_LBA64 + if (sz_vol >= FF_MIN_GPT) { /* Which partition type to create, MBR or GPT? */ + fsopt |= 0x80; /* Partitioning is in GPT */ + b_vol = GPT_ALIGN / ss; sz_vol -= b_vol + GPT_ITEMS * SZ_GPTE / ss + 1; /* Estimated partition offset and size */ + } else +#endif + { /* Partitioning is in MBR */ + if (sz_vol > N_SEC_TRACK) { + b_vol = N_SEC_TRACK; sz_vol -= b_vol; /* Estimated partition offset and size */ + } + } + } + } + if (sz_vol < 128) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if volume size is >=128s */ + + /* Now start to create a FAT volume at b_vol and sz_vol */ + + do { /* Pre-determine the FAT type */ + if (FF_FS_EXFAT && (fsopt & FM_EXFAT)) { /* exFAT possible? */ + if ((fsopt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || sz_au > 128) { /* exFAT only, vol >= 64MS or sz_au > 128S ? */ + fsty = FS_EXFAT; break; + } + } +#if FF_LBA64 + if (sz_vol >= 0x100000000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too large volume for FAT/FAT32 */ +#endif + if (sz_au > 128) sz_au = 128; /* Invalid AU for FAT/FAT32? */ + if (fsopt & FM_FAT32) { /* FAT32 possible? */ + if (!(fsopt & FM_FAT)) { /* no-FAT? */ + fsty = FS_FAT32; break; + } + } + if (!(fsopt & FM_FAT)) LEAVE_MKFS(FR_INVALID_PARAMETER); /* no-FAT? */ + fsty = FS_FAT16; + } while (0); + +#if FF_FS_EXFAT + if (fsty == FS_EXFAT) { /* Create an exFAT volume */ + DWORD szb_bit, szb_case, sum, nbit, clu, clen[3]; + WCHAR ch, si; + UINT j, st; + + if (sz_vol < 0x1000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume for exFAT? */ +#if FF_USE_TRIM + lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */ + disk_ioctl(pdrv, CTRL_TRIM, lba); +#endif + /* Determine FAT location, data location and number of clusters */ + if (sz_au == 0) { /* AU auto-selection */ + sz_au = 8; + if (sz_vol >= 0x80000) sz_au = 64; /* >= 512Ks */ + if (sz_vol >= 0x4000000) sz_au = 256; /* >= 64Ms */ + } + b_fat = b_vol + 32; /* FAT start at offset 32 */ + sz_fat = (DWORD)((sz_vol / sz_au + 2) * 4 + ss - 1) / ss; /* Number of FAT sectors */ + b_data = (b_fat + sz_fat + sz_blk - 1) & ~((LBA_t)sz_blk - 1); /* Align data area to the erase block boundary */ + if (b_data - b_vol >= sz_vol / 2) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */ + n_clst = (DWORD)(sz_vol - (b_data - b_vol)) / sz_au; /* Number of clusters */ + if (n_clst <16) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too few clusters? */ + if (n_clst > MAX_EXFAT) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters? */ + + szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */ + clen[0] = (szb_bit + sz_au * ss - 1) / (sz_au * ss); /* Number of allocation bitmap clusters */ + + /* Create a compressed up-case table */ + sect = b_data + sz_au * clen[0]; /* Table start sector */ + sum = 0; /* Table checksum to be stored in the 82 entry */ + st = 0; si = 0; i = 0; j = 0; szb_case = 0; + do { + switch (st) { + case 0: + ch = (WCHAR)ff_wtoupper(si); /* Get an up-case char */ + if (ch != si) { + si++; break; /* Store the up-case char if exist */ + } + for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case block */ + if (j >= 128) { + ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 chars */ + } + st = 1; /* Do not compress short run */ + /* FALLTHROUGH */ + case 1: + ch = si++; /* Fill the short run */ + if (--j == 0) st = 0; + break; + + default: + ch = (WCHAR)j; si += (WCHAR)j; /* Number of chars to skip */ + st = 0; + } + sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put it into the write buffer */ + sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum); + i += 2; szb_case += 2; + if (si == 0 || i == sz_buf * ss) { /* Write buffered data when buffer full or end of process */ + n = (i + ss - 1) / ss; + if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + sect += n; i = 0; + } + } while (si); + clen[1] = (szb_case + sz_au * ss - 1) / (sz_au * ss); /* Number of up-case table clusters */ + clen[2] = 1; /* Number of root dir clusters */ + + /* Initialize the allocation bitmap */ + sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of bitmap sectors */ + nbit = clen[0] + clen[1] + clen[2]; /* Number of clusters in-use by system (bitmap, up-case and root-dir) */ + do { + mem_set(buf, 0, sz_buf * ss); /* Initialize bitmap buffer */ + for (i = 0; nbit != 0 && i / 8 < sz_buf * ss; buf[i / 8] |= 1 << (i % 8), i++, nbit--) ; /* Mark used clusters */ + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ + if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + sect += n; nsect -= n; + } while (nsect); + + /* Initialize the FAT */ + sect = b_fat; nsect = sz_fat; /* Start of FAT and number of FAT sectors */ + j = nbit = clu = 0; + do { + mem_set(buf, 0, sz_buf * ss); i = 0; /* Clear work area and reset write offset */ + if (clu == 0) { /* Initialize FAT [0] and FAT[1] */ + st_dword(buf + i, 0xFFFFFFF8); i += 4; clu++; + st_dword(buf + i, 0xFFFFFFFF); i += 4; clu++; + } + do { /* Create chains of bitmap, up-case and root dir */ + while (nbit != 0 && i < sz_buf * ss) { /* Create a chain */ + st_dword(buf + i, (nbit > 1) ? clu + 1 : 0xFFFFFFFF); + i += 4; clu++; nbit--; + } + if (nbit == 0 && j < 3) nbit = clen[j++]; /* Get next chain length */ + } while (nbit != 0 && i < sz_buf * ss); + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ + if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + sect += n; nsect -= n; + } while (nsect); + + /* Initialize the root directory */ + mem_set(buf, 0, sz_buf * ss); + buf[SZDIRE * 0 + 0] = ET_VLABEL; /* Volume label entry (no label) */ + buf[SZDIRE * 1 + 0] = ET_BITMAP; /* Bitmap entry */ + st_dword(buf + SZDIRE * 1 + 20, 2); /* cluster */ + st_dword(buf + SZDIRE * 1 + 24, szb_bit); /* size */ + buf[SZDIRE * 2 + 0] = ET_UPCASE; /* Up-case table entry */ + st_dword(buf + SZDIRE * 2 + 4, sum); /* sum */ + st_dword(buf + SZDIRE * 2 + 20, 2 + clen[0]); /* cluster */ + st_dword(buf + SZDIRE * 2 + 24, szb_case); /* size */ + sect = b_data + sz_au * (clen[0] + clen[1]); nsect = sz_au; /* Start of the root directory and number of sectors */ + do { /* Fill root directory sectors */ + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + mem_set(buf, 0, ss); /* Rest of entries are filled with zero */ + sect += n; nsect -= n; + } while (nsect); + + /* Create two set of the exFAT VBR blocks */ + sect = b_vol; + for (n = 0; n < 2; n++) { + /* Main record (+0) */ + mem_set(buf, 0, ss); + mem_cpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11); /* Boot jump code (x86), OEM name */ + st_qword(buf + BPB_VolOfsEx, b_vol); /* Volume offset in the physical drive [sector] */ + st_qword(buf + BPB_TotSecEx, sz_vol); /* Volume size [sector] */ + st_dword(buf + BPB_FatOfsEx, (DWORD)(b_fat - b_vol)); /* FAT offset [sector] */ + st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */ + st_dword(buf + BPB_DataOfsEx, (DWORD)(b_data - b_vol)); /* Data offset [sector] */ + st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */ + st_dword(buf + BPB_RootClusEx, 2 + clen[0] + clen[1]); /* Root dir cluster # */ + st_dword(buf + BPB_VolIDEx, GET_FATTIME()); /* VSN */ + st_word(buf + BPB_FSVerEx, 0x100); /* Filesystem version (1.00) */ + for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */ + for (buf[BPB_SecPerClusEx] = 0, i = sz_au; i >>= 1; buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */ + buf[BPB_NumFATsEx] = 1; /* Number of FATs */ + buf[BPB_DrvNumEx] = 0x80; /* Drive number (for int13) */ + st_word(buf + BS_BootCodeEx, 0xFEEB); /* Boot code (x86) */ + st_word(buf + BS_55AA, 0xAA55); /* Signature (placed here regardless of sector size) */ + for (i = sum = 0; i < ss; i++) { /* VBR checksum */ + if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum); + } + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + /* Extended bootstrap record (+1..+8) */ + mem_set(buf, 0, ss); + st_word(buf + ss - 2, 0xAA55); /* Signature (placed at end of sector) */ + for (j = 1; j < 9; j++) { + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + } + /* OEM/Reserved record (+9..+10) */ + mem_set(buf, 0, ss); + for ( ; j < 11; j++) { + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + } + /* Sum record (+11) */ + for (i = 0; i < ss; i += 4) st_dword(buf + i, sum); /* Fill with checksum value */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + } + + } else +#endif /* FF_FS_EXFAT */ + { /* Create an FAT/FAT32 volume */ + do { + pau = sz_au; + /* Pre-determine number of clusters and FAT sub-type */ + if (fsty == FS_FAT32) { /* FAT32 volume */ + if (pau == 0) { /* AU auto-selection */ + n = (DWORD)sz_vol / 0x20000; /* Volume size in unit of 128KS */ + for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; i++, pau <<= 1) ; /* Get from table */ + } + n_clst = (DWORD)sz_vol / pau; /* Number of clusters */ + sz_fat = (n_clst * 4 + 8 + ss - 1) / ss; /* FAT size [sector] */ + sz_rsv = 32; /* Number of reserved sectors */ + sz_dir = 0; /* No static directory */ + if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) LEAVE_MKFS(FR_MKFS_ABORTED); + } else { /* FAT volume */ + if (pau == 0) { /* au auto-selection */ + n = (DWORD)sz_vol / 0x1000; /* Volume size in unit of 4KS */ + for (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, pau <<= 1) ; /* Get from table */ + } + n_clst = (DWORD)sz_vol / pau; + if (n_clst > MAX_FAT12) { + n = n_clst * 2 + 4; /* FAT size [byte] */ + } else { + fsty = FS_FAT12; + n = (n_clst * 3 + 1) / 2 + 3; /* FAT size [byte] */ + } + sz_fat = (n + ss - 1) / ss; /* FAT size [sector] */ + sz_rsv = 1; /* Number of reserved sectors */ + sz_dir = (DWORD)n_root * SZDIRE / ss; /* Root dir size [sector] */ + } + b_fat = b_vol + sz_rsv; /* FAT base */ + b_data = b_fat + sz_fat * n_fat + sz_dir; /* Data base */ + + /* Align data area to erase block boundary (for flash memory media) */ + n = (DWORD)(((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data); /* Sectors to next nearest from current data base */ + if (fsty == FS_FAT32) { /* FAT32: Move FAT */ + sz_rsv += n; b_fat += n; + } else { /* FAT: Expand FAT */ + if (n % n_fat) { /* Adjust fractional error if needed */ + n--; sz_rsv++; b_fat++; + } + sz_fat += n / n_fat; + } + + /* Determine number of clusters and final check of validity of the FAT sub-type */ + if (sz_vol < b_data + pau * 16 - b_vol) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */ + n_clst = ((DWORD)sz_vol - sz_rsv - sz_fat * n_fat - sz_dir) / pau; + if (fsty == FS_FAT32) { + if (n_clst <= MAX_FAT16) { /* Too few clusters for FAT32? */ + if (sz_au == 0 && (sz_au = pau / 2) != 0) continue; /* Adjust cluster size and retry */ + LEAVE_MKFS(FR_MKFS_ABORTED); + } + } + if (fsty == FS_FAT16) { + if (n_clst > MAX_FAT16) { /* Too many clusters for FAT16 */ + if (sz_au == 0 && (pau * 2) <= 64) { + sz_au = pau * 2; continue; /* Adjust cluster size and retry */ + } + if ((fsopt & FM_FAT32)) { + fsty = FS_FAT32; continue; /* Switch type to FAT32 and retry */ + } + if (sz_au == 0 && (sz_au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ + LEAVE_MKFS(FR_MKFS_ABORTED); + } + if (n_clst <= MAX_FAT12) { /* Too few clusters for FAT16 */ + if (sz_au == 0 && (sz_au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ + LEAVE_MKFS(FR_MKFS_ABORTED); + } + } + if (fsty == FS_FAT12 && n_clst > MAX_FAT12) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters for FAT12 */ + + /* Ok, it is the valid cluster configuration */ + break; + } while (1); + +#if FF_USE_TRIM + lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */ + disk_ioctl(pdrv, CTRL_TRIM, lba); +#endif + /* Create FAT VBR */ + mem_set(buf, 0, ss); + mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code (x86), OEM name */ + st_word(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */ + buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */ + st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */ + buf[BPB_NumFATs] = (BYTE)n_fat; /* Number of FATs */ + st_word(buf + BPB_RootEntCnt, (WORD)((fsty == FS_FAT32) ? 0 : n_root)); /* Number of root directory entries */ + if (sz_vol < 0x10000) { + st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume size in 16-bit LBA */ + } else { + st_dword(buf + BPB_TotSec32, (DWORD)sz_vol); /* Volume size in 32-bit LBA */ + } + buf[BPB_Media] = 0xF8; /* Media descriptor byte */ + st_word(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */ + st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */ + st_dword(buf + BPB_HiddSec, (DWORD)b_vol); /* Volume offset in the physical drive [sector] */ + if (fsty == FS_FAT32) { + st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */ + st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size [sector] */ + st_dword(buf + BPB_RootClus32, 2); /* Root directory cluster # (2) */ + st_word(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */ + st_word(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */ + buf[BS_DrvNum32] = 0x80; /* Drive number (for int13) */ + buf[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ + } else { + st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */ + st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */ + buf[BS_DrvNum] = 0x80; /* Drive number (for int13) */ + buf[BS_BootSig] = 0x29; /* Extended boot signature */ + mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ + } + st_word(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */ + if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the VBR sector */ + + /* Create FSINFO record if needed */ + if (fsty == FS_FAT32) { + disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */ + mem_set(buf, 0, ss); + st_dword(buf + FSI_LeadSig, 0x41615252); + st_dword(buf + FSI_StrucSig, 0x61417272); + st_dword(buf + FSI_Free_Count, n_clst - 1); /* Number of free clusters */ + st_dword(buf + FSI_Nxt_Free, 2); /* Last allocated cluster# */ + st_word(buf + BS_55AA, 0xAA55); + disk_write(pdrv, buf, b_vol + 7, 1); /* Write backup FSINFO (VBR + 7) */ + disk_write(pdrv, buf, b_vol + 1, 1); /* Write original FSINFO (VBR + 1) */ + } + + /* Initialize FAT area */ + mem_set(buf, 0, sz_buf * ss); + sect = b_fat; /* FAT start sector */ + for (i = 0; i < n_fat; i++) { /* Initialize FATs each */ + if (fsty == FS_FAT32) { + st_dword(buf + 0, 0xFFFFFFF8); /* FAT[0] */ + st_dword(buf + 4, 0xFFFFFFFF); /* FAT[1] */ + st_dword(buf + 8, 0x0FFFFFFF); /* FAT[2] (root directory) */ + } else { + st_dword(buf + 0, (fsty == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8); /* FAT[0] and FAT[1] */ + } + nsect = sz_fat; /* Number of FAT sectors */ + do { /* Fill FAT sectors */ + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + mem_set(buf, 0, ss); /* Rest of FAT all are cleared */ + sect += n; nsect -= n; + } while (nsect); + } + + /* Initialize root directory (fill with zero) */ + nsect = (fsty == FS_FAT32) ? pau : sz_dir; /* Number of root directory sectors */ + do { + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + sect += n; nsect -= n; + } while (nsect); + } + + /* A FAT volume has been created here */ + + /* Determine system ID in the MBR partition table */ + if (FF_FS_EXFAT && fsty == FS_EXFAT) { + sys = 0x07; /* exFAT */ + } else { + if (fsty == FS_FAT32) { + sys = 0x0C; /* FAT32X */ + } else { + if (sz_vol >= 0x10000) { + sys = 0x06; /* FAT12/16 (large) */ + } else { + sys = (fsty == FS_FAT16) ? 0x04 : 0x01; /* FAT16 : FAT12 */ + } + } + } + + /* Update partition information */ + if (FF_MULTI_PARTITION && ipart != 0) { /* Volume is in the existing partition */ + if (!FF_LBA64 || !(fsopt & 0x80)) { + /* Update system ID in the partition table */ + if (disk_read(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Read the MBR */ + buf[MBR_Table + (ipart - 1) * SZ_PTE + PTE_System] = sys; /* Set system ID */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it back to the MBR */ + } + } else { /* Volume as a new single partition */ + if (!(fsopt & FM_SFD)) { /* Create partition table if not in SFD */ + lba[0] = sz_vol, lba[1] = 0; + fr = create_partition(pdrv, lba, sys, buf); + if (fr != FR_OK) LEAVE_MKFS(fr); + } + } + + if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + + LEAVE_MKFS(FR_OK); +} + + + + +#if FF_MULTI_PARTITION +/*-----------------------------------------------------------------------*/ +/* Create Partition Table on the Physical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_fdisk ( + BYTE pdrv, /* Physical drive number */ + const LBA_t ptbl[], /* Pointer to the size table for each partitions */ + void* work /* Pointer to the working buffer (null: use heap memory) */ +) +{ + BYTE *buf = (BYTE*)work; + DSTATUS stat; + + + stat = disk_initialize(pdrv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; +#if FF_USE_LFN == 3 + if (!buf) buf = ff_memalloc(FF_MAX_SS); /* Use heap memory for working buffer */ +#endif + if (!buf) return FR_NOT_ENOUGH_CORE; + + LEAVE_MKFS(create_partition(pdrv, ptbl, 0x07, buf)); +} + +#endif /* FF_MULTI_PARTITION */ +#endif /* !FF_FS_READONLY && FF_USE_MKFS */ + + + + +#if FF_USE_STRFUNC +#if FF_USE_LFN && FF_LFN_UNICODE && (FF_STRF_ENCODE < 0 || FF_STRF_ENCODE > 3) +#error Wrong FF_STRF_ENCODE setting +#endif +/*-----------------------------------------------------------------------*/ +/* Get a String from the File */ +/*-----------------------------------------------------------------------*/ + +TCHAR* f_gets ( + TCHAR* buff, /* Pointer to the buffer to store read string */ + int len, /* Size of string buffer (items) */ + FIL* fp /* Pointer to the file object */ +) +{ + int nc = 0; + TCHAR *p = buff; + BYTE s[4]; + UINT rc; + DWORD dc; +#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE <= 2 + WCHAR wc; +#endif +#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE == 3 + UINT ct; +#endif + +#if FF_USE_LFN && FF_LFN_UNICODE /* With code conversion (Unicode API) */ + /* Make a room for the character and terminator */ + if (FF_LFN_UNICODE == 1) len -= (FF_STRF_ENCODE == 0) ? 1 : 2; + if (FF_LFN_UNICODE == 2) len -= (FF_STRF_ENCODE == 0) ? 3 : 4; + if (FF_LFN_UNICODE == 3) len -= 1; + while (nc < len) { +#if FF_STRF_ENCODE == 0 /* Read a character in ANSI/OEM */ + f_read(fp, s, 1, &rc); /* Get a code unit */ + if (rc != 1) break; /* EOF? */ + wc = s[0]; + if (dbc_1st((BYTE)wc)) { /* DBC 1st byte? */ + f_read(fp, s, 1, &rc); /* Get DBC 2nd byte */ + if (rc != 1 || !dbc_2nd(s[0])) continue; /* Wrong code? */ + wc = wc << 8 | s[0]; + } + dc = ff_oem2uni(wc, CODEPAGE); /* OEM --> */ + if (dc == 0) continue; +#elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2 /* Read a character in UTF-16LE/BE */ + f_read(fp, s, 2, &rc); /* Get a code unit */ + if (rc != 2) break; /* EOF? */ + dc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1]; + if (IsSurrogateL(dc)) continue; /* Broken surrogate pair? */ + if (IsSurrogateH(dc)) { /* High surrogate? */ + f_read(fp, s, 2, &rc); /* Get low surrogate */ + if (rc != 2) break; /* EOF? */ + wc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1]; + if (!IsSurrogateL(wc)) continue; /* Broken surrogate pair? */ + dc = ((dc & 0x3FF) + 0x40) << 10 | (wc & 0x3FF); /* Merge surrogate pair */ + } +#else /* Read a character in UTF-8 */ + f_read(fp, s, 1, &rc); /* Get a code unit */ + if (rc != 1) break; /* EOF? */ + dc = s[0]; + if (dc >= 0x80) { /* Multi-byte sequence? */ + ct = 0; + if ((dc & 0xE0) == 0xC0) { dc &= 0x1F; ct = 1; } /* 2-byte sequence? */ + if ((dc & 0xF0) == 0xE0) { dc &= 0x0F; ct = 2; } /* 3-byte sequence? */ + if ((dc & 0xF8) == 0xF0) { dc &= 0x07; ct = 3; } /* 4-byte sequence? */ + if (ct == 0) continue; + f_read(fp, s, ct, &rc); /* Get trailing bytes */ + if (rc != ct) break; + rc = 0; + do { /* Merge the byte sequence */ + if ((s[rc] & 0xC0) != 0x80) break; + dc = dc << 6 | (s[rc] & 0x3F); + } while (++rc < ct); + if (rc != ct || dc < 0x80 || IsSurrogate(dc) || dc >= 0x110000) continue; /* Wrong encoding? */ + } +#endif + /* A code point is avaialble in dc to be output */ + + if (FF_USE_STRFUNC == 2 && dc == '\r') continue; /* Strip \r off if needed */ +#if FF_LFN_UNICODE == 1 || FF_LFN_UNICODE == 3 /* Output it in UTF-16/32 encoding */ + if (FF_LFN_UNICODE == 1 && dc >= 0x10000) { /* Out of BMP at UTF-16? */ + *p++ = (TCHAR)(0xD800 | ((dc >> 10) - 0x40)); nc++; /* Make and output high surrogate */ + dc = 0xDC00 | (dc & 0x3FF); /* Make low surrogate */ + } + *p++ = (TCHAR)dc; nc++; + if (dc == '\n') break; /* End of line? */ +#elif FF_LFN_UNICODE == 2 /* Output it in UTF-8 encoding */ + if (dc < 0x80) { /* Single byte? */ + *p++ = (TCHAR)dc; + nc++; + if (dc == '\n') break; /* End of line? */ + } else { + if (dc < 0x800) { /* 2-byte sequence? */ + *p++ = (TCHAR)(0xC0 | (dc >> 6 & 0x1F)); + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); + nc += 2; + } else { + if (dc < 0x10000) { /* 3-byte sequence? */ + *p++ = (TCHAR)(0xE0 | (dc >> 12 & 0x0F)); + *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F)); + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); + nc += 3; + } else { /* 4-byte sequence? */ + *p++ = (TCHAR)(0xF0 | (dc >> 18 & 0x07)); + *p++ = (TCHAR)(0x80 | (dc >> 12 & 0x3F)); + *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F)); + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); + nc += 4; + } + } + } +#endif + } + +#else /* Byte-by-byte read without any conversion (ANSI/OEM API) */ + len -= 1; /* Make a room for the terminator */ + while (nc < len) { + f_read(fp, s, 1, &rc); /* Get a byte */ + if (rc != 1) break; /* EOF? */ + dc = s[0]; + if (FF_USE_STRFUNC == 2 && dc == '\r') continue; + *p++ = (TCHAR)dc; nc++; + if (dc == '\n') break; + } +#endif + + *p = 0; /* Terminate the string */ + return nc ? buff : 0; /* When no data read due to EOF or error, return with error. */ +} + + + + +#if !FF_FS_READONLY +#include <stdarg.h> +/*-----------------------------------------------------------------------*/ +/* Put a Character to the File (sub-functions) */ +/*-----------------------------------------------------------------------*/ + +/* Putchar output buffer and work area */ + +typedef struct { + FIL *fp; /* Ptr to the writing file */ + int idx, nchr; /* Write index of buf[] (-1:error), number of encoding units written */ +#if FF_USE_LFN && FF_LFN_UNICODE == 1 + WCHAR hs; +#elif FF_USE_LFN && FF_LFN_UNICODE == 2 + BYTE bs[4]; + UINT wi, ct; +#endif + BYTE buf[64]; /* Write buffer */ +} putbuff; + + +/* Buffered write with code conversion */ + +static void putc_bfd (putbuff* pb, TCHAR c) +{ + UINT n; + int i, nc; +#if FF_USE_LFN && FF_LFN_UNICODE + WCHAR hs, wc; +#if FF_LFN_UNICODE == 2 + DWORD dc; + const TCHAR *tp; +#endif +#endif + + if (FF_USE_STRFUNC == 2 && c == '\n') { /* LF -> CRLF conversion */ + putc_bfd(pb, '\r'); + } + + i = pb->idx; /* Write index of pb->buf[] */ + if (i < 0) return; + nc = pb->nchr; /* Write unit counter */ + +#if FF_USE_LFN && FF_LFN_UNICODE +#if FF_LFN_UNICODE == 1 /* UTF-16 input */ + if (IsSurrogateH(c)) { /* High surrogate? */ + pb->hs = c; return; /* Save it for next */ + } + hs = pb->hs; pb->hs = 0; + if (hs != 0) { /* There is a leading high surrogate */ + if (!IsSurrogateL(c)) hs = 0; /* Discard high surrogate if not a surrogate pair */ + } else { + if (IsSurrogateL(c)) return; /* Discard stray low surrogate */ + } + wc = c; +#elif FF_LFN_UNICODE == 2 /* UTF-8 input */ + for (;;) { + if (pb->ct == 0) { /* Out of multi-byte sequence? */ + pb->bs[pb->wi = 0] = (BYTE)c; /* Save 1st byte */ + if ((BYTE)c < 0x80) break; /* Single byte? */ + if (((BYTE)c & 0xE0) == 0xC0) pb->ct = 1; /* 2-byte sequence? */ + if (((BYTE)c & 0xF0) == 0xE0) pb->ct = 2; /* 3-byte sequence? */ + if (((BYTE)c & 0xF1) == 0xF0) pb->ct = 3; /* 4-byte sequence? */ + return; + } else { /* In the multi-byte sequence */ + if (((BYTE)c & 0xC0) != 0x80) { /* Broken sequence? */ + pb->ct = 0; continue; + } + pb->bs[++pb->wi] = (BYTE)c; /* Save the trailing byte */ + if (--pb->ct == 0) break; /* End of multi-byte sequence? */ + return; + } + } + tp = (const TCHAR*)pb->bs; + dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */ + if (dc == 0xFFFFFFFF) return; /* Wrong code? */ + wc = (WCHAR)dc; + hs = (WCHAR)(dc >> 16); +#elif FF_LFN_UNICODE == 3 /* UTF-32 input */ + if (IsSurrogate(c) || c >= 0x110000) return; /* Discard invalid code */ + if (c >= 0x10000) { /* Out of BMP? */ + hs = (WCHAR)(0xD800 | ((c >> 10) - 0x40)); /* Make high surrogate */ + wc = 0xDC00 | (c & 0x3FF); /* Make low surrogate */ + } else { + hs = 0; + wc = (WCHAR)c; + } +#endif + /* A code point in UTF-16 is available in hs and wc */ + +#if FF_STRF_ENCODE == 1 /* Write a code point in UTF-16LE */ + if (hs != 0) { /* Surrogate pair? */ + st_word(&pb->buf[i], hs); + i += 2; + nc++; + } + st_word(&pb->buf[i], wc); + i += 2; +#elif FF_STRF_ENCODE == 2 /* Write a code point in UTF-16BE */ + if (hs != 0) { /* Surrogate pair? */ + pb->buf[i++] = (BYTE)(hs >> 8); + pb->buf[i++] = (BYTE)hs; + nc++; + } + pb->buf[i++] = (BYTE)(wc >> 8); + pb->buf[i++] = (BYTE)wc; +#elif FF_STRF_ENCODE == 3 /* Write a code point in UTF-8 */ + if (hs != 0) { /* 4-byte sequence? */ + nc += 3; + hs = (hs & 0x3FF) + 0x40; + pb->buf[i++] = (BYTE)(0xF0 | hs >> 8); + pb->buf[i++] = (BYTE)(0x80 | (hs >> 2 & 0x3F)); + pb->buf[i++] = (BYTE)(0x80 | (hs & 3) << 4 | (wc >> 6 & 0x0F)); + pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F)); + } else { + if (wc < 0x80) { /* Single byte? */ + pb->buf[i++] = (BYTE)wc; + } else { + if (wc < 0x800) { /* 2-byte sequence? */ + nc += 1; + pb->buf[i++] = (BYTE)(0xC0 | wc >> 6); + } else { /* 3-byte sequence */ + nc += 2; + pb->buf[i++] = (BYTE)(0xE0 | wc >> 12); + pb->buf[i++] = (BYTE)(0x80 | (wc >> 6 & 0x3F)); + } + pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F)); + } + } +#else /* Write a code point in ANSI/OEM */ + if (hs != 0) return; + wc = ff_uni2oem(wc, CODEPAGE); /* UTF-16 ==> ANSI/OEM */ + if (wc == 0) return; + if (wc >= 0x100) { + pb->buf[i++] = (BYTE)(wc >> 8); nc++; + } + pb->buf[i++] = (BYTE)wc; +#endif + +#else /* ANSI/OEM input (without re-encoding) */ + pb->buf[i++] = (BYTE)c; +#endif + + if (i >= (int)(sizeof pb->buf) - 4) { /* Write buffered characters to the file */ + f_write(pb->fp, pb->buf, (UINT)i, &n); + i = (n == (UINT)i) ? 0 : -1; + } + pb->idx = i; + pb->nchr = nc + 1; +} + + +/* Flush remaining characters in the buffer */ + +static int putc_flush (putbuff* pb) +{ + UINT nw; + + if ( pb->idx >= 0 /* Flush buffered characters to the file */ + && f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK + && (UINT)pb->idx == nw) return pb->nchr; + return -1; +} + + +/* Initialize write buffer */ + +static void putc_init (putbuff* pb, FIL* fp) +{ + mem_set(pb, 0, sizeof (putbuff)); + pb->fp = fp; +} + + + +int f_putc ( + TCHAR c, /* A character to be output */ + FIL* fp /* Pointer to the file object */ +) +{ + putbuff pb; + + + putc_init(&pb, fp); + putc_bfd(&pb, c); /* Put the character */ + return putc_flush(&pb); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a String to the File */ +/*-----------------------------------------------------------------------*/ + +int f_puts ( + const TCHAR* str, /* Pointer to the string to be output */ + FIL* fp /* Pointer to the file object */ +) +{ + putbuff pb; + + + putc_init(&pb, fp); + while (*str) putc_bfd(&pb, *str++); /* Put the string */ + return putc_flush(&pb); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a Formatted String to the File */ +/*-----------------------------------------------------------------------*/ + +int f_printf ( + FIL* fp, /* Pointer to the file object */ + const TCHAR* fmt, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + putbuff pb; + BYTE f, r; + UINT i, j, w; + DWORD v; + TCHAR c, d, str[32], *p; + + + putc_init(&pb, fp); + + va_start(arp, fmt); + + for (;;) { + c = *fmt++; + if (c == 0) break; /* End of string */ + if (c != '%') { /* Non escape character */ + putc_bfd(&pb, c); + continue; + } + w = f = 0; + c = *fmt++; + if (c == '0') { /* Flag: '0' padding */ + f = 1; c = *fmt++; + } else { + if (c == '-') { /* Flag: left justified */ + f = 2; c = *fmt++; + } + } + if (c == '*') { /* Minimum width by argument */ + w = va_arg(arp, int); + c = *fmt++; + } else { + while (IsDigit(c)) { /* Minimum width */ + w = w * 10 + c - '0'; + c = *fmt++; + } + } + if (c == 'l' || c == 'L') { /* Type prefix: Size is long int */ + f |= 4; c = *fmt++; + } + if (c == 0) break; + d = c; + if (IsLower(d)) d -= 0x20; + switch (d) { /* Atgument type is... */ + case 'S': /* String */ + p = va_arg(arp, TCHAR*); + for (j = 0; p[j]; j++) ; + if (!(f & 2)) { /* Right padded */ + while (j++ < w) putc_bfd(&pb, ' ') ; + } + while (*p) putc_bfd(&pb, *p++) ; /* String body */ + while (j++ < w) putc_bfd(&pb, ' ') ; /* Left padded */ + continue; + + case 'C': /* Character */ + putc_bfd(&pb, (TCHAR)va_arg(arp, int)); + continue; + + case 'B': /* Unsigned binary */ + r = 2; + break; + + case 'O': /* Unsigned octal */ + r = 8; + break; + + case 'D': /* Signed decimal */ + case 'U': /* Unsigned decimal */ + r = 10; + break; + + case 'X': /* Unsigned hexdecimal */ + r = 16; + break; + + default: /* Unknown type (pass-through) */ + putc_bfd(&pb, c); continue; + } + + /* Get an argument and put it in numeral */ + v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int)); + if (d == 'D' && (v & 0x80000000)) { + v = 0 - v; + f |= 8; + } + i = 0; + do { + d = (TCHAR)(v % r); v /= r; + if (d > 9) d += (c == 'x') ? 0x27 : 0x07; + str[i++] = d + '0'; + } while (v && i < sizeof str / sizeof *str); + if (f & 8) str[i++] = '-'; + j = i; d = (f & 1) ? '0' : ' '; + if (!(f & 2)) { + while (j++ < w) putc_bfd(&pb, d); /* Right pad */ + } + do { + putc_bfd(&pb, str[--i]); /* Number body */ + } while (i); + while (j++ < w) putc_bfd(&pb, d); /* Left pad */ + } + + va_end(arp); + + return putc_flush(&pb); +} + +#endif /* !FF_FS_READONLY */ +#endif /* FF_USE_STRFUNC */ + + + +#if FF_CODE_PAGE == 0 +/*-----------------------------------------------------------------------*/ +/* Set Active Codepage for the Path Name */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_setcp ( + WORD cp /* Value to be set as active code page */ +) +{ + static const WORD validcp[] = { 437, 720, 737, 771, 775, 850, 852, 857, 860, 861, 862, 863, 864, 865, 866, 869, 932, 936, 949, 950, 0}; + static const BYTE* const tables[] = {Ct437, Ct720, Ct737, Ct771, Ct775, Ct850, Ct852, Ct857, Ct860, Ct861, Ct862, Ct863, Ct864, Ct865, Ct866, Ct869, Dc932, Dc936, Dc949, Dc950, 0}; + UINT i; + + + for (i = 0; validcp[i] != 0 && validcp[i] != cp; i++) ; /* Find the code page */ + if (validcp[i] != cp) return FR_INVALID_PARAMETER; /* Not found? */ + + CodePage = cp; + if (cp >= 900) { /* DBCS */ + ExCvt = 0; + DbcTbl = tables[i]; + } else { /* SBCS */ + ExCvt = tables[i]; + DbcTbl = 0; + } + return FR_OK; +} +#endif /* FF_CODE_PAGE == 0 */ diff --git a/fatfs/ff.h b/fatfs/ff.h new file mode 100644 index 0000000..ac41fa4 --- /dev/null +++ b/fatfs/ff.h @@ -0,0 +1,425 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - Generic FAT Filesystem module R0.14a / +/-----------------------------------------------------------------------------/ +/ +/ Copyright (C) 2020, ChaN, all right reserved. +/ +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: +/ +/ 1. Redistributions of source code must retain the above copyright notice, +/ this condition and the following disclaimer. +/ +/ This software is provided by the copyright holder and contributors "AS IS" +/ and any warranties related to this software are DISCLAIMED. +/ The copyright owner or contributors be NOT LIABLE for any damages caused +/ by use of this software. +/ +/ SPDX-License-Identifier: BSD-1-Clause +/ +/----------------------------------------------------------------------------*/ + + +#ifndef FF_DEFINED +#define FF_DEFINED 80196 /* Revision ID */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ffconf.h" /* FatFs configuration options */ + +#if FF_DEFINED != FFCONF_DEF +#error Wrong configuration file (ffconf.h). +#endif + + +/* Integer types used for FatFs API */ + +#if defined(_WIN32) /* Main development platform */ +#define FF_INTDEF 2 +#include <windows.h> +typedef unsigned __int64 QWORD; +#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */ +#define FF_INTDEF 2 +#include <stdint.h> +typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ +typedef unsigned char BYTE; /* char must be 8-bit */ +typedef uint16_t WORD; /* 16-bit unsigned integer */ +typedef uint32_t DWORD; /* 32-bit unsigned integer */ +typedef uint64_t QWORD; /* 64-bit unsigned integer */ +typedef WORD WCHAR; /* UTF-16 character type */ +#else /* Earlier than C99 */ +#define FF_INTDEF 1 +typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ +typedef unsigned char BYTE; /* char must be 8-bit */ +typedef unsigned short WORD; /* 16-bit unsigned integer */ +typedef unsigned long DWORD; /* 32-bit unsigned integer */ +typedef WORD WCHAR; /* UTF-16 character type */ +#endif + + +/* Definitions of volume management */ + +#if FF_MULTI_PARTITION /* Multiple partition configuration */ +typedef struct { + BYTE pd; /* Physical drive number */ + BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ +} PARTITION; +extern PARTITION VolToPart[]; /* Volume - Partition mapping table */ +#endif + +#if FF_STR_VOLUME_ID +#ifndef FF_VOLUME_STRS +extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */ +#endif +#endif + + + +/* Type of path name strings on FatFs API */ + +#ifndef _INC_TCHAR +#define _INC_TCHAR + +#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */ +typedef WCHAR TCHAR; +#define _T(x) L ## x +#define _TEXT(x) L ## x +#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */ +typedef char TCHAR; +#define _T(x) u8 ## x +#define _TEXT(x) u8 ## x +#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */ +typedef DWORD TCHAR; +#define _T(x) U ## x +#define _TEXT(x) U ## x +#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3) +#error Wrong FF_LFN_UNICODE setting +#else /* ANSI/OEM code in SBCS/DBCS */ +typedef char TCHAR; +#define _T(x) x +#define _TEXT(x) x +#endif + +#endif + + + +/* Type of file size and LBA variables */ + +#if FF_FS_EXFAT +#if FF_INTDEF != 2 +#error exFAT feature wants C99 or later +#endif +typedef QWORD FSIZE_t; +#if FF_LBA64 +typedef QWORD LBA_t; +#else +typedef DWORD LBA_t; +#endif +#else +#if FF_LBA64 +#error exFAT needs to be enabled when enable 64-bit LBA +#endif +typedef DWORD FSIZE_t; +typedef DWORD LBA_t; +#endif + + + +/* Filesystem object structure (FATFS) */ + +typedef struct { + BYTE fs_type; /* Filesystem type (0:not mounted) */ + BYTE pdrv; /* Associated physical drive */ + BYTE n_fats; /* Number of FATs (1 or 2) */ + BYTE wflag; /* win[] flag (b0:dirty) */ + BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ + WORD id; /* Volume mount ID */ + WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ + WORD csize; /* Cluster size [sectors] */ +#if FF_MAX_SS != FF_MIN_SS + WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */ +#endif +#if FF_USE_LFN + WCHAR* lfnbuf; /* LFN working buffer */ +#endif +#if FF_FS_EXFAT + BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */ +#endif +#if FF_FS_REENTRANT + FF_SYNC_t sobj; /* Identifier of sync object */ +#endif +#if !FF_FS_READONLY + DWORD last_clst; /* Last allocated cluster */ + DWORD free_clst; /* Number of free clusters */ +#endif +#if FF_FS_RPATH + DWORD cdir; /* Current directory start cluster (0:root) */ +#if FF_FS_EXFAT + DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */ + DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */ + DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */ +#endif +#endif + DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */ + DWORD fsize; /* Size of an FAT [sectors] */ + LBA_t volbase; /* Volume base sector */ + LBA_t fatbase; /* FAT base sector */ + LBA_t dirbase; /* Root directory base sector/cluster */ + LBA_t database; /* Data base sector */ +#if FF_FS_EXFAT + LBA_t bitbase; /* Allocation bitmap base sector */ +#endif + LBA_t winsect; /* Current sector appearing in the win[] */ + BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */ +} FATFS; + + + +/* Object ID and allocation information (FFOBJID) */ + +typedef struct { + FATFS* fs; /* Pointer to the hosting volume of this object */ + WORD id; /* Hosting volume mount ID */ + BYTE attr; /* Object attribute */ + BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */ + DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */ + FSIZE_t objsize; /* Object size (valid when sclust != 0) */ +#if FF_FS_EXFAT + DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */ + DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */ + DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */ + DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */ + DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */ +#endif +#if FF_FS_LOCK + UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ +#endif +} FFOBJID; + + + +/* File object structure (FIL) */ + +typedef struct { + FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */ + BYTE flag; /* File status flags */ + BYTE err; /* Abort flag (error code) */ + FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */ + DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */ + LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */ +#if !FF_FS_READONLY + LBA_t dir_sect; /* Sector number containing the directory entry (not used at exFAT) */ + BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */ +#endif +#if FF_USE_FASTSEEK + DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */ +#endif +#if !FF_FS_TINY + BYTE buf[FF_MAX_SS]; /* File private data read/write window */ +#endif +} FIL; + + + +/* Directory object structure (DIR) */ + +typedef struct { + FFOBJID obj; /* Object identifier */ + DWORD dptr; /* Current read/write offset */ + DWORD clust; /* Current cluster */ + LBA_t sect; /* Current sector (0:Read operation has terminated) */ + BYTE* dir; /* Pointer to the directory item in the win[] */ + BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */ +#if FF_USE_LFN + DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */ +#endif +#if FF_USE_FIND + const TCHAR* pat; /* Pointer to the name matching pattern */ +#endif +} DIR; + + + +/* File information structure (FILINFO) */ + +typedef struct { + FSIZE_t fsize; /* File size */ + WORD fdate; /* Modified date */ + WORD ftime; /* Modified time */ + BYTE fattrib; /* File attribute */ +#if FF_USE_LFN + TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */ + TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */ +#else + TCHAR fname[12 + 1]; /* File name */ +#endif +} FILINFO; + + + +/* Format parameter structure (MKFS_PARM) */ + +typedef struct { + BYTE fmt; /* Format option (FM_FAT, FM_FAT32, FM_EXFAT and FM_SFD) */ + BYTE n_fat; /* Number of FATs */ + UINT align; /* Data area alignment (sector) */ + UINT n_root; /* Number of root directory entries */ + DWORD au_size; /* Cluster size (byte) */ +} MKFS_PARM; + + + +/* File function return code (FRESULT) */ + +typedef enum { + FR_OK = 0, /* (0) Succeeded */ + FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */ + FR_INT_ERR, /* (2) Assertion failed */ + FR_NOT_READY, /* (3) The physical drive cannot work */ + FR_NO_FILE, /* (4) Could not find the file */ + FR_NO_PATH, /* (5) Could not find the path */ + FR_INVALID_NAME, /* (6) The path name format is invalid */ + FR_DENIED, /* (7) Access denied due to prohibited access or directory full */ + FR_EXIST, /* (8) Access denied due to prohibited access */ + FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ + FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ + FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ + FR_NOT_ENABLED, /* (12) The volume has no work area */ + FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ + FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */ + FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */ + FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */ + FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ + FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */ + FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ +} FRESULT; + + + +/*--------------------------------------------------------------*/ +/* FatFs module application interface */ + +FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ +FRESULT f_close (FIL* fp); /* Close an open file object */ +FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */ +FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */ +FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */ +FRESULT f_truncate (FIL* fp); /* Truncate the file */ +FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */ +FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */ +FRESULT f_closedir (DIR* dp); /* Close an open directory */ +FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */ +FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */ +FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */ +FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */ +FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */ +FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ +FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */ +FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */ +FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */ +FRESULT f_chdir (const TCHAR* path); /* Change current directory */ +FRESULT f_chdrive (const TCHAR* path); /* Change current drive */ +FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */ +FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ +FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */ +FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ +FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ +FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */ +FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ +FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */ +FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */ +FRESULT f_setcp (WORD cp); /* Set current code page */ +int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */ +int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */ +int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */ +TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */ + +#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) +#define f_error(fp) ((fp)->err) +#define f_tell(fp) ((fp)->fptr) +#define f_size(fp) ((fp)->obj.objsize) +#define f_rewind(fp) f_lseek((fp), 0) +#define f_rewinddir(dp) f_readdir((dp), 0) +#define f_rmdir(path) f_unlink(path) +#define f_unmount(path) f_mount(0, path, 0) + + + + +/*--------------------------------------------------------------*/ +/* Additional user defined functions */ + +/* RTC function */ +#if !FF_FS_READONLY && !FF_FS_NORTC +DWORD get_fattime (void); +#endif + +/* LFN support functions */ +#if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */ +WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */ +WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */ +DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */ +#endif +#if FF_USE_LFN == 3 /* Dynamic memory allocation */ +void* ff_memalloc (UINT msize); /* Allocate memory block */ +void ff_memfree (void* mblock); /* Free memory block */ +#endif + +/* Sync functions */ +#if FF_FS_REENTRANT +int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync object */ +int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */ +void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */ +int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */ +#endif + +/* Function for assigning UNIX devices to FatFs library. */ +extern int fatfs_init(char *device); +extern void fatfs_release(void); + +/*--------------------------------------------------------------*/ +/* Flags and offset address */ + + +/* File access mode and open method flags (3rd argument of f_open) */ +#define FA_READ 0x01 +#define FA_WRITE 0x02 +#define FA_OPEN_EXISTING 0x00 +#define FA_CREATE_NEW 0x04 +#define FA_CREATE_ALWAYS 0x08 +#define FA_OPEN_ALWAYS 0x10 +#define FA_OPEN_APPEND 0x30 + +/* Fast seek controls (2nd argument of f_lseek) */ +#define CREATE_LINKMAP ((FSIZE_t)0 - 1) + +/* Format options (2nd argument of f_mkfs) */ +#define FM_FAT 0x01 +#define FM_FAT32 0x02 +#define FM_EXFAT 0x04 +#define FM_ANY 0x07 +#define FM_SFD 0x08 + +/* Filesystem type (FATFS.fs_type) */ +#define FS_FAT12 1 +#define FS_FAT16 2 +#define FS_FAT32 3 +#define FS_EXFAT 4 + +/* File attribute bits for directory entry (FILINFO.fattrib) */ +#define AM_RDO 0x01 /* Read only */ +#define AM_HID 0x02 /* Hidden */ +#define AM_SYS 0x04 /* System */ +#define AM_DIR 0x10 /* Directory */ +#define AM_ARC 0x20 /* Archive */ + + +#ifdef __cplusplus +} +#endif + +#endif /* FF_DEFINED */ diff --git a/fatfs/ffconf.h b/fatfs/ffconf.h new file mode 100644 index 0000000..b85bbb0 --- /dev/null +++ b/fatfs/ffconf.h @@ -0,0 +1,317 @@ +/*---------------------------------------------------------------------------/ +/ FatFs Functional Configurations +/----------------------------------------------------------------------------/ +/ +/ Copyright (C) 2019, ChaN, all right reserved. +/ +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: +/ +/ 1. Redistributions of source code must retain the above copyright notice, +/ this condition and the following disclaimer. +/ +/ This software is provided by the copyright holder and contributors "AS IS" +/ and any warranties related to this software are DISCLAIMED. +/ The copyright owner or contributors be NOT LIABLE for any damages caused +/ by use of this software. +/ +/ SPDX-License-Identifier: BSD-1-Clause +/ +/---------------------------------------------------------------------------*/ + + +#define FFCONF_DEF 80196 /* Revision ID */ + +/*---------------------------------------------------------------------------/ +/ Function Configurations +/---------------------------------------------------------------------------*/ + +#define FF_FS_READONLY 0 +/* This option switches read-only configuration. (0:Read/Write or 1:Read-only) +/ Read-only configuration removes writing API functions, f_write(), f_sync(), +/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() +/ and optional writing functions as well. */ + + +#define FF_FS_MINIMIZE 3 +/* This option defines minimization level to remove some basic API functions. +/ +/ 0: Basic functions are fully enabled. +/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() +/ are removed. +/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. +/ 3: f_lseek() function is removed in addition to 2. */ + + +#define FF_USE_STRFUNC 0 +/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf(). +/ +/ 0: Disable string functions. +/ 1: Enable without LF-CRLF conversion. +/ 2: Enable with LF-CRLF conversion. */ + + +#define FF_USE_FIND 0 +/* This option switches filtered directory read functions, f_findfirst() and +/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ + + +#define FF_USE_MKFS 1 +/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ + + +#define FF_USE_FASTSEEK 0 +/* This option switches fast seek function. (0:Disable or 1:Enable) */ + + +#define FF_USE_EXPAND 0 +/* This option switches f_expand function. (0:Disable or 1:Enable) */ + + +#define FF_USE_CHMOD 0 +/* This option switches attribute manipulation functions, f_chmod() and f_utime(). +/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */ + + +#define FF_USE_LABEL 0 +/* This option switches volume label functions, f_getlabel() and f_setlabel(). +/ (0:Disable or 1:Enable) */ + + +#define FF_USE_FORWARD 0 +/* This option switches f_forward() function. (0:Disable or 1:Enable) */ + + +/*---------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/---------------------------------------------------------------------------*/ + +#define FF_CODE_PAGE 932 +/* This option specifies the OEM code page to be used on the target system. +/ Incorrect code page setting can cause a file open failure. +/ +/ 437 - U.S. +/ 720 - Arabic +/ 737 - Greek +/ 771 - KBL +/ 775 - Baltic +/ 850 - Latin 1 +/ 852 - Latin 2 +/ 855 - Cyrillic +/ 857 - Turkish +/ 860 - Portuguese +/ 861 - Icelandic +/ 862 - Hebrew +/ 863 - Canadian French +/ 864 - Arabic +/ 865 - Nordic +/ 866 - Russian +/ 869 - Greek 2 +/ 932 - Japanese (DBCS) +/ 936 - Simplified Chinese (DBCS) +/ 949 - Korean (DBCS) +/ 950 - Traditional Chinese (DBCS) +/ 0 - Include all code pages above and configured by f_setcp() +*/ + + +#define FF_USE_LFN 0 +#define FF_MAX_LFN 255 +/* The FF_USE_LFN switches the support for LFN (long file name). +/ +/ 0: Disable LFN. FF_MAX_LFN has no effect. +/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. +/ 2: Enable LFN with dynamic working buffer on the STACK. +/ 3: Enable LFN with dynamic working buffer on the HEAP. +/ +/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function +/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and +/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled. +/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can +/ be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN +/ specification. +/ When use stack for the working buffer, take care on stack overflow. When use heap +/ memory for the working buffer, memory management functions, ff_memalloc() and +/ ff_memfree() exemplified in ffsystem.c, need to be added to the project. */ + + +#define FF_LFN_UNICODE 0 +/* This option switches the character encoding on the API when LFN is enabled. +/ +/ 0: ANSI/OEM in current CP (TCHAR = char) +/ 1: Unicode in UTF-16 (TCHAR = WCHAR) +/ 2: Unicode in UTF-8 (TCHAR = char) +/ 3: Unicode in UTF-32 (TCHAR = DWORD) +/ +/ Also behavior of string I/O functions will be affected by this option. +/ When LFN is not enabled, this option has no effect. */ + + +#define FF_LFN_BUF 255 +#define FF_SFN_BUF 12 +/* This set of options defines size of file name members in the FILINFO structure +/ which is used to read out directory items. These values should be suffcient for +/ the file names to read. The maximum possible length of the read file name depends +/ on character encoding. When LFN is not enabled, these options have no effect. */ + + +#define FF_STRF_ENCODE 3 +/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(), +/ f_putc(), f_puts and f_printf() convert the character encoding in it. +/ This option selects assumption of character encoding ON THE FILE to be +/ read/written via those functions. +/ +/ 0: ANSI/OEM in current CP +/ 1: Unicode in UTF-16LE +/ 2: Unicode in UTF-16BE +/ 3: Unicode in UTF-8 +*/ + + +#define FF_FS_RPATH 0 +/* This option configures support for relative path. +/ +/ 0: Disable relative path and remove related functions. +/ 1: Enable relative path. f_chdir() and f_chdrive() are available. +/ 2: f_getcwd() function is available in addition to 1. +*/ + + +/*---------------------------------------------------------------------------/ +/ Drive/Volume Configurations +/---------------------------------------------------------------------------*/ + +#define FF_VOLUMES 1 +/* Number of volumes (logical drives) to be used. (1-10) */ + + +#define FF_STR_VOLUME_ID 0 +#define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" +/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings. +/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive +/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each +/ logical drives. Number of items must not be less than FF_VOLUMES. Valid +/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are +/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is +/ not defined, a user defined volume string table needs to be defined as: +/ +/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",... +*/ + + +#define FF_MULTI_PARTITION 0 +/* This option switches support for multiple volumes on the physical drive. +/ By default (0), each logical drive number is bound to the same physical drive +/ number and only an FAT volume found on the physical drive will be mounted. +/ When this function is enabled (1), each logical drive number can be bound to +/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() +/ funciton will be available. */ + + +#define FF_MIN_SS 512 +#define FF_MAX_SS 512 +/* This set of options configures the range of sector size to be supported. (512, +/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and +/ harddisk. But a larger value may be required for on-board flash memory and some +/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured +/ for variable sector size mode and disk_ioctl() function needs to implement +/ GET_SECTOR_SIZE command. */ + + +#define FF_LBA64 0 +/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable) +/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */ + + +#define FF_MIN_GPT 0x10000000 +/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and +/ f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */ + + +#define FF_USE_TRIM 0 +/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable) +/ To enable Trim function, also CTRL_TRIM command should be implemented to the +/ disk_ioctl() function. */ + + + +/*---------------------------------------------------------------------------/ +/ System Configurations +/---------------------------------------------------------------------------*/ + +#define FF_FS_TINY 0 +/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) +/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes. +/ Instead of private sector buffer eliminated from the file object, common sector +/ buffer in the filesystem object (FATFS) is used for the file data transfer. */ + + +#define FF_FS_EXFAT 0 +/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable) +/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1) +/ Note that enabling exFAT discards ANSI C (C89) compatibility. */ + + +#define FF_FS_NORTC 0 +#define FF_NORTC_MON 1 +#define FF_NORTC_MDAY 1 +#define FF_NORTC_YEAR 2020 +/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have +/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable +/ the timestamp function. Every object modified by FatFs will have a fixed timestamp +/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time. +/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be +/ added to the project to read current time form real-time clock. FF_NORTC_MON, +/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect. +/ These options have no effect in read-only configuration (FF_FS_READONLY = 1). */ + + +#define FF_FS_NOFSINFO 0 +/* If you need to know correct free space on the FAT32 volume, set bit 0 of this +/ option, and f_getfree() function at first time after volume mount will force +/ a full FAT scan. Bit 1 controls the use of last allocated cluster number. +/ +/ bit0=0: Use free cluster count in the FSINFO if available. +/ bit0=1: Do not trust free cluster count in the FSINFO. +/ bit1=0: Use last allocated cluster number in the FSINFO if available. +/ bit1=1: Do not trust last allocated cluster number in the FSINFO. +*/ + + +#define FF_FS_LOCK 0 +/* The option FF_FS_LOCK switches file lock function to control duplicated file open +/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY +/ is 1. +/ +/ 0: Disable file lock function. To avoid volume corruption, application program +/ should avoid illegal open, remove and rename to the open objects. +/ >0: Enable file lock function. The value defines how many files/sub-directories +/ can be opened simultaneously under file lock control. Note that the file +/ lock control is independent of re-entrancy. */ + + +/* #include <somertos.h> // O/S definitions */ +#define FF_FS_REENTRANT 0 +#define FF_FS_TIMEOUT 1000 +#define FF_SYNC_t HANDLE +/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs +/ module itself. Note that regardless of this option, file access to different +/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs() +/ and f_fdisk() function, are always not re-entrant. Only file/directory access +/ to the same volume is under control of this function. +/ +/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect. +/ 1: Enable re-entrancy. Also user provided synchronization handlers, +/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj() +/ function, must be added to the project. Samples are available in +/ option/syscall.c. +/ +/ The FF_FS_TIMEOUT defines timeout period in unit of time tick. +/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, +/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be +/ included somewhere in the scope of ff.h. */ + + + +/*--- End of configuration options ---*/