FatFs源码分析
#include "ff.h"
#include "diskio.h"/* 擴展字符范圍 */
#define _DF1S 0x81
#define _DF1E 0xFE
#define _DS1S 0x40
#define _DS1E 0x7E
#define _DS2S 0x80
#define _DS2E 0xFE/* 字符類型相關宏定義 */
#define IsUpper(c) (((c)>='A')&&((c)<='Z'))
#define IsLower(c) (((c)>='a')&&((c)<='z'))
#define IsDigit(c) (((c)>='0')&&((c)<='9'))/* 是否是擴展字符 */
#define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E)
#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E))/* 文件屬性位 */
#define AM_VOL 0x08 /* 卷標 */
#define AM_LFN 0x0F /* 長文件名 */
#define AM_MASK 0x3F /* 屬性有效位掩碼 *//* Additional file access control and file status flags for internal use */
#define FA_SEEKEND 0x20 /* 打開文件的時候將偏移量指向文件結果 */
#define FA_MODIFIED 0x40 /* 文件已經被修改 */
#define FA_DIRTY 0x80 /* 數據緩沖窗口數據需要回寫 *//* Name status flags in fn[] */
#define NSFLAG 11 /* 路徑名狀態字節偏移量 */
#define NS_LOSS 0x01 /* Out of 8.3 format */
#define NS_LFN 0x02 /* Force to create LFN entry */
#define NS_LAST 0x04 /* 最后一段路徑 */
#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 /* 無文件名 *//* 限制和邊界 */
#define MAX_DIR 0x200000 /* FAT最大目錄項數 */
#define MAX_DIR_EX 0x10000000 /* exFAT最大目錄項數 */
#define MAX_FAT12 0xFF5 /* FAT12最大簇數 */
#define MAX_FAT16 0xFFF5 /* FAT16最大簇數 */
#define MAX_FAT32 0x0FFFFFF5 /* FAT32最大簇數 */
#define MAX_EXFAT 0x7FFFFFFD /* exFAT最大簇數 *//* boot扇區各字段偏移量 */
#define BS_JmpBoot 0 /* x86跳轉指令 */
#define BS_OEMName 3 /* 創建該卷的系統的名稱 */
#define BPB_BytsPerSec 11 /* FAT12/16:扇區大小 FAT32:0 */
#define BPB_SecPerClus 13 /* 簇的扇區數 */
#define BPB_RsvdSecCnt 14 /* 保留區的扇區數 */
#define BPB_NumFATs 16 /* FAT表個數 */
#define BPB_RootEntCnt 17 /* FAT12/16:根目錄中目錄項個數 FAT32:0 */
#define BPB_TotSec16 19 /* FAT12/16卷的總扇區數 FAT32:0 */
#define BPB_Media 21 /* 媒體確定,不再用于任何目的 */
#define BPB_FATSz16 22 /* FAT占用的扇區數 */
#define BPB_SecPerTrk 24 /* 每個軌道的扇區數 */
#define BPB_NumHeads 26 /* 磁頭數 */
#define BPB_HiddSec 28 /* FAT卷之前隱藏的物理扇區數 */
#define BPB_TotSec32 32 /* FAT32卷的總扇區數 */
#define BS_DrvNum 36 /* 驅動器號 */
#define BS_NTres 37 /* 保留 */
#define BS_BootSig 38 /* 擴展引導簽名 */
#define BS_VolID 39 /* 卷序列號 */
#define BS_VolLab 43 /* 卷標 */
#define BS_FilSysType 54 /* 取值"FAT12", "FAT16" or "FAT"之一 */
#define BS_BootCode 62 /* Bootstrap程序 */
#define BS_55AA 510 /* 引導簽名 */#define BPB_FATSz32 36 /* FAT32:FAT占用的扇區數 */
#define BPB_ExtFlags32 40 /* FAT32:Extended flags (WORD) */
#define BPB_FSVer32 42 /* FAT32:版本 */
#define BPB_RootClus32 44 /* FAT32:根目錄起始簇號 */
#define BPB_FSInfo32 48 /* FAT32:FSInfo的扇區相對于FAT32卷的頂部的偏移 */
#define BPB_BkBootSec32 50 /* FAT32:備份引導扇區相對于FAT32卷頂部的偏移扇區 */
#define BS_DrvNum32 64 /* FAT32:驅動器號 */
#define BS_NTres32 65 /* FAT32:保留 */
#define BS_BootSig32 66 /* FAT32:擴展引導簽名 */
#define BS_VolID32 67 /* FAT32:卷序列號 */
#define BS_VolLab32 71 /* FAT32:卷標 */
#define BS_FilSysType32 82 /* FAT32:取值"FAT32" */
#define BS_BootCode32 90 /* FAT32:引導簽名 *//* 目錄項各字段偏移量 */
#define DIR_Name 0 /* 短文件名 */
#define DIR_Attr 11 /* 屬性 */
#define DIR_NTres 12 /* 0x08:文件名為小寫 0x10:擴展名為小寫 */
#define DIR_CrtTime10 13 /* 創建時間分辨率 */
#define DIR_CrtTime 14 /* 創建時間 */
#define DIR_LstAccDate 18 /* 上次訪問日期 */
#define DIR_FstClusHI 20 /* 文件起始簇號高16位 */
#define DIR_ModTime 22 /* 修改時間 */
#define DIR_FstClusLO 26 /* 文件起始簇號低16位 */
#define DIR_FileSize 28 /* 文件大小(字節) *//* 目錄項相關定義 */
#define SZDIRE 32 /* 目錄項大小 */
#define DDEM 0xE5 /* 目錄項未被使用(刪除),(0:最后一個目錄項 0xE5:不一定最后一個目錄項) */
#define RDDEM 0x05 /* 文件名第一個字符為0xE5,則替換成0x05 *//* FSInfo各字段偏移量 */
#define FSI_LeadSig 0 /* FSInfo的主要簽名 */
#define FSI_StrucSig 484 /* FSInfo的主要簽名 */
#define FSI_Free_Count 488 /* 空閑簇數 */
#define FSI_Nxt_Free 492 /* 最后一個被分配的簇號,程序應該從此開始尋找空閑簇 *//* MBR分區表各字段偏移量 */
#define MBR_Table 446 /* 分區表在MBR中的偏移量 */
#define SZ_PTE 16 /* 每個分區表大小 */
#define PTE_Boot 0 /* 引導標志 0x80:活動分區 0x00:非活動分區 */
#define PTE_StHead 1 /* 起始磁頭號 */
#define PTE_StSec 2 /* 起始扇區號 */
#define PTE_StCyl 3 /* 起始柱面號 */
#define PTE_System 4 /* 分區類型 0x00:未用 0x06:FAT16 0x0B:FAT32 0x05:擴展分區 0x07:NTFS分區 0x0F:擴展分區(LBA模式) 0x83:Linux分區 */
#define PTE_EdHead 5 /* 結束磁頭號 */
#define PTE_EdSec 6 /* 結束扇區號 */
#define PTE_EdCyl 7 /* 結束柱面號 */
#define PTE_StLba 8 /* 邏輯起始扇區號 */
#define PTE_SizLba 12 /* 總扇區數 *//* 錯誤中止 */
#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); }/* 重新進入相關 */
#define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; }
#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; }/* 分區轉換 */
#define LD2PD(vol) (BYTE)(vol)
#define LD2PT(vol) 0/* 扇區大小 */
#define SS(fs) ((UINT)_MAX_SS)/* 時間戳 */
#define GET_FATTIME() get_fattime()/* 文件鎖 */
typedef struct
{FATFS *fs; /* 卷ID(NULL:blank entry) */DWORD clu; /* 簇ID,包含目錄(0:root) */DWORD ofs; /* 偏移量ID,目錄中偏移量 */WORD ctr; /* 文件打開次數, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */
}FILESEM;/* 文件系統指針數組 */
static FATFS *FatFs[_VOLUMES];
/* 文件系統掛載ID */
static WORD Fsid;/* 文件鎖 */
static FILESEM Files[_FS_LOCK];///
/* 加載2字節小端數據 */
static WORD ld_word(const BYTE *ptr)
{WORD rv;rv = ptr[1];rv = rv << 8 | ptr[0];return rv;
}/* 加載4字節小端數據 */
static DWORD ld_dword(const BYTE *ptr)
{DWORD rv;rv = ptr[3];rv = rv << 8 | ptr[2];rv = rv << 8 | ptr[1];rv = rv << 8 | ptr[0];return rv;
}/* 以小端模式存儲2字節數據 */
static void st_word(BYTE *ptr, WORD val)
{*ptr++ = (BYTE)val; val >>= 8;*ptr++ = (BYTE)val;
}/* 以小端模式存儲4字節數據 */
static void st_dword(BYTE *ptr, DWORD val)
{*ptr++ = (BYTE)val; val >>= 8;*ptr++ = (BYTE)val; val >>= 8;*ptr++ = (BYTE)val; val >>= 8;*ptr++ = (BYTE)val;
}///
/* 內存拷貝 */
static void mem_cpy(void *dst, const void *src, UINT cnt)
{BYTE *d = (BYTE *)dst;const BYTE *s = (const BYTE *)src;if(cnt) {do {*d++ = *s++;}while(--cnt);}
}/* 內存塊填充 */
static void mem_set(void *dst, int val, UINT cnt)
{BYTE *d = (BYTE *)dst;do {*d++ = (BYTE)val;}while(--cnt);
}/* 內存塊比較 */
static int mem_cmp(const void *dst, const void *src, UINT cnt)
{const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src;int r = 0;do {r = *d++ - *s++;}while(--cnt && r == 0);return r;
}/* 檢查字符串是否包含字符 */
static int chk_chr(const char *str, int chr)
{while(*str && *str != chr) str++;return *str;
}///
/* 請求互斥鎖 */
static int lock_fs(FATFS *fs)
{return (fs && ff_req_grant(fs->sobj)) ? 1 : 0;
}/* 釋放互斥鎖 */
static void unlock_fs(FATFS *fs, FRESULT res)
{if(fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) {ff_rel_grant(fs->sobj);}
}///
/* 檢查文件是否可以采用acc方式進行訪問 */
static FRESULT chk_lock(DIR *dp, int acc)
{UINT i, be;/* 搜索文件鎖列表 */for(i = be = 0; i < _FS_LOCK; i++) {/* 該文件鎖被占用 */if(Files[i].fs) { /* 該文件已經被打開 */if(Files[i].fs == dp->obj.fs && Files[i].clu == dp->obj.sclust && Files[i].ofs == dp->dptr) break;}/* 存在空文件鎖 */else {be = 1;}}/* 文件沒有打開 */if(i == _FS_LOCK) { /* 存在空文件鎖或者刪除/重命名操作 則文件允許訪問;不存在文件鎖不允許讀寫操作 */return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES;}/* 文件被打開,不允許同時讀寫/同時寫寫 */return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK;
}/* 檢查是否存在空文件鎖 */
static int enq_lock(void)
{UINT i;for(i = 0; i < _FS_LOCK && Files[i].fs; i++) ;return (i == _FS_LOCK) ? 0 : 1;
}/* 文件訪問次數加一 acc(0:Read, 1:Write, 2:Delete/Rename) */
static UINT inc_lock(DIR *dp, int acc)
{UINT i;/* 搜索文件鎖列表 */for(i = 0; i < _FS_LOCK; i++) { /* 該文件已經被打開 */if(Files[i].fs == dp->obj.fs && Files[i].clu == dp->obj.sclust && Files[i].ofs == dp->dptr) break;}/* 該文件未被打開 */if(i == _FS_LOCK) { /* 查找空的文件鎖 */for(i = 0; i < _FS_LOCK && Files[i].fs; i++) ;/* 沒有空的文件鎖 */if(i == _FS_LOCK) return 0;/* 注冊文件持有該文件鎖 */Files[i].fs = dp->obj.fs;Files[i].clu = dp->obj.sclust;Files[i].ofs = dp->dptr;Files[i].ctr = 0;}/* 不允許同時讀以外的同時操作 */if (acc && Files[i].ctr) return 0;/* 讀訪問次數加一,寫訪問次數設為0x100 */Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1;return i + 1;
}/* 文件訪問次數減一 */
static FRESULT dec_lock(UINT i)
{WORD n;FRESULT res;if(--i < _FS_LOCK) {/* 取出訪問模式 */n = Files[i].ctr;/* 寫訪問直接訪問次數設為0 */if(n == 0x100) n = 0;/* 讀訪問次數減一 */if(n > 0) n--;Files[i].ctr = n;/* 訪問次數為0,釋放文件鎖 */if(n == 0) Files[i].fs = 0;res = FR_OK;} else {res = FR_INT_ERR;}return res;
}/* 釋放該卷占用的所有文件鎖 */
static void clear_lock(FATFS *fs)
{UINT i;for(i = 0; i < _FS_LOCK; i++) {if(Files[i].fs == fs) Files[i].fs = 0;}
}///
/* 將緩存窗口數據同步到扇區中 */
static FRESULT sync_window(FATFS *fs)
{DWORD wsect;UINT nf;FRESULT res = FR_OK;/* 請求將緩存窗口數據回寫到扇區 */if(fs->wflag) {/* 將緩存窗口數據回寫到扇區 */wsect = fs->winsect;if(disk_write(fs->drv, fs->win, wsect, 1) != RES_OK) {res = FR_DISK_ERR;} else {fs->wflag = 0;/* 該扇區在FAT表區域 */if(wsect - fs->fatbase < fs->fsize) {/* 更新備份FAT表 */for(nf = fs->n_fats; nf >= 2; nf--) {wsect += fs->fsize;disk_write(fs->drv, fs->win, wsect, 1);}}}}return res;
}/* 移動緩存窗口 */
static FRESULT move_window(FATFS *fs, DWORD sector)
{FRESULT res = FR_OK;/* 緩存窗口需要移動 */if(sector != fs->winsect) {/* 將緩存窗口數據同步到原扇區中 */res = sync_window(fs);if(res == FR_OK) { /* 將新扇區中數據讀取到緩存窗口中 */if(disk_read(fs->drv, fs->win, sector, 1) != RES_OK) {sector = 0xFFFFFFFF;res = FR_DISK_ERR;}/* 變更緩存窗口扇區號 */fs->winsect = sector;}}return res;
}/* 將數據同步到文件系統 包括緩存窗口數據和FSInfo數據 */
static FRESULT sync_fs(FATFS *fs)
{FRESULT res;/* 將緩存窗口數據同步到扇區中 */res = sync_window(fs);if(res == FR_OK) {/* FAT32文件系統,需要將FSInfo數據更新到扇區中 */if(fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) {/* 將FSInfo內容拷貝到緩存窗口中 */mem_set(fs->win, 0, SS(fs));st_word(fs->win + BS_55AA, 0xAA55); //FSInfo簽名st_dword(fs->win + FSI_LeadSig, 0x41615252); //FSInfo簽名st_dword(fs->win + FSI_StrucSig, 0x61417272); //FSInfo簽名st_dword(fs->win + FSI_Free_Count, fs->free_clst);//空閑簇數st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); //最后一個被分配的簇號/* FSInfo扇區為卷的第二扇區,緊挨著引導扇區 */fs->winsect = fs->volbase + 1;/* 將FSInfo數據更新到扇區中 */disk_write(fs->drv, fs->win, fs->winsect, 1);fs->fsi_flag = 0;}/* 立即寫入,不允許掛起 */if(disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK) res = FR_DISK_ERR;}return res;
}///
/* 將數據區簇號轉換為扇區號 */
static DWORD clust2sect(FATFS *fs, DWORD clst)
{clst -= 2;if(clst >= fs->n_fatent - 2) return 0;return clst * fs->csize + fs->database;
}///
/* 獲取指定簇對應FAT表項的內容 */
static DWORD get_fat(_FDID *obj, DWORD clst)
{UINT wc, bc;DWORD val;FATFS *fs = obj->fs;/* 錯誤簇號 */if(clst < 2 || clst >= fs->n_fatent) {val = 1;}/* 有效簇號 */else {val = 0xFFFFFFFF;/* 獲取該簇對應FAT表項的內容 */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)];if(move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;wc |= fs->win[bc % SS(fs)] << 8;val = (clst & 1) ? (wc >> 4) : (wc & 0xFFF);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));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;break;default:val = 1;}}return val;
}/* 將數據寫入指定簇對應的FAT表項中 */
static FRESULT put_fat(FATFS *fs, DWORD clst, DWORD val)
{UINT bc;BYTE *p;FRESULT res = FR_INT_ERR;/* 有效簇號 */if(clst >= 2 && clst < fs->n_fatent) {/* 將數據寫入該簇對應FAT表項 */switch(fs->fs_type) {case FS_FAT12 :bc = (UINT)clst; bc += bc / 2;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;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));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);fs->wflag = 1;break;case FS_FAT32:res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));if (res != FR_OK) break;if (!_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;
}/* 移除簇鏈中指定簇后面的所有簇 */
static FRESULT remove_chain(_FDID *obj, DWORD clst, DWORD pclst)
{FRESULT res = FR_OK;DWORD nxt;FATFS *fs = obj->fs;if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR;/* 將前一個簇對應的FAT表項設置為'EOC',即文件最后一個簇 */if(pclst) {res = put_fat(fs, pclst, 0xFFFFFFFF);if(res != FR_OK) return res;}/* 移除(設為空閑)簇鏈中指定簇后面的所有簇 */do {/* 獲取當前簇對應的FAT表項內容 */nxt = get_fat(obj, clst);/* 空簇,說明上一個簇就是最后一個簇 */if(nxt == 0) break;/* 非法參數錯誤 */if(nxt == 1) return FR_INT_ERR;/* 讀取失敗 */if (nxt == 0xFFFFFFFF) return FR_DISK_ERR;/* 當前簇對應的FAT表項內容設置為空閑 */res = put_fat(fs, clst, 0);if(res != FR_OK) return res;/* 更新FSINFO內容 */if(fs->free_clst < fs->n_fatent - 2) {fs->free_clst++; //空閑簇數加一fs->fsi_flag |= 1; //FSINFO數據變化需要更新}clst = nxt;}while(clst < fs->n_fatent);return FR_OK;
}/* 在指定簇后面再擴展一個簇 clst(0:創建新簇鏈) */
static DWORD create_chain(_FDID *obj, DWORD clst)
{DWORD cs, ncl, scl;FRESULT res;FATFS *fs = obj->fs;/* 創建一個簇鏈 */if(clst == 0) {/* 從建議的簇(最后一個被分配的簇)開始尋找空閑簇 */scl = fs->last_clst;if(scl == 0 || scl >= fs->n_fatent) scl = 1;}/* 擴展指定簇鏈 */else {/* 獲取指定簇對應FAT表項的內容 */cs = get_fat(obj, clst);/* 該簇應該已經分配,空閑簇則錯誤非法參數也要報錯 */if(cs < 2) return 1;/* 讀取失敗 */if(cs == 0xFFFFFFFF) return cs;/* 下一個簇號必須在FAT表中 */if(cs < fs->n_fatent) return cs;/* 從指定簇開始尋找空閑簇 */scl = clst;}/* 尋找空閑簇 */ncl = scl;for(;;) {ncl++;/* 找到了FAT尾部 */if(ncl >= fs->n_fatent) {/* 從頭(2號簇)開始繼續找 */ncl = 2;/* 整個FAT表找個遍,沒有空閑簇 */if(ncl > scl) return 0;}/* 獲取ncl簇對應FAT表項的內容 */cs = get_fat(obj, ncl);/* 找到空閑簇 */if (cs == 0) break;/* 發生錯誤 */if(cs == 1 || cs == 0xFFFFFFFF) return cs;/* 找了一遍沒有空閑簇 */if(ncl == scl) return 0;}/* 找到空閑簇,將該空閑簇設置為EOC */res = put_fat(fs, ncl, 0xFFFFFFFF);if(res == FR_OK && clst != 0){/* 將指定簇指向找到的簇(即將新簇鏈接到簇鏈) */res = put_fat(fs, clst, ncl);}/* 更新FSINFO */if(res == FR_OK) {fs->last_clst = ncl; //最后一個被分配的簇號if(fs->free_clst <= fs->n_fatent - 2) fs->free_clst--; //空閑簇減一fs->fsi_flag |= 1; //FSINFO數據變化,需要更新}else {ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1;}return ncl;
}///
/* 設置目錄讀寫偏移量 */
static FRESULT dir_sdi(DIR *dp, DWORD ofs)
{DWORD csz, clst;FATFS *fs = dp->obj.fs;if(ofs >= (DWORD)MAX_DIR || ofs % SZDIRE) {return FR_INT_ERR;}/* 當前偏移量 */dp->dptr = ofs;/* 該目錄所在簇(0:root) */clst = dp->obj.sclust;/* FAT32根目錄 */if(clst == 0 && fs->fs_type >= FS_FAT32) {/* 根目錄起始簇 */clst = fs->dirbase;}/* FAT12/16根目錄 */if(clst == 0) {/* 偏移量不能超過根目錄的目錄項數 */if(ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR;/* 根目錄起始扇區號 */dp->sect = fs->dirbase;} /* 子目錄或FAT32根目錄 */else{ /* 每簇字節數 */csz = (DWORD)fs->csize * SS(fs);/* 查找偏移量所在簇 */while(ofs >= csz) { clst = get_fat(&dp->obj, clst);if(clst == 0xFFFFFFFF) return FR_DISK_ERR;if(clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR;ofs -= csz;}/* 偏移量所在簇的起始扇區 */dp->sect = clust2sect(fs, clst);}/* 偏移量所在當前簇 */dp->clust = clst;if (!dp->sect) return FR_INT_ERR;/* 偏移量所在當前扇區 */dp->sect += ofs / SS(fs);/* 偏移量所對應的緩存窗口指針 */dp->dir = fs->win + (ofs % SS(fs));return FR_OK;
}/* 當前偏移量轉移到下一個目錄項 stretch:0不擴展 1:擴展 */
static FRESULT dir_next(DIR *dp, int stretch)
{DWORD ofs, clst;FATFS *fs = dp->obj.fs;UINT n;/* 將偏移量指向下一個目錄項 */ofs = dp->dptr + SZDIRE;if(!dp->sect || ofs >= (DWORD)MAX_DIR) return FR_NO_FILE;/* 偏移量所在扇區號發生變化 */if(ofs % SS(fs) == 0) {dp->sect++;/* 根目錄 */if(!dp->clust) {/* 超出根目錄范圍 */if(ofs / SZDIRE >= fs->n_rootdir) {dp->sect = 0; return FR_NO_FILE;}}/* 子目錄 */else {/* 偏移量所在簇號發生變化 */if((ofs / SS(fs) & (fs->csize - 1)) == 0) {/* 獲取下一個簇號 */clst = get_fat(&dp->obj, dp->clust);if(clst <= 1) return FR_INT_ERR;if(clst == 0xFFFFFFFF) return FR_DISK_ERR;/* 文件最后最后一個簇 */if(clst >= fs->n_fatent){/* 不允許擴展 */if(!stretch) {dp->sect = 0; return FR_NO_FILE;}/* 擴展一個簇 */clst = create_chain(&dp->obj, dp->clust);if (clst == 0) return FR_DENIED;if (clst == 1) return FR_INT_ERR;if (clst == 0xFFFFFFFF) return FR_DISK_ERR;if (_FS_EXFAT) dp->obj.stat |= 4; /* 將緩存窗口數據同步到扇區中 */if(sync_window(fs) != FR_OK) return FR_DISK_ERR;/* 將新簇清空為0 */mem_set(fs->win, 0, SS(fs));for (n = 0, fs->winsect = clust2sect(fs, clst); n < fs->csize; n++, fs->winsect++) { /* Fill the new cluster with 0 */fs->wflag = 1;if(sync_window(fs) != FR_OK) return FR_DISK_ERR;}/* 重新設置緩存窗口扇區號為當前簇起始扇區(即偏移量所在扇區) */fs->winsect -= n;}/* 當前偏移量所在簇號 */dp->clust = clst;/* 當前偏移量所在扇區號 */dp->sect = clust2sect(fs, clst);}}}/* 當前偏移量 */dp->dptr = ofs;/* 當前偏移量對應緩存窗口中的指針 */dp->dir = fs->win + ofs % SS(fs);return FR_OK;
}/* 在指定目錄中申請連續nent個目錄項的空間 */
static FRESULT dir_alloc(DIR *dp, UINT nent)
{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(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) {/* 連續空目錄項的個數加一 */if(++n == nent) break;} /* 該處有目錄項 */else {/* 連續空目錄項的個數清零 */n = 0;}/* 當前偏移量轉移到下一個目錄項,簇不夠放了就擴展 */res = dir_next(dp, 1);}while(res == FR_OK);}if (res == FR_NO_FILE) res = FR_DENIED;return res;
}/* 從目錄項中讀出文件/目錄起始簇號 */
static DWORD ld_clust(FATFS *fs, const BYTE *dir)
{DWORD cl;cl = ld_word(dir + DIR_FstClusLO);if(fs->fs_type == FS_FAT32) {cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16;}return cl;
}/* 將文件/目錄起始簇號寫入目錄項 */
static void st_clust(FATFS *fs, BYTE *dir, DWORD cl)
{st_word(dir + DIR_FstClusLO, (WORD)cl);if(fs->fs_type == FS_FAT32) {st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16));}
}/* 指定目錄中查找一個指定屬性(vol)的目錄項 vol(0:文件/目錄 1:卷標) */
static FRESULT dir_read(DIR *dp, int vol)
{FRESULT res = FR_NO_FILE;FATFS *fs = dp->obj.fs;BYTE a, c;/* 遍歷整個目錄,查找目錄項 0:文件/目錄項 1:卷標 */while(dp->sect) {res = move_window(fs, dp->sect);if(res != FR_OK) break;/* 檢查該目錄項是否被使用 */c = dp->dir[DIR_Name];if(c == 0) //未被使用{res = FR_NO_FILE; break;}/* 獲取目錄項屬性 */dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK;/* 檢查是不是合理的目錄項 1表示查找卷標 0表示查找文件或目錄 */if(c != DDEM && c != '.' && a != AM_LFN && (int)((a & ~AM_ARC) == AM_VOL) == vol) {break;}/* 繼續查找下一個目錄項 */res = dir_next(dp, 0);if(res != FR_OK) break;}if(res != FR_OK) dp->sect = 0;return res;
}/* 指定目錄中通過文件名查找一個目錄項 */
static FRESULT dir_find(DIR *dp)
{FRESULT res;FATFS *fs = dp->obj.fs;BYTE c;/* 設置偏移量到目錄起始位置 */res = dir_sdi(dp, 0);if(res != FR_OK) return res;/* 循環查找目錄項 */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; }/* 非卷標,匹配文件名 */dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK;if(!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, dp->fn, 11)) break;res = dir_next(dp, 0);}while(res == FR_OK);return res;
}/* 在指定目錄中創建一個目錄項(指定文件名和擴展名) */
static FRESULT dir_register(DIR *dp)
{FRESULT res;FATFS *fs = dp->obj.fs;/* 申請連續1個目錄項的空間 */res = dir_alloc(dp, 1);if (res == FR_OK) {/* 將緩存窗口移到新的目錄項空間 */res = move_window(fs, dp->sect);if(res == FR_OK) {/* 將文件名寫入目錄項,其它都清零 */mem_set(dp->dir, 0, SZDIRE);mem_cpy(dp->dir + DIR_Name, dp->fn, 11);fs->wflag = 1;}}return res;
}/* 在指定目錄中移除當前偏移量指向的目錄項 */
static FRESULT dir_remove(DIR *dp)
{FRESULT res;FATFS *fs = dp->obj.fs;/* 將當前緩存窗口移到當前偏移量所在扇區 */res = move_window(fs, dp->sect);if(res == FR_OK) {/* 將目錄項設置為未被使用(刪除) */dp->dir[DIR_Name] = DDEM;fs->wflag = 1;}return res;
}/* 從指定目錄項中讀出文件信息 */
static void get_fileinfo(DIR *dp, FILINFO *fno)
{UINT i, j;TCHAR c;DWORD tm;/* 清空文件信息 */fno->fname[0] = 0;/* 偏移量不允許指向目錄結尾 */if(!dp->sect) return;/* 獲取短文件名 */i = j = 0;while(i < 11) {/* 從文件名中獲取字符 */c = (TCHAR)dp->dir[i++];/* 跳過空格 */if(c == ' ') continue;/* 將0x05替換成0xE5 */if(c == RDDEM) c = (TCHAR)DDEM;/* 第9個字符一定要插入'.' */if(i == 9) fno->fname[j++] = '.';/* 填入字符 */fno->fname[j++] = c;}/* 結尾用'/0'表示 */fno->fname[j] = 0;/* 文件屬性屬性 */fno->fattrib = dp->dir[DIR_Attr];/* 文件大小 */fno->fsize = ld_dword(dp->dir + DIR_FileSize);/* 修改日期和時間 */tm = ld_dword(dp->dir + DIR_ModTime);fno->ftime = (WORD)tm; fno->fdate = (WORD)(tm >> 16);
}/* 取出最頂級路徑,返回剩余路徑段 */
static FRESULT create_name(DIR *dp, const TCHAR **path)
{BYTE c, d, *sfn;UINT ni, si, i;const char *p;/* 獲取路徑指針 */p = *path; /* 清空文件名緩沖區 */sfn = dp->fn;mem_set(sfn, ' ', 11);si = i = 0; ni = 8;/* "FILENAME.TXT" "FILENAMETXT""FILE.TXT" "FILE TXT""file.txt" "FILE TXT""蜃気樓.TXT" "?気樓 TXT" "蜃"的第一個字節為0xE5,用0x05替換"NOEXT" "NOEXT "*/for(;;) {c = (BYTE)p[si++];/* \0到\x1F被識別為路徑結尾,在短文件名中空格被識別為路徑結尾 */if(c <= ' ') //路徑結尾break;/* 發現分隔符 */if(c == '/' || c == '\\') { /* 自動跳過并忽略重復的分隔符 */while (p[si] == '/' || p[si] == '\\') si++;break;}/* 規范為8.3存儲格式 */if(c == '.' || i >= ni) {/* 1.2.txt非法 123456789.txt非法 1234567891111非法 */if(ni == 11 || c != '.') return FR_INVALID_NAME;/* 跳到擴展名 */i = 8; ni = 11;continue;}/* 擴展字符第一字節 */if(IsDBCS1(c)) { /* 擴展字符第二字節 */d = (BYTE)p[si++];if(!IsDBCS2(d) || i >= ni - 1) return FR_INVALID_NAME;/* 擴展字符占兩字節 */sfn[i++] = c;sfn[i++] = d;} /* 非擴展字符 */else {/* 非法字符 */if(chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return FR_INVALID_NAME;/* 全部轉成大小 */if(IsLower(c)) c -= 0x20;sfn[i++] = c;}}/* 返回下一段路徑的指針 */*path = p + si;if(i == 0) return FR_INVALID_NAME;/* 第一個字節是DDEM轉換為RDDEM */if(sfn[0] == DDEM) sfn[0] = RDDEM;/* 如果是最后一段路徑,則添加路徑結束標志 */sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0;return FR_OK;
}/* 進入文件所在路徑(將偏移量定位到指定路徑) */
static FRESULT follow_path(DIR *dp, const TCHAR *path)
{FRESULT res;BYTE ns;_FDID *obj = &dp->obj;FATFS *fs = obj->fs;/* 跳過分隔符 */while(*path == '/' || *path == '\\') path++;/* 從根目錄開始找 */obj->sclust = 0;/* ' '表示已經到了路徑結尾(沒有路徑),將偏移量設置為當前目錄起始端 */if((UINT)*path < ' ') { dp->fn[NSFLAG] = NS_NONAME;res = dir_sdi(dp, 0);} else {/* 進入路徑 */for(;;) {/* 取出最頂級路徑,返回剩余路徑段 */res = create_name(dp, &path);if(res != FR_OK) break;/* 在目錄中查找取出的最頂級路徑的目錄項 */res = dir_find(dp);ns = dp->fn[NSFLAG];/* 沒找到該目錄項 */if(res != FR_OK) {if(res == FR_NO_FILE) {/* 將錯誤返回精確到沒有最后一段 */if(!(ns & NS_LAST)) res = FR_NO_PATH;}break;}/* 最后一段路徑 */if(ns & NS_LAST) break;/* 如果找到該目錄項,并且不是最后一段路徑,則該目錄項必須為子目錄 */if(!(obj->attr & AM_DIR)) {res = FR_NO_PATH; break;}/* 從目錄項中讀出文件/目錄起始簇號 */obj->sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs));}}return res;
}/* 從路徑中取出邏輯驅動器號,返回剩余路徑段 */
static int get_ldnumber(const TCHAR **path)
{const TCHAR *tp, *tt;UINT i;int vol = -1;/* 路徑不為空 */if(*path) {/* 從路徑中找到':'所在位置 */for(tt = *path; (UINT)*tt >= (_USE_LFN ? ' ' : '!') && *tt != ':'; tt++) ;/* 存在':' */if(*tt == ':') {/* 取出邏輯驅動器號,并返回剩余的路徑 */tp = *path;i = *tp++ - '0';if(i < 10 && tp == tt) {if(i < _VOLUMES) {vol = (int)i;*path = ++tt;}}return vol;}/* 未指明驅動器號,則默認為0號 */vol = 0;}return vol;
}///
/* 讀指定扇區內容并檢查是不是boot扇區 0:FAT引導扇區, 1:exFAT引導扇區, 2:有效的引導扇區(當前可能是MBR), 3:無效引導扇區, 4:Disk error */
static BYTE check_fs(FATFS *fs, DWORD sect)
{fs->wflag = 0; fs->winsect = 0xFFFFFFFF;/* 將窗口移到指定扇區 */if(move_window(fs, sect) != FR_OK) return 4;/* 一個引導簽名,表明這是一個有效的引導扇區 */if(ld_word(fs->win + BS_55AA) != 0xAA55) return 3;/* x86跳轉指令。如果兩種格式之外的任何格式,Windows都將無法識別該卷 */if(fs->win[BS_JmpBoot] == 0xE9 || (fs->win[BS_JmpBoot] == 0xEB && fs->win[BS_JmpBoot + 2] == 0x90)) {/* 文件系統類型FAT12/FAT16 */if((ld_dword(fs->win + BS_FilSysType) & 0xFFFFFF) == 0x544146) return 0;/* 文件系統類型FAT32 */if(ld_dword(fs->win + BS_FilSysType32) == 0x33544146) return 0;}return 2;
}/* 通過路徑中的邏輯驅動器號,在邏輯驅動器上查找該卷是否被掛載(同時檢查磁盤是否寫保護(mode!=0寫保護檢查)),如果沒有則嘗試掛載 返回邏輯驅動器號后的路徑,返回文件系統指針 */
static FRESULT find_volume(const TCHAR **path, FATFS **rfs, BYTE mode)
{BYTE fmt, *pt;int vol;DSTATUS stat;DWORD bsect, fasize, tsect, sysect, nclst, szbfat, br[4];WORD nrsv;FATFS *fs;UINT i;/* 從路徑中取出邏輯驅動器號,返回剩余路徑段 */*rfs = 0;vol = get_ldnumber(path);if(vol < 0) return FR_INVALID_DRIVE;/* 根據邏輯驅動器號獲取文件系統指針 */fs = FatFs[vol];if(!fs) return FR_NOT_ENABLED;ENTER_FF(fs);/* 返回文件系統指針 */*rfs = fs;/* 檢查是否有寫訪問權限 */mode &= (BYTE)~FA_READ;/* 文件系統已經掛載 */if(fs->fs_type) {/* 物理設備已經初始化 */stat = disk_status(fs->drv);if(!(stat & STA_NOINIT)) { /* 請求檢測寫保護權限,但物理設備寫保護 */if(!_FS_READONLY && mode && (stat & STA_PROTECT)) { return FR_WRITE_PROTECTED;}return FR_OK;}}/* 文件系統未掛載或物理設備未初始化 *//* 清空文件系統掛載標志 */fs->fs_type = 0;/* 綁定邏輯驅動器號 */fs->drv = LD2PD(vol);/* 初始化物理設備 */stat = disk_initialize(fs->drv);if(stat & STA_NOINIT) {return FR_NOT_READY;}/* 請求檢測寫保護權限,但物理設備寫保護 */if(!_FS_READONLY && mode && (stat & STA_PROTECT)) {return FR_WRITE_PROTECTED;}/* 讀取0號扇區,檢查是不是FAT的boot扇區 */bsect = 0;fmt = check_fs(fs, bsect);/* 不是FAT的boot扇區,但可能是MBR(分區表放在MBR中) */if(fmt == 2 || (fmt < 2 && LD2PT(vol) != 0)) {/* 遍歷分區表 */for(i = 0; i < 4; i++) {/* 分區表指針 */pt = fs->win + (MBR_Table + i * SZ_PTE);/* 從分區表中獲取分區的起始扇區 */br[i] = pt[PTE_System] ? ld_dword(pt + PTE_StLba) : 0;}/* 分區號0:auto, 1-4:forced */i = LD2PT(vol);if(i) i--;/* 查找分區的起始扇區,檢查FAT的boot扇區 */do{bsect = br[i];fmt = bsect ? check_fs(fs, bsect) : 3;}while(LD2PT(vol) == 0 && fmt >= 2 && ++i < 4);}if(fmt == 4) return FR_DISK_ERR;if(fmt >= 2) return FR_NO_FILESYSTEM;/* 存在FAT文件系統 *//* 檢查扇區字節數和物理設備相不相同 */if(ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM;/* 每個FAT占用的扇區數 */fasize = ld_word(fs->win + BPB_FATSz16);if(fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32);fs->fsize = fasize;/* FAT表個數 */fs->n_fats = fs->win[BPB_NumFATs];if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM;/* FAT總扇區數 */fasize *= fs->n_fats;/* 每個簇的扇區數 */fs->csize = fs->win[BPB_SecPerClus];if(fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM;/* FAT12/16根目錄中目錄項個數 */fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt);if(fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM;/* 卷的總扇區數 */tsect = ld_word(fs->win + BPB_TotSec16);if(tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32);/* 保留區扇區數 */nrsv = ld_word(fs->win + BPB_RsvdSecCnt);if(nrsv == 0) return FR_NO_FILESYSTEM;/* RSV + FAT + DIR總扇區數 */sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* RSV + FAT + DIR */if(tsect < sysect) return FR_NO_FILESYSTEM;/* 簇個數 */nclst = (tsect - sysect) / fs->csize;if(nclst == 0) return FR_NO_FILESYSTEM;/* 根據簇數確定FAT子系統類型 */fmt = FS_FAT32;if(nclst <= MAX_FAT16) fmt = FS_FAT16;if(nclst <= MAX_FAT12) fmt = FS_FAT12;/* FAT表項個數 簇數 + 2 */fs->n_fatent = nclst + 2;/* 卷起始扇區 */fs->volbase = bsect;/* FAT表起始扇區 */fs->fatbase = bsect + nrsv;/* 數據區起始扇區 */fs->database = bsect + sysect;/* FAT32 */if(fmt == FS_FAT32) {/* FAT32版本號必定為0.0 */if(ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM;/* FAT32根目錄中目錄項個數字段為0 */if(fs->n_rootdir) return FR_NO_FILESYSTEM;/* 根目錄起始簇號 */fs->dirbase = ld_dword(fs->win + BPB_RootClus32);/* FAT表字節數 */szbfat = fs->n_fatent * 4;}/* FAT12/16 */else {/* FAT12/16根目錄中目錄項個數字段不為0 */if(fs->n_rootdir == 0) return FR_NO_FILESYSTEM;/* 根目錄起始扇區號 */fs->dirbase = fs->fatbase + fasize;/* FAT表字節數 */szbfat = (fmt == FS_FAT16) ? 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;/* 最后一個被分配的簇和空閑簇數,初始化為未知,0xFFFFFFFF表示未知 */fs->last_clst = fs->free_clst = 0xFFFFFFFF;fs->fsi_flag = 0x80;/* 如果是FAT32則讀出FSINFO,為最后一個被分配的簇和空閑簇數賦值 */if(fmt == FS_FAT32 && ld_word(fs->win + BPB_FSInfo32) == 1&& move_window(fs, bsect + 1) == FR_OK){fs->fsi_flag = 0;/* 有效的FSINFO */if(ld_word(fs->win + BS_55AA) == 0xAA55&& ld_dword(fs->win + FSI_LeadSig) == 0x41615252&& ld_dword(fs->win + FSI_StrucSig) == 0x61417272){/* 空閑簇數 */fs->free_clst = ld_dword(fs->win + FSI_Free_Count);/* 驅動程序分配的最后一個簇編號 */fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free);}}/* FAT子系統類型 */fs->fs_type = fmt;/* 文件系統掛載ID */fs->id = ++Fsid;clear_lock(fs);return FR_OK;
}///
/* 檢查該文件/目錄是否有效,并請求互斥鎖,返回文件系統指針 */
static FRESULT validate(_FDID *obj, FATFS **fs)
{FRESULT res = FR_INVALID_OBJECT;if(obj && obj->fs && obj->fs->fs_type && obj->id == obj->fs->id) {if(lock_fs(obj->fs)) {if(!(disk_status(obj->fs->drv) & STA_NOINIT)) {res = FR_OK;} else {unlock_fs(obj->fs, FR_OK);}} else {res = FR_TIMEOUT;}}*fs = (res == FR_OK) ? obj->fs : 0;return res;
}///
/* 掛載/卸載一個邏輯驅動器((!fs || opt != 1)卸載原邏輯驅動器;否則掛載新驅動器) */
FRESULT f_mount(FATFS *fs, const TCHAR *path, BYTE opt)
{FATFS *cfs;int vol;FRESULT res;const TCHAR *rp = path;/* 從路徑中取出邏輯驅動器號,返回剩余路徑段 */vol = get_ldnumber(&rp);if(vol < 0) return FR_INVALID_DRIVE;/* 文件系統指針 */cfs = FatFs[vol];/* 文件系統指針已經存在,將原文件系統取消掛載 */if(cfs){/* 釋放該卷占用的所有文件鎖 */clear_lock(cfs);/* 刪除互斥鎖 */if(!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR;/* 將原文件系統取消掛載 */cfs->fs_type = 0;}/* 新文件系統 */if(fs) {/* 清空掛載標志位 */fs->fs_type = 0;/* 創建互斥鎖 */if(!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR;}/* 注冊新的文件系統指針 */FatFs[vol] = fs;if(!fs || opt != 1) return FR_OK;/* 通過路徑中的邏輯驅動器號,并嘗試掛載 */res = find_volume(&path, &fs, 0);LEAVE_FF(fs, res);
}/* 打開文件(創建;檢查訪問權限;更新讀寫偏移量) */
FRESULT f_open(FIL *fp, const TCHAR *path, BYTE mode)
{FRESULT res;DIR dj;FATFS *fs;DWORD dw, cl, bcs, clst, sc;FSIZE_t ofs;if(!fp) return FR_INVALID_OBJECT;/* 訪問模式 */mode &= _FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND | FA_SEEKEND;/* 通過路徑中的邏輯驅動器號,在邏輯驅動器上查找該卷是否被掛載(同時檢查磁盤是否寫保護(mode!=0寫保護檢查)),如果沒有則嘗試掛載 返回邏輯驅動器號后的路徑,返回文件系統指針 */res = find_volume(&path, &fs, mode);if(res == FR_OK) {/* 目錄項所處文件系統 */dj.obj.fs = fs;/* 進入文件所在路徑(將偏移量定位到指定路徑) */res = follow_path(&dj, path);if(res == FR_OK) {/* 非法路徑名 */if(dj.fn[NSFLAG] & NS_NONAME) {res = FR_INVALID_NAME;}else {/* 檢查文件是否可以采用acc方式進行訪問 */res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0);}}/* 打開或創建并打開一個文件 */if(mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {/* 沒有這個文件,創建一個新的 */if(res != FR_OK) {/* 在指定目錄中創建一個目錄項(指定文件名和擴展名) */if(res == FR_NO_FILE) {res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES;}/* 文件已經創建 */mode |= FA_CREATE_ALWAYS;}/* 文件已經存在 */else {/* 文件屬性只讀或目錄,不允許創建 */if(dj.obj.attr & (AM_RDO | AM_DIR)) {res = FR_DENIED;}else{/* 已存在不允許創建 */if(mode & FA_CREATE_NEW) res = FR_EXIST;}}/* 覆蓋模式 */if(res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* 獲取時間戳 */dw = GET_FATTIME();{/* 覆蓋修改日期和時間 */st_dword(dj.dir + DIR_CrtTime, dw);st_dword(dj.dir + DIR_ModTime, dw);/* 重置屬性 */dj.dir[DIR_Attr] = AM_ARC;/* 獲取文件第一個簇號 */cl = ld_clust(fs, dj.dir);/* 重置文件第一個簇號 */st_clust(fs, dj.dir, 0);/* 重置文件大小 */st_dword(dj.dir + DIR_FileSize, 0);fs->wflag = 1;/* 移除FAT中原有簇鏈 */if(cl) {dw = fs->winsect;res = remove_chain(&dj.obj, cl, 0);if(res == FR_OK) {res = move_window(fs, dw);fs->last_clst = cl - 1; }}}}}/* 只允許打開已存在的文件 */else {if(res == FR_OK) {/* 目錄不能打開 */if(dj.obj.attr & AM_DIR) {res = FR_NO_FILE;} else {/* 只讀項不能寫 */if((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) {res = FR_DENIED;}}}}if(res == FR_OK) {/* 覆蓋模式 */if(mode & FA_CREATE_ALWAYS)mode |= FA_MODIFIED; //文件已經被修改/* 指針直到該目錄項扇區 */fp->dir_sect = fs->winsect;/* 當前目錄項在緩存窗口中的指針 */fp->dir_ptr = dj.dir;/* 文件訪問次數加一 acc(0:Read, 1:Write, 2:Delete/Rename) */fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0);if (!fp->obj.lockid) res = FR_INT_ERR;}if(res == FR_OK) {{/* 從目錄項中讀出文件起始簇號 */fp->obj.sclust = ld_clust(fs, dj.dir);/* 從目錄項中讀出文件大小 */fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize);}fp->obj.fs = fs; /* 文件系統指針 */fp->obj.id = fs->id; /* 文件系統掛載ID */fp->flag = mode; /* 文件當前狀態 */fp->err = 0; /* 清空錯誤 */fp->sect = 0; /* 清空當前數據扇區 */fp->fptr = 0; /* 文件偏移量指向文件首部 */mem_set(fp->buf, 0, _MAX_SS); /* 清空讀寫數據緩存窗口 *//* 指向文件結尾 */if((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* 當前讀寫偏移量指向文件結尾 */fp->fptr = fp->obj.objsize;/* 調整讀寫指針所在簇號 */bcs = (DWORD)fs->csize * SS(fs);clst = fp->obj.sclust;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)) { if((sc = clust2sect(fs, clst)) == 0) {res = FR_INT_ERR;} else {/* 調整讀寫偏移量所在扇區號 */fp->sect = sc + (DWORD)(ofs / SS(fs));/* 將讀寫偏移量所在扇區數據讀到讀寫數據緩存窗口 */if(disk_read(fs->drv, fp->buf, fp->sect, 1) != RES_OK) res = FR_DISK_ERR;}}}}}if(res != FR_OK) fp->obj.fs = 0;LEAVE_FF(fs, res);
}/* 讀文件 */
FRESULT f_read(FIL *fp, void *buff, UINT btr, UINT *br)
{FRESULT res;FATFS *fs;DWORD clst, sect;FSIZE_t remain;UINT rcnt, cc, csect;BYTE *rbuff = (BYTE *)buff;/* 成功讀取字節數清零 */*br = 0;/* 檢查該文件是否有效,返回文件系統指針 */res = validate(&fp->obj, &fs);if(res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);/* 檢查讀權限 */if(!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED);/* 文件剩余字節數 */remain = fp->obj.objsize - fp->fptr;if(btr > remain) btr = (UINT)remain;for(; btr; rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {/* 在扇區邊界 */if(fp->fptr % SS(fs) == 0) { /* 偏移量處于所在簇的第幾個扇區 */csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1));/* 在簇的邊界 */if(csect == 0) {/* 在文件頂部 */if(fp->fptr == 0) {/* 當前偏移量在文件起始簇 */clst = fp->obj.sclust;} /* 不在文件頂部 */else {/* 查找FAT,取出下一簇號 */{clst = get_fat(&fp->obj, fp->clust);}}if(clst < 2) ABORT(fs, FR_INT_ERR);if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);/* 更新當前讀寫偏移量所在簇號 */fp->clust = clst;}/* 更新當前讀寫偏移量所在扇區號 */sect = clust2sect(fs, fp->clust);if(!sect) ABORT(fs, FR_INT_ERR);sect += csect;/* 當剩余字節大于等于一個扇區 */cc = btr / SS(fs);if(cc) {/* 在該簇中剩余扇區數 */if(csect + cc > fs->csize) {cc = fs->csize - csect;}/* 將該簇中剩余數據都讀出來 */if(disk_read(fs->drv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);/* 還沒來得及回寫的數據,需要從數據緩存窗口中讀出來 */if((fp->flag & FA_DIRTY) && fp->sect - sect < cc) {mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs));}rcnt = SS(fs) * cc;continue;}/* 剩余字節數不大于一個扇區 *//* 數據緩存窗口扇區不等于偏移量所在扇區 */if(fp->sect != sect) {/* 先將緩存窗口數據回寫到扇區 */if(fp->flag & FA_DIRTY) {if(disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);fp->flag &= (BYTE)~FA_DIRTY;}/* 將偏移量所在扇區數據更新到數據緩存窗口 */if(disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);}/* 更新數據緩存窗口扇區號 */fp->sect = sect;}/* 確定該扇區中需要讀的字節數 */rcnt = SS(fs) - (UINT)fp->fptr % SS(fs);if(rcnt > btr) rcnt = btr;/* 將數據從緩存窗口中讀出來 */mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt);}LEAVE_FF(fs, FR_OK);
}/* 寫文件 */
FRESULT f_write(FIL *fp, const void *buff, UINT btw, UINT *bw)
{FRESULT res;FATFS *fs;DWORD clst, sect;UINT wcnt, cc, csect;const BYTE *wbuff = (const BYTE*)buff;*bw = 0;/* 檢查該文件/目錄是否有效,返回文件系統指針 */res = validate(&fp->obj, &fs);if(res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);/* 檢查文件是否具備寫權限 */if(!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED);/* 文件大小不能超過4GB */if((!_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) {btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr);}for(; btw; wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize, *bw += wcnt, btw -= wcnt) {/* 在扇區邊界 */if(fp->fptr % SS(fs) == 0) {/* 偏移量處于所在簇的第幾個扇區 */csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster *//* 在簇的邊界 */if(csect == 0) { /* 在文件頂部 */if(fp->fptr == 0) { /* 當前偏移量在文件起始簇 */clst = fp->obj.sclust;/* 文件還沒有寫入過任何內容 */if(clst == 0) {/* 創建新簇鏈 */clst = create_chain(&fp->obj, 0);}} /* 不在文件頂部 */else {/* 查找FAT,取出下一簇號 */{clst = create_chain(&fp->obj, fp->clust);}}if (clst == 0) break;if (clst == 1) ABORT(fs, FR_INT_ERR);if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);/* 更新當前讀寫偏移量所在簇號 */fp->clust = clst;/* 第一次寫,設置文件起始簇 */if(fp->obj.sclust == 0) fp->obj.sclust = clst;}/* 數據緩存窗口由數據需要回寫,先將數據回寫 */if(fp->flag & FA_DIRTY) {if(disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);fp->flag &= (BYTE)~FA_DIRTY;}/* 更新當前讀寫偏移量所在扇區號 */sect = clust2sect(fs, fp->clust);if(!sect) ABORT(fs, FR_INT_ERR);sect += csect;/* 當剩余字節大于等于一個扇區 */cc = btw / SS(fs);if(cc) {/* 在該簇中剩余扇區數 */if(csect + cc > fs->csize) {cc = fs->csize - csect;}/* 將屬于該簇的所有數據寫入 */if(disk_write(fs->drv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);/* 同時更新數據緩存窗口中數據 */if(fp->sect - sect < cc) {mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs));fp->flag &= (BYTE)~FA_DIRTY;}wcnt = SS(fs) * cc;continue;}/* 剩余字節數不大于一個扇區 *//* 數據緩存窗口扇區不等于偏移量所在扇區 *//* 將偏移量所在扇區數據更新到數據緩存窗口 */if(fp->sect != sect && fp->fptr < fp->obj.objsize &&disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) {ABORT(fs, FR_DISK_ERR);}/* 更新數據緩存窗口扇區號 */fp->sect = sect;}/* 確定該扇區中需要寫的字節數 */wcnt = SS(fs) - (UINT)fp->fptr % SS(fs);if(wcnt > btw) wcnt = btw;/* 將數據寫到緩沖區,下次有機會更新到存儲設備 */mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt);fp->flag |= FA_DIRTY;}/* 文件發生修改 */fp->flag |= FA_MODIFIED;LEAVE_FF(fs, FR_OK);
}/* 將數據和文件信息立即同步到存儲設備 */
FRESULT f_sync(FIL *fp)
{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) {/* 有數據需要回寫到存儲設備中 */if(fp->flag & FA_DIRTY) {if(disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) LEAVE_FF(fs, FR_DISK_ERR);fp->flag &= (BYTE)~FA_DIRTY;}/* 獲取時間戳 */tm = GET_FATTIME();{/* 將緩存窗口移動到文件對應的目錄項扇區 */res = move_window(fs, fp->dir_sect);if(res == FR_OK) {/* 將文件信息寫入目錄項 */dir = fp->dir_ptr;dir[DIR_Attr] |= AM_ARC; //歸檔st_clust(fp->obj.fs, dir, fp->obj.sclust); //文件起始簇st_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize); //文件大小st_dword(dir + DIR_ModTime, tm); //修改時間st_word(dir + DIR_LstAccDate, 0); //最后訪問日期/* 文件信息立即寫入 */fs->wflag = 1;res = sync_fs(fs);fp->flag &= (BYTE)~FA_MODIFIED;}}}}LEAVE_FF(fs, res);
}/* 關閉文件 */
FRESULT f_close(FIL *fp)
{FRESULT res;FATFS *fs;/* 將數據和文件信息立即同步到存儲設備 */res = f_sync(fp);if(res == FR_OK){/* 檢查該文件/目錄是否有效,返回文件系統指針 */res = validate(&fp->obj, &fs); /* Lock volume */if (res == FR_OK) {/* 文件訪問次數減一 */res = dec_lock(fp->obj.lockid);if (res == FR_OK){/* 文件指針所處文件系統請空 */fp->obj.fs = 0;}unlock_fs(fs, FR_OK);}}return res;
}/* 將讀寫偏移量查找到指定位置 */
FRESULT f_lseek(FIL *fp, FSIZE_t ofs)
{FRESULT res;FATFS *fs;DWORD clst, bcs, nsect;FSIZE_t ifptr;/* 檢查該文件/目錄是否有效,返回文件系統指針 */res = validate(&fp->obj, &fs);if (res == FR_OK) res = (FRESULT)fp->err;if(res != FR_OK) LEAVE_FF(fs, res);/* 不允許快速查找 */{/* 偏移量超過文件大小,并且當前不在寫操作過程,則將偏移量調整為文件大小 */if(ofs > fp->obj.objsize && (_FS_READONLY || !(fp->flag & FA_WRITE))) {ofs = fp->obj.objsize;}/* 文件當前讀寫偏移量 */ifptr = fp->fptr;fp->fptr = nsect = 0;if(ofs) {/* 每個簇字節數 */bcs = (DWORD)fs->csize * SS(fs);/* 請求偏移量在當前偏移量所在簇或后面簇 */if(ifptr > 0 && (ofs - 1) / bcs >= (ifptr - 1) / bcs) {/* 當前簇起始偏移量 */fp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1);/* 剩余偏移量 */ofs -= fp->fptr;/* 從當前偏移量所在簇開始往后找 */clst = fp->clust;} /* 請求偏移量在當前偏移量所在簇的前年簇 */else { /* 文件起始簇 */clst = fp->obj.sclust;/* 沒有寫過任何數據,沒有創建簇鏈,則創建簇鏈 */if(clst == 0) {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;}/* 從文件起始簇開始找 */fp->clust = clst;}/* 向后找,更新當前偏移量、所在簇和扇區 */if(clst != 0) {while(ofs > bcs) {ofs -= bcs; fp->fptr += bcs;/* 寫請求,文件大小不夠需要擴展 */if(fp->flag & FA_WRITE) {if(_FS_EXFAT && fp->fptr > fp->obj.objsize) {fp->obj.objsize = fp->fptr;fp->flag |= FA_MODIFIED;}clst = create_chain(&fp->obj, clst);if(clst == 0) {ofs = 0; break;}} else{clst = get_fat(&fp->obj, clst);}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 = clust2sect(fs, clst); /* Current sector */if(!nsect) ABORT(fs, FR_INT_ERR);nsect += (DWORD)(ofs / SS(fs));}}}/* 擴展了簇鏈 */if(!_FS_READONLY && fp->fptr > fp->obj.objsize) {fp->obj.objsize = fp->fptr;fp->flag |= FA_MODIFIED;}/* 請求偏移量所在扇區和當前數據緩沖窗口所在扇區不同 */if(fp->fptr % SS(fs) && nsect != fp->sect) {/* 將原窗口數據寫入扇區 */if(fp->flag & FA_DIRTY) {if(disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);fp->flag &= (BYTE)~FA_DIRTY;}/* 將請求偏移量所在扇區數據讀到窗口中 */if(disk_read(fs->drv, fp->buf, nsect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);/* 更新偏移量所在扇區號 */fp->sect = nsect;}}LEAVE_FF(fs, res);
}/* 打開一個目錄項 */
FRESULT f_opendir(DIR *dp, const TCHAR *path)
{FRESULT res;FATFS *fs;_FDID *obj;if(!dp) return FR_INVALID_OBJECT;/* 通過路徑中的邏輯驅動器號,在邏輯驅動器上查找該卷是否被掛載,如果沒有則嘗試掛載 返回邏輯驅動器號后的路徑,返回文件系統指針 */obj = &dp->obj;res = find_volume(&path, &fs, 0);if(res == FR_OK) {/* 設置文件系統指針 */obj->fs = fs;/* 進入文件所在路徑(將偏移量定位到指定路徑) */res = follow_path(dp, path);if(res == FR_OK) {/* 不是原目錄本身 */if(!(dp->fn[NSFLAG] & NS_NONAME)) { /* 該對象是一個子目錄 */if(obj->attr & AM_DIR) {/* 從目錄項中讀出文件/目錄起始簇號 */obj->sclust = ld_clust(fs, dp->dir);}/* 該對象是一個文件 */ else {res = FR_NO_PATH;}}if(res == FR_OK) {obj->id = fs->id;/* 設置目錄偏移量到當前目錄起始位置 */res = dir_sdi(dp, 0);if(res == FR_OK) {if(obj->sclust) {/* 文件訪問次數加一 0:Read */obj->lockid = inc_lock(dp, 0);if(!obj->lockid) res = FR_TOO_MANY_OPEN_FILES;} /* 根目錄不需要鎖定 */else {obj->lockid = 0;}}}}if(res == FR_NO_FILE) res = FR_NO_PATH;}if(res != FR_OK) obj->fs = 0;LEAVE_FF(fs, res);
}/* 關閉一個目錄項 */
FRESULT f_closedir(DIR *dp)
{FRESULT res;FATFS *fs;/* 檢查該文件/目錄是否有效,返回文件系統指針 */res = validate(&dp->obj, &fs);if (res == FR_OK) {/* 文件訪問次數減一 */if(dp->obj.lockid) {res = dec_lock(dp->obj.lockid);}/* 清除所處文件系統指針,表示關閉 */if(res == FR_OK){dp->obj.fs = 0;}unlock_fs(fs, FR_OK);}return res;
}/* 在指定目錄中讀出一個目錄項 */
FRESULT f_readdir(DIR *dp, FILINFO *fno)
{FRESULT res;FATFS *fs;/* 檢查該文件/目錄是否有效,返回文件系統指針 */res = validate(&dp->obj, &fs);if(res == FR_OK) {if(!fno) {/* 設置目錄偏移量到目錄起始處 */res = dir_sdi(dp, 0);} else{/* 指定目錄中查找一個目錄項 */res = dir_read(dp, 0);if(res == FR_NO_FILE) res = FR_OK;if(res == FR_OK) {/* 獲取文件信息 */get_fileinfo(dp, fno);/* 當前偏移量轉移到下一個目錄項 */res = dir_next(dp, 0);if(res == FR_NO_FILE) res = FR_OK;}}}LEAVE_FF(fs, res);
}/* 讀取指定文件信息 */
FRESULT f_stat(const TCHAR *path, FILINFO *fno)
{FRESULT res;DIR dj;/* 通過路徑中的邏輯驅動器號,在邏輯驅動器上查找該卷是否被掛載,如果沒有則嘗試掛載 返回邏輯驅動器號后的路徑,返回文件系統指針 */res = find_volume(&path, &dj.obj.fs, 0);if (res == FR_OK) {/* 進入文件所在路徑(將偏移量定位到指定路徑) */res = follow_path(&dj, path);if(res == FR_OK) { if(dj.fn[NSFLAG] & NS_NONAME) {res = FR_INVALID_NAME;} else { /* 獲取文件信息 */if(fno) get_fileinfo(&dj, fno);}}}LEAVE_FF(dj.obj.fs, res);
}/* 獲取指定卷中的空閑簇個數,并返回文件系統指針 */
FRESULT f_getfree(const TCHAR *path, DWORD *nclst, FATFS **fatfs)
{FRESULT res;FATFS *fs;DWORD nfree, clst, sect, stat;UINT i;BYTE *p;_FDID obj;/* 通過路徑中的邏輯驅動器號,在邏輯驅動器上查找該卷是否被掛載,如果沒有則嘗試掛載 返回邏輯驅動器號后的路徑,返回文件系統指針 */res = find_volume(&path, &fs, 0);if(res == FR_OK) {/* 返回文件系統指針 */*fatfs = fs;/* 空閑簇個數值合理,返回空閑簇個數 */if(fs->free_clst <= fs->n_fatent - 2) {*nclst = fs->free_clst;} else {/* 通過FAT表計算空閑簇個數 */nfree = 0;if(fs->fs_type == FS_FAT12) {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 {{ /* FAT16/32: Sector alighed FAT entries */clst = fs->n_fatent; sect = fs->fatbase;i = 0; p = 0;do {if (i == 0) {res = move_window(fs, sect++);if (res != FR_OK) break;p = fs->win;i = SS(fs);}if (fs->fs_type == FS_FAT16) {if (ld_word(p) == 0) nfree++;p += 2; i -= 2;} else {if ((ld_dword(p) & 0x0FFFFFFF) == 0) nfree++;p += 4; i -= 4;}} while (--clst);}}*nclst = nfree;/* 更新一個合理的空閑簇個數到FSInfo中 */fs->free_clst = nfree; fs->fsi_flag |= 1;}}LEAVE_FF(fs, res);
}/* 將文件大小縮減到當前文件讀寫偏移量的位置 */
FRESULT f_truncate(FIL *fp)
{FRESULT res;FATFS *fs;DWORD ncl;/* 檢查該文件/目錄是否有效,返回文件系統指針 */res = validate(&fp->obj, &fs);if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);/* 文件必須可寫 */if(!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED);/* 文件讀寫偏移量不在尾部 */if(fp->fptr < fp->obj.objsize) {/* 文件讀寫偏移量在文件首部 */if(fp->fptr == 0) {/* 將文件對應的簇鏈所有簇移除 */res = remove_chain(&fp->obj, fp->obj.sclust, 0);fp->obj.sclust = 0;} /* 讀寫偏移量在中間 */else { /* 獲取讀寫偏移量所在簇對應下一個簇號 */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;fp->flag |= FA_MODIFIED;/* 需要回寫數據緩存窗口內容 */if(res == FR_OK && (fp->flag & FA_DIRTY)) {if(disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) {res = FR_DISK_ERR;} else {fp->flag &= (BYTE)~FA_DIRTY;}}if (res != FR_OK) ABORT(fs, res);}LEAVE_FF(fs, res);
}/* 刪除路徑所指文件/目錄 */
FRESULT f_unlink(const TCHAR *path)
{FRESULT res;DIR dj, sdj;DWORD dclst = 0;FATFS *fs;/* 通過路徑中的邏輯驅動器號,在邏輯驅動器上查找該卷是否被掛載,如果沒有則嘗試掛載 返回邏輯驅動器號后的路徑,返回文件系統指針 */res = find_volume(&path, &fs, FA_WRITE);dj.obj.fs = fs;if(res == FR_OK) {/* 進入文件所在路徑(將偏移量定位到指定路徑) */res = follow_path(&dj, path);if(_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) {res = FR_INVALID_NAME;}if(res == FR_OK) res = chk_lock(&dj, 2);if(res == FR_OK) {if(dj.fn[NSFLAG] & NS_NONAME) {res = FR_INVALID_NAME;} else {if(dj.obj.attr & AM_RDO) {res = FR_DENIED;}}if(res == FR_OK) {{/* 從目錄項中讀出文件/目錄起始簇號 */dclst = ld_clust(fs, dj.dir);}/* 這是一個目錄 */if(dj.obj.attr & AM_DIR) {{/* 設置目錄偏移量到該目錄起始處 */sdj.obj.fs = fs;sdj.obj.sclust = dclst;res = dir_sdi(&sdj, 0);if(res == FR_OK) {/* 指定目錄中查找一個文件/目錄 */res = dir_read(&sdj, 0);if(res == FR_OK) res = FR_DENIED; //該目錄不是空的if(res == FR_NO_FILE) res = FR_OK;}}}}/* 該目錄或文件可以刪除 */if(res == FR_OK) {/* 在指定目錄中移除當前偏移量指向的目錄項 */res = dir_remove(&dj);if(res == FR_OK && dclst) {/* 將該目錄/文件對應簇鏈移除 */res = remove_chain(&dj.obj, dclst, 0);}/* 將數據同步到文件系統 包括緩存窗口數據和FSInfo數據 */if(res == FR_OK) res = sync_fs(fs);}}}LEAVE_FF(fs, res);
}/* 創建一個目錄項 */
FRESULT f_mkdir(const TCHAR *path)
{FRESULT res;DIR dj;FATFS *fs;BYTE *dir;UINT n;DWORD dsc, dcl, pcl, tm;/* 通過路徑中的邏輯驅動器號,在邏輯驅動器上查找該卷是否被掛載,如果沒有則嘗試掛載 返回邏輯驅動器號后的路徑,返回文件系統指針 */res = find_volume(&path, &fs, FA_WRITE);dj.obj.fs = fs;if(res == FR_OK) {/* 進入文件所在路徑(將偏移量定位到指定路徑) */res = follow_path(&dj, path);if (res == FR_OK) res = FR_EXIST;if(_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) {res = FR_INVALID_NAME;}/* 可以創建該新目錄 */if(res == FR_NO_FILE) {/* 創建新簇鏈 */dcl = create_chain(&dj.obj, 0);/* 目錄項大小為一簇 */dj.obj.objsize = (DWORD)fs->csize * SS(fs);res = FR_OK;if(dcl == 0) res = FR_DENIED; if(dcl == 1) res = FR_INT_ERR;if(dcl == 0xFFFFFFFF) res = FR_DISK_ERR;/* 將緩存窗口數據同步到扇區中(更新FAT表) */if(res == FR_OK) res = sync_window(fs);/* 獲取時間戳 */tm = GET_FATTIME();if (res == FR_OK) { /* 將目錄起始簇號轉換為扇區號 */dsc = clust2sect(fs, dcl);/* 初始化目錄中的兩個目錄項 .和.. */dir = fs->win;mem_set(dir, 0, SS(fs));if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) {/* 第一個目錄項. */mem_set(dir + DIR_Name, ' ', 11); /* 清空文件名 */dir[DIR_Name] = '.'; //目錄項名.dir[DIR_Attr] = AM_DIR; //屬性為目錄st_dword(dir + DIR_ModTime, tm); //修改時間st_clust(fs, dir, dcl); /* 將目錄.起始簇號寫入目錄項 *//* 第二個目錄項.. */mem_cpy(dir + SZDIRE, dir, SZDIRE); dir[SZDIRE + 1] = '.'; //目錄項名..pcl = dj.obj.sclust;if(fs->fs_type == FS_FAT32 && pcl == fs->dirbase) pcl = 0;st_clust(fs, dir + SZDIRE, pcl); //目錄項..起始簇號寫入目錄項}/* 將目錄當前簇的數據寫進去(第一扇區包含.和..,其它空間清零) */for(n = fs->csize; n; n--) {fs->winsect = dsc++;fs->wflag = 1;res = sync_window(fs);if(res != FR_OK) break;mem_set(dir, 0, SS(fs));}}if(res == FR_OK) {/* 將創建號的目錄項加入目錄 */res = dir_register(&dj);}/* 設置新創建的目錄項的文件信息 */if(res == FR_OK) {{dir = dj.dir;st_dword(dir + DIR_ModTime, tm); /* Created time */st_clust(fs, dir, dcl); /* Table start cluster */dir[DIR_Attr] = AM_DIR; /* Attribute */fs->wflag = 1;}if(res == FR_OK) {res = sync_fs(fs);}}/* 沒注冊成功,刪除創建好的目錄項簇鏈 */ else {remove_chain(&dj.obj, dcl, 0);}}}LEAVE_FF(fs, res);
}/* 重命名目錄/文件 */
FRESULT f_rename(const TCHAR *path_old, const TCHAR *path_new)
{FRESULT res;DIR djo, djn;FATFS *fs;BYTE buf[_FS_EXFAT ? SZDIRE * 2 : 24], *dir;DWORD dw;/* 從路徑中取出邏輯驅動器號,返回剩余路徑段 */get_ldnumber(&path_new);/* 通過路徑中的邏輯驅動器號,在邏輯驅動器上查找該卷是否被掛載同時檢查磁盤是否寫保護,如果沒有則嘗試掛載 返回邏輯驅動器號后的路徑,返回文件系統指針 */res = find_volume(&path_old, &fs, FA_WRITE);if(res == FR_OK) {djo.obj.fs = fs;/* 進入文件所在路徑(將偏移量定位到指定路徑) */res = follow_path(&djo, path_old);if(res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME;if(res == FR_OK) {res = chk_lock(&djo, 2);}/* 重命名的對象被找到 */if(res == FR_OK) {{ /* 保存名稱以外的文件信息 */mem_cpy(buf, djo.dir + DIR_Attr, 21);/* 復制目錄項 */mem_cpy(&djn, &djo, sizeof(DIR));/* 進入新文件所在路徑(將偏移量定位到指定路徑) */res = follow_path(&djn, path_new);if(res == FR_OK) {res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST;}/* 新文件名不沖突 */if(res == FR_NO_FILE) {/* 在指定目錄中創建新文件名的目錄項 */res = dir_register(&djn); /* Register the new entry */if (res == FR_OK) {/* 將老文件名的目錄項信息拷貝到新目錄項 */dir = djn.dir;mem_cpy(dir + 13, buf + 2, 19);dir[DIR_Attr] = buf[0] | AM_ARC;fs->wflag = 1;/* 新老目錄項不在同一個簇中 */if((dir[DIR_Attr] & AM_DIR) && djo.obj.sclust != djn.obj.sclust) {/* 將新目錄中的..目錄項的起始簇更新過來 */dw = clust2sect(fs, ld_clust(fs, dir));if(!dw) {res = FR_INT_ERR;} else {res = move_window(fs, dw);dir = fs->win + SZDIRE * 1;if (res == FR_OK && dir[1] == '.') {st_clust(fs, dir, djn.obj.sclust);fs->wflag = 1;}}}}}}/* 移除老的目錄項 */if(res == FR_OK) {res = dir_remove(&djo);if(res == FR_OK) {res = sync_fs(fs);}}}}LEAVE_FF(fs, res);
}/* 創建一個FAT卷 opt指定文件系統類型 au指定簇字節數 work和len工作緩沖區指針和長度(最好是一個扇區) */
FRESULT f_mkfs(const TCHAR *path, BYTE opt, DWORD au, void *work, UINT len)
{const UINT n_fats = 1;const UINT n_rootdir = 512;static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0};static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0};BYTE fmt, sys, *buf, *pte, pdrv;WORD ss;DWORD szb_buf, sz_buf, sz_blk, n_clst, pau, sect, nsect, n;DWORD b_vol, b_fat, b_data;DWORD sz_vol, sz_rsv, sz_fat, sz_dir;UINT i;int vol;DSTATUS stat;/* 從路徑中取出邏輯驅動器號,返回剩余路徑段 */vol = get_ldnumber(&path);if (vol < 0) return FR_INVALID_DRIVE;/* 清除原來的卷 */if(FatFs[vol])FatFs[vol]->fs_type = 0;/* 物理設備號 */pdrv = LD2PD(vol);/* 檢查物理設備狀態 */stat = disk_initialize(pdrv);if(stat & STA_NOINIT) return FR_NOT_READY;if(stat & STA_PROTECT) return FR_WRITE_PROTECTED;/* 塊扇區個數 */if(disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK || !sz_blk || sz_blk > 32768 || (sz_blk & (sz_blk - 1))) sz_blk = 1;/* 扇區字節數 */ss = _MAX_SS;if((au != 0 && au < ss) || au > 0x1000000 || (au & (au - 1))) return FR_INVALID_PARAMETER;/* 每個簇多少扇區 */au /= ss;/* 工作緩沖區指針、扇區數、字節數(扇區對齊) */buf = (BYTE *)work;sz_buf = len / ss;szb_buf = sz_buf * ss;if(!szb_buf) return FR_MKFS_ABORTED;/* 創建一個單分區 *//* 獲取扇區個數 */if(disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) return FR_DISK_ERR;/* 卷起始扇區 */b_vol = (opt & FM_SFD) ? 0 : 63;if(sz_vol < b_vol) return FR_MKFS_ABORTED;/* 卷扇區個數 */sz_vol -= b_vol;if(sz_vol < 128) return FR_MKFS_ABORTED;/* 指定FAT類型 */do { if(au > 128) return FR_INVALID_PARAMETER;/* FAT32 */if(opt & FM_FAT32) {if((opt & FM_ANY) == FM_FAT32 || !(opt & FM_FAT)) {fmt = FS_FAT32; break;}}if(!(opt & FM_FAT)) return FR_INVALID_PARAMETER;/* FAT12/16 */fmt = FS_FAT16;}while(0);/* 創建FAT12/16/32卷 */do{pau = au;/* 預先確定了FAT32類型 */if(fmt == FS_FAT32) {/* 沒指定簇大小 */if(!pau) {/* 一個卷有多少128K扇區單元 */n = sz_vol / 0x20000;/* 計算一個簇占多少扇區 */for(i = 0, pau = 1; cst32[i] && cst32[i] <= n; i++, pau <<= 1) ;}/* 計算簇個數 */n_clst = sz_vol / pau;/* FAT表的扇區數 */sz_fat = (n_clst * 4 + 8 + ss - 1) / ss;/* 保留扇區個數 */sz_rsv = 32;/* 無靜態目錄 */sz_dir = 0;/* 簇個數不符合FAT32要求 */if(n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) return FR_MKFS_ABORTED;}/* FAT12/16類型 */else {/* 沒指定簇大小 */if(!pau) {/* 一個卷有多少4KS扇區單元 */n = sz_vol / 0x1000;/* 計算一個簇占多少扇區 */for(i = 0, pau = 1; cst[i] && cst[i] <= n; i++, pau <<= 1) ;}/* 計算簇個數 */n_clst = sz_vol / pau;/* FAT16 */if(n_clst > MAX_FAT12) {n = n_clst * 2 + 4;} /* FAT12 */else {fmt = FS_FAT12;n = (n_clst * 3 + 1) / 2 + 3;}/* FAT表的扇區數 */sz_fat = (n + ss - 1) / ss;/* 保留區域扇區數 */sz_rsv = 1;/* 根目錄扇區數 */sz_dir = (DWORD)n_rootdir * SZDIRE / ss;}/* FAT起始扇區 */b_fat = b_vol + sz_rsv;/* 數據起始扇區 */b_data = b_fat + sz_fat * n_fats + sz_dir;/* 向后n扇區可進行擦除塊對齊 */n = ((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data;if(fmt == FS_FAT32) {/* 移動保留區和FAT表起始扇區 */sz_rsv += n; b_fat += n;} else { /* 擴展FAT大小 */sz_fat += n / n_fats;}if(sz_vol < b_data + pau * 16 - b_vol) return FR_MKFS_ABORTED;/* 計算簇個數 */n_clst = (sz_vol - sz_rsv - sz_fat * n_fats - sz_dir) / pau;if(fmt == FS_FAT32) {if(n_clst <= MAX_FAT16) {if(!au && (au = pau / 2) != 0) continue;return FR_MKFS_ABORTED;}}/* 對于FAT16 */if(fmt == FS_FAT16) {/* 簇太多了 */if(n_clst > MAX_FAT16) {/* 簇扇區個數很少,可以調整,但不能大于64 */if(!au && (pau * 2) <= 64) {au = pau * 2; continue;}/* 換成FAT32 */if((opt & FM_FAT32)) {fmt = FS_FAT32; continue;}/* 簇扇區個數很少,可以調整,但不能大于128 */if(!au && (au = pau * 2) <= 128) continue;return FR_MKFS_ABORTED;}/* 簇太少了 */if (n_clst <= MAX_FAT12) { /* 簇扇區個數很少,可以調整,但不能大于128 */if(!au && (au = pau * 2) <= 128) continue;return FR_MKFS_ABORTED;}}/* 對于FAT12來說簇太多了 */if(fmt == FS_FAT12 && n_clst > MAX_FAT12) return FR_MKFS_ABORTED;/* 計算出了合理的簇扇區數和簇數 */break;}while(1);/* 創建FAT的boot扇區 */mem_set(buf, 0, ss);/* x86跳轉指令 */mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11); /* FAT12/16:扇區大小 FAT32:0 */st_word(buf + BPB_BytsPerSec, ss); /* 簇的扇區數 */ buf[BPB_SecPerClus] = (BYTE)pau; /* 保留區的扇區數 */st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* FAT表個數 */buf[BPB_NumFATs] = (BYTE)n_fats; /* FAT12/16:根目錄中目錄項個數 FAT32:0 */st_word(buf + BPB_RootEntCnt, (WORD)((fmt == FS_FAT32) ? 0 : n_rootdir));/* FAT卷的總扇區數 */if(sz_vol < 0x10000) {st_word(buf + BPB_TotSec16, (WORD)sz_vol);} else {st_dword(buf + BPB_TotSec32, sz_vol);}/* 媒體確定,不再用于任何目的 */buf[BPB_Media] = 0xF8;/* 每個軌道的扇區數 */st_word(buf + BPB_SecPerTrk, 63);/* 磁頭數 */st_word(buf + BPB_NumHeads, 255);/* FAT卷之前隱藏的物理扇區數 */st_dword(buf + BPB_HiddSec, b_vol);/* FAT32 */if(fmt == FS_FAT32) {/* 卷序列號 */st_dword(buf + BS_VolID32, GET_FATTIME());/* FAT占用的扇區數 */st_dword(buf + BPB_FATSz32, sz_fat);/* 根目錄起始簇號 */st_dword(buf + BPB_RootClus32, 2);/* FSInfo的扇區相對于FAT32卷的頂部的偏移 */st_word(buf + BPB_FSInfo32, 1);/* 備份引導扇區相對于FAT32卷頂部的偏移扇區 */st_word(buf + BPB_BkBootSec32, 6);/* 驅動器號 */buf[BS_DrvNum32] = 0x80;/* 擴展引導簽名 */buf[BS_BootSig32] = 0x29;/* 卷標 */mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); }/* FAT12/16 */else {/* 卷序列號 */st_dword(buf + BS_VolID, GET_FATTIME());/* FAT占用的扇區數 */st_word(buf + BPB_FATSz16, (WORD)sz_fat);/* 驅動器號 */buf[BS_DrvNum] = 0x80;/* 擴展引導簽名 */buf[BS_BootSig] = 0x29;/* 卷標 */mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", 19);}/* 引導簽名 */st_word(buf + BS_55AA, 0xAA55);if(disk_write(pdrv, buf, b_vol, 1) != RES_OK) return FR_DISK_ERR;/* FAT32還要創建FSINFO */if(fmt == FS_FAT32) {/* boot扇區備份 */disk_write(pdrv, buf, b_vol + 6, 1);mem_set(buf, 0, ss);/* FSInfo的主要簽名 */st_dword(buf + FSI_LeadSig, 0x41615252);st_dword(buf + FSI_StrucSig, 0x61417272);/* 空閑簇數 */st_dword(buf + FSI_Free_Count, n_clst - 1);/* 最后一個被分配的簇號,程序應該從此開始尋找空閑簇 */st_dword(buf + FSI_Nxt_Free, 2);/* 引導簽名 */st_word(buf + BS_55AA, 0xAA55);/* FSINFO扇區備份 */disk_write(pdrv, buf, b_vol + 7, 1);/* FSINFO扇區 */disk_write(pdrv, buf, b_vol + 1, 1);}/* 初始化FAT區域 */mem_set(buf, 0, (UINT)szb_buf);sect = b_fat;for(i = 0; i < n_fats; i++) {/* 設置FAT表項簇0和簇1 */if(fmt == FS_FAT32) {st_dword(buf + 0, 0xFFFFFFF8); /* 簇0 */st_dword(buf + 4, 0xFFFFFFFF); /* 簇1 */st_dword(buf + 8, 0x0FFFFFFF); /* 簇2(根目錄) */}else {st_dword(buf + 0, (fmt == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8);}/* 其余FAT表項清空 */nsect = sz_fat;do {n = (nsect > sz_buf) ? sz_buf : nsect;if(disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR;mem_set(buf, 0, ss);sect += n; nsect -= n;}while(nsect);}/* 初始化根目錄 */nsect = (fmt == FS_FAT32) ? pau : sz_dir;do {n = (nsect > sz_buf) ? sz_buf : nsect;if(disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR;sect += n; nsect -= n;} while(nsect);/* 文件系統類型ID */if(fmt == FS_FAT32) {sys = 0x0C;} else {if(sz_vol >= 0x10000) {sys = 0x06; /* FAT12/16 (>=64KS) */} else{sys = (fmt == FS_FAT16) ? 0x04 : 0x01; /* FAT16 (<64KS) : FAT12 (<64KS) */}}/* 創建MBR區域 */if(!(opt & FM_SFD)) {mem_set(buf, 0, ss);st_word(buf + BS_55AA, 0xAA55); /* MBR簽名 */pte = buf + MBR_Table; /* 分區表指針 */pte[PTE_Boot] = 0; /* 引導標志 0x00:非活動分區 */pte[PTE_StHead] = 1; /* 起始磁頭號 */pte[PTE_StSec] = 1; /* 起始扇區號 */pte[PTE_StCyl] = 0; /* 起始柱面號 */pte[PTE_System] = sys; /* 分區類型 0x06:FAT16 0x0B:FAT32 */n = (b_vol + sz_vol) / (63 * 255); /* (End CHS may be invalid) */pte[PTE_EdHead] = 254; /* 結束磁頭號 */pte[PTE_EdSec] = (BYTE)(n >> 2 | 63); /* 結束扇區號*/pte[PTE_EdCyl] = (BYTE)n; /* 結束柱面號 */st_dword(pte + PTE_StLba, b_vol); /* 邏輯起始扇區號 */st_dword(pte + PTE_SizLba, sz_vol); /* 總扇區數 */if(disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR;}if(disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) return FR_DISK_ERR;return FR_OK;
}
?
總結
- 上一篇: 脉冲波形的变换与产生
- 下一篇: Simulink之相控整流电路的组合