日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

FatFs源码分析

發布時間:2025/3/15 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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; }

?

總結

以上是生活随笔為你收集整理的FatFs源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

91热视频| 99精彩视频在线观看免费 | 国产破处在线播放 | 国产成人av综合色 | 青青河边草观看完整版高清 | 欧美91视频 | 婷婷久久一区二区三区 | 成人影片免费 | 久久久久久久综合色一本 | 亚洲一区日韩精品 | 午夜色性片 | 亚洲精品女人 | 国产精品 久久 | 九色最新网址 | 国产精品乱码高清在线看 | 久久国产精品免费看 | 探花视频在线观看免费 | 91中文字幕永久在线 | 91av社区| 中文字幕 第二区 | 日韩高清激情 | 国产精品一区在线 | 蜜臀久久99静品久久久久久 | 在线观看视频在线观看 | 99亚洲精品 | 99中文在线 | 欧洲性视频 | 在线视频观看你懂的 | 久久综合免费 | 天天操偷偷干 | 在线小视频你懂的 | 青青草在久久免费久久免费 | 亚洲精品在线观 | av一区二区三区在线 | 天天视频亚洲 | 久久久久久国产一区二区三区 | 狠狠狠色丁香综合久久天下网 | 国产精品va在线播放 | 国产精品原创在线 | 九九热久久久 | www黄免费 | 色婷婷色| 天天射狠狠干 | 91九色国产视频 | 久久9999久久免费精品国产 | 一区二区三区精品在线视频 | 91免费在线视频 | 国产高清在线免费视频 | 亚洲一级片 | 狠狠狠色丁香综合久久天下网 | 久久99国产综合精品免费 | 麻豆91在线观看 | 最近中文字幕在线播放 | 午夜性盈盈 | 国产精品久久久久久高潮 | 日本精品一区二区三区在线播放视频 | 米奇影视7777| 日本夜夜草视频网站 | 四虎影视8848dvd | 五月婷婷视频在线 | 久久精品欧美日韩精品 | 久久精品三 | 韩国在线一区二区 | 亚洲国产精品资源 | 一区 二区 精品 | 精品久久免费 | 国产精品一区二区三区在线看 | 一区二区三区在线免费观看视频 | 国产一区二区影院 | 中文字幕免费高清在线 | 日韩欧美精品在线视频 | 免费高清看电视网站 | 成年人在线电影 | 你操综合 | 国产精品理论片在线观看 | 亚洲日本黄色 | 日韩在线不卡视频 | 在线播放亚洲 | 美女网站在线看 | 4hu视频| 99精品国产兔费观看久久99 | 亚洲电影黄色 | 色视频网站免费观看 | 国内精品一区二区 | 欧美日韩中文在线观看 | 免费看黄电影 | 伊人天天狠天天添日日拍 | 丁香五香天综合情 | 在线免费视频你懂的 | 91传媒在线播放 | 日韩欧美在线综合网 | 亚洲在线免费视频 | 国产精品一区二区电影 | 天天曰夜夜操 | 亚洲色图av | 日韩免费不卡视频 | 黄色电影小说 | 一区二区伦理 | 亚洲成人av电影 | 久久久久国产一区二区三区 | 成人啪啪18免费游戏链接 | 91亚洲精品在线 | 久久99视频免费观看 | 国产一级二级在线 | 日韩欧美成| 久久精品国产亚洲 | 中文字幕日韩有码 | 999精品视频 | 正在播放国产精品 | 五月婷婷欧美 | 国产婷婷vvvv激情久 | 国产在线不卡 | 久久久久福利视频 | 久久av福利 | 国产一级精品视频 | 在线免费亚洲 | 日韩高清精品一区二区 | 亚洲一区二区黄色 | 日产乱码一二三区别在线 | 国产视频69 | 九色91在线 | 亚洲精品视频观看 | 久久96国产精品久久99软件 | 国产又粗又长的视频 | 在线视频在线观看 | 国产亚洲91 | 国产一区二区久久 | 亚洲久久视频 | 欧美日韩视频 | 国产视频精选在线 | 香蕉视频在线免费看 | 日韩免费播放 | 国产精品第10页 | 在线综合 亚洲 欧美在线视频 | 成人综合婷婷国产精品久久免费 | 中文字幕av电影下载 | 婷婷免费在线视频 | 日韩在线观看三区 | 国外av在线| 黄色大全在线观看 | 中文字幕在线精品 | 国产精品久久久久亚洲影视 | 狠狠的干 | 久久草草影视免费网 | 亚洲黄色激情小说 | 亚洲最大激情中文字幕 | 欧美日韩国产一区二区三区 | 国产在线精品一区二区不卡了 | 日韩视频三区 | 中文在线天堂资源 | 色九九影院 | 大型av综合网站 | 婷婷5月激情5月 | 久日视频 | 夜色.com | 手机看片中文字幕 | 激情网第四色 | 日日躁夜夜躁aaaaxxxx | 人人爱人人做人人爽 | 手机av电影在线 | 69久久夜色精品国产69 | 中文字幕中文字幕在线中文字幕三区 | 波多野结衣视频一区二区 | 亚洲天天摸日日摸天天欢 | 日日精品 | 激情电影在线观看 | 久久精品99国产精品酒店日本 | 久久这里有 | 亚洲永久字幕 | 天天操夜操 | 日韩欧美在线视频一区二区 | 91av成人 | 免费久久99精品国产婷婷六月 | 日韩美女黄色片 | 激情视频免费观看 | 国产不卡在线观看 | 国产午夜在线观看 | 中日韩三级视频 | 美女国内精品自产拍在线播放 | 91精品福利在线 | 在线观看第一页 | 国产成人一区二区啪在线观看 | 日本中文一级片 | 女人18毛片a级毛片一区二区 | 欧美在线观看视频 | 在线观看小视频 | 99情趣网视频 | 91人人澡人人爽 | 成人黄色资源 | 91精品一区二区在线观看 | 在线观看免费一区 | 亚洲精品欧美精品 | 日本三级大片 | 中文永久免费观看 | 成人欧美日韩国产 | 免费精品| 欧美成人高清 | 国产99久久久国产精品免费二区 | 亚洲乱亚洲乱亚洲 | 色婷婷国产在线 | 久久久久久久久精 | 99热这里精品 | 伊人天天操 | 91看片麻豆 | 日韩精品一区电影 | 欧美在线视频精品 | 婷婷精品在线 | 狠狠躁日日躁夜夜躁av | 国产精品中文字幕av | 麻豆传媒视频在线免费观看 | 免费在线中文字幕 | 国产男女无遮挡猛进猛出在线观看 | 国产一级视频 | 久久理论片 | 国产美女精品人人做人人爽 | 国产精品一区二区免费在线观看 | 激情五月综合网 | 五月综合在线观看 | 国产精品剧情 | 中文字幕在线色 | 久久人91精品久久久久久不卡 | 日韩一级片观看 | 国产原创在线 | 婷婷亚洲五月色综合 | 蜜臀av.com | www.大网伊人 | 99久久爱| 久久不卡日韩美女 | 国产区免费在线 | 一级淫片a | 999抗病毒口服液 | 欧美日韩一区二区在线观看 | 色婷婷国产精品一区在线观看 | 亚洲区色| 国产一区欧美一区 | 日韩av黄| 一区中文字幕在线观看 | 伊人五月在线 | av片一区二区| 激情五月婷婷激情 | 91在线观看黄 | 免费网站v | 国产做a爱一级久久 | 日批视频在线观看免费 | 亚洲精品福利在线观看 | 久久久久亚洲精品男人的天堂 | 综合网婷婷 | 人人狠狠综合久久亚洲 | 欧美色图亚洲图片 | 99精品视频精品精品视频 | 去干成人网 | 亚洲精品美女久久 | 国产精品18p | 人人爽人人乐 | 黄色a级片在线观看 | av在线激情 | 一区 二区 精品 | 色吊丝在线永久观看最新版本 | 亚洲一区二区三区四区在线视频 | 最近2019中文免费高清视频观看www99 | 国产精品wwwwww | 中文字幕国语官网在线视频 | 美女久久精品 | 国产午夜精品一区二区三区欧美 | 精品一区精品二区高清 | 欧洲黄色片 | 97韩国电影| 亚洲毛片视频 | 欧美日韩精品网站 | av在线观 | 亚洲最新在线视频 | 天天综合久久综合 | 中文亚洲欧美日韩 | 国产麻豆精品久久 | 久久露脸国产精品 | 91av综合| 日韩欧美在线免费观看 | 狠狠躁日日躁 | 亚洲九九 | 欧日韩在线 | 国产手机视频在线 | 亚洲国产精品久久久久婷婷884 | 激情五月看片 | 人人澡人人爽欧一区 | 亚洲人在线视频 | 国产大片免费久久 | 成人久久久久 | 99这里只有久久精品视频 | 黄色成品视频 | 91成人天堂久久成人 | 91视频在线免费下载 | 中文字幕在线视频一区 | wwwwww国产 | 亚洲国产精品成人女人久久 | 久久久国产影院 | 午夜精品视频在线 | 18国产精品福利片久久婷 | 999视频网| 欧美激情精品久久久 | 中文字幕在线观看完整版 | 免费a网站 | 欧美成年网站 | 免费在线一区二区 | 我要看黄色一级片 | 国产区在线 | 国产高清久久久久 | 日韩美视频 | 成人资源在线播放 | 精品国产精品国产偷麻豆 | 久久久久久国产精品免费 | 国产黄免费看 | 国产精品2018| 国产91学生粉嫩喷水 | 9ⅰ精品久久久久久久久中文字幕 | 插插插色综合 | 久久久这里有精品 | 免费看黄色小说的网站 | 日韩一区视频在线 | 亚洲观看黄色网 | 国产精品久久久久久久久久久不卡 | 在线小视频 | 麻豆av一区二区三区在线观看 | 国产午夜精品一区 | 欧美日韩一区二区久久 | 欧美视频一区二 | 一级一片免费观看 | 最近最新最好看中文视频 | 一区二精品 | 成人午夜电影网站 | 久插视频 | 91理论片午午伦夜理片久久 | av日韩在线网站 | 欧美成年黄网站色视频 | 国内精品久久久久影院一蜜桃 | 免费色av | 美女黄久久 | 99九九热只有国产精品 | 欧美日韩中字 | 日韩中文幕 | 天天干com | 91亚洲影院 | 日韩videos| 国产高清不卡在线 | 欧美黑吊大战白妞欧美 | 亚洲狠狠丁香婷婷综合久久久 | 久久99久久99精品中文字幕 | 欧美a级在线播放 | 99久精品 | 美女视频一区 | 2018好看的中文在线观看 | 在线色视频小说 | 中文字幕av在线电影 | 国产在线中文字幕 | 国产专区精品 | 在线观看黄色的网站 | 在线观看91 | 国产又粗又长的视频 | 国产色视频网站 | 国产成人三级在线 | 一区在线观看 | av短片在线观看 | 国产一区在线视频播放 | 亚洲精品在线观看av | 国产精品视频专区 | 天天干干 | 视频在线在亚洲 | 欧洲亚洲激情 | 国产精品乱码在线 | 91成熟丰满女人少妇 | 久久国内精品99久久6app | 久久黄色影院 | 久久国产精品99久久人人澡 | 国产成人精品av | 国产精品视频久久久 | 免费不卡中文字幕视频 | 黄色在线观看免费 | 精品欧美一区二区在线观看 | www.久热| 97夜夜澡人人双人人人喊 | 国产伦理久久 | 欧美色插 | 夜夜澡人模人人添人人看 | 久久九九九九 | 久久综合久久八八 | 亚洲免费在线观看视频 | 中文字幕中文字幕在线中文字幕三区 | 亚洲成人免费在线观看 | 国产精品高潮久久av | 日本黄色免费观看 | 91成人精品视频 | 狠狠干中文字幕 | 精品一区精品二区高清 | 99久久精品日本一区二区免费 | 免费观看完整版无人区 | 91av美女| 国产色黄网站 | 一区在线观看视频 | 97看片吧| 美女福利视频网 | 97免费在线视频 | 欧美在线视频一区二区三区 | 黄av在线 | 亚洲清纯国产 | 亚洲精品国产麻豆 | 中文在线8新资源库 | 久久久国产影院 | 97视频在线 | 免费在线观看国产黄 | 最新国产中文字幕 | 在线观看中文 | 亚洲欧美经典 | 久久福利综合 | 天堂在线一区 | 99在线视频精品 | 天天操天 | 国产一级在线视频 | 中文字幕一二三区 | 午夜视频日本 | 亚洲精品动漫成人3d无尽在线 | 麻豆视频免费在线 | 成人国产精品一区 | 亚洲精品午夜aaa久久久 | 91麻豆精品国产91久久久久 | 婷婷丁香av| 色偷偷88888欧美精品久久久 | 狠狠干综合| 91免费高清 | www.天天干| 爱色av.com| 久久噜噜少妇网站 | 亚洲成人黄色在线观看 | 色97在线 | 97天堂网| 国产一级片视频 | 日韩av成人在线观看 | 久久爱www.| 免费在线观看一级片 | 成人cosplay福利网站 | 亚洲最大av在线播放 | 久久色中文字幕 | 久久婷婷丁香 | 天天操天天干天天摸 | 免费看毛片在线 | 亚洲91av| 欧美aa级| 亚洲国产中文字幕在线观看 | 久久久免费网站 | 日本公妇在线观看高清 | 国产精品一区在线观看你懂的 | 亚洲黄色av | 亚洲精品免费在线播放 | 免费成人在线网站 | 香蕉视频在线播放 | av在线免费观看网站 | 久久精品直播 | 99久久国产免费,99久久国产免费大片 | 国产亚洲视频系列 | 午夜精品久久久久久久99热影院 | 国产精品一区专区欧美日韩 | 欧美午夜精品久久久久久浪潮 | 69久久久| 国产精品人人做人人爽人人添 | 91在线视频精品 | 一区二精品 | 免费男女羞羞的视频网站中文字幕 | 国产一级免费电影 | 免费网站黄 | 亚洲aⅴ在线 | 亚洲免费高清视频 | 91桃花视频 | av大全在线看 | 蜜臀av夜夜澡人人爽人人桃色 | 欧美一级性 | 色999视频| 在线 国产 亚洲 欧美 | 欧美 日韩 国产 成人 在线 | 国产精品一区二区在线观看免费 | 中文免费在线观看 | 四虎伊人| 日韩三级久久 | 午夜性生活片 | 五月婷婷久 | 99精品成人 | 狠狠色噜噜狠狠狠狠2021天天 | 久久综合五月天 | 视频国产区 | 国产一区二区高清 | 午夜视频欧美 | 日韩欧三级 | 中文字幕电影高清在线观看 | 婷婷在线看 | 久久久久久久久黄色 | av久久在线 | 亚洲免费视频在线观看 | 亚洲精品乱码久久久一二三 | 婷婷伊人综合亚洲综合网 | 亚洲精品在线观看av | 黄色片亚洲 | 国产精品扒开做爽爽的视频 | 永久免费av在线播放 | 国产96av | 五月花激情| 国产99久久久国产精品免费二区 | 日韩中出在线 | 欧美影片 | av在线h | se视频网址| 亚洲妇女av| 精品视频9999 | 性色av一区二区 | 日日麻批40分钟视频免费观看 | 三上悠亚一区二区在线观看 | 9免费视频 | 97热视频| 国产黄网在线 | 天天综合在线观看 | 美女网站一区 | 91在线资源 | 国产在线97 | 国产一区二区网址 | 成人黄色电影在线观看 | 亚洲精品大全 | 久久久久人人 | 成年人看片网站 | 97手机电影网 | 中文字幕在线观看视频一区二区三区 | 久久av伊人 | 亚洲黄色片一级 | 麻豆视频观看 | 天天射色综合 | 欧美精品一区二区三区一线天视频 | 又黄又爽又刺激的视频 | 国色天香第二季 | 香蕉视频4aa | 色吧av色av| 免费在线观看毛片网站 | 中文字幕在线观看免费观看 | 亚洲干 | av资源在线看 | 国产一区二区精品久久91 | 天天色天天色天天色 | 免费在线观看成人av | 久久怡红院 | 亚洲精品黄色在线观看 | 国产精品手机视频 | 国产韩国日本高清视频 | 91网在线 | 成人国产精品入口 | 在线免费高清 | 人人爽人人av | 免费日韩一区二区三区 | 亚洲国产视频直播 | 91精彩视频在线观看 | 91亚洲精品视频 | 美女福利视频网 | 碰超在线 | 久久夜色精品亚洲噜噜国4 午夜视频在线观看欧美 | 欧美激情视频久久 | 欧美a视频在线观看 | 四虎亚洲精品 | 日韩1页 | 国产视频在线免费 | 精品一区精品二区高清 | 尤物九九久久国产精品的分类 | 久草视频国产 | 色精品视频 | 日韩毛片久久久 | 伊人五月 | 天天干天天做天天操 | 欧美精彩视频 | 日韩精品中文字幕在线播放 | 特级毛片爽www免费版 | 成人av手机在线 | 97超碰资源总站 | 欧美贵妇性狂欢 | 久久精品a | 国产亚洲综合精品 | 欧美韩国日本在线观看 | 久久精品99精品国产香蕉 | 亚洲欧美成人网 | 在线看日韩| www.一区二区三区 | av大片免费在线观看 | 国产成人精品久久久 | 97色婷婷人人爽人人 | 日韩免费福利 | 少妇搡bbbb搡bbb搡忠贞 | 五月天久久精品 | 久久综合色综合88 | 欧美精品久久久久久久久免 | 国产在线免费av | 天天操天天操天天操天天操 | 国产在线播放一区二区 | 久久精品免视看 | 国产三级香港三韩国三级 | 狠狠色丁香婷婷 | 亚洲欧美国产精品 | a久久免费视频 | 天天躁日日躁狠狠躁av麻豆 | 五月婷婷综合网 | 欧美极品久久 | 欧美日韩免费观看一区二区三区 | 日韩三级视频 | 91免费视频黄 | 黄色软件视频大全免费下载 | 国产婷婷一区二区 | 亚洲乱码在线观看 | 精品国产一区二 | 久久久午夜精品福利内容 | 涩av在线| 色噜噜狠狠色综合中国 | 免费观看一级特黄欧美大片 | 久久99久久99精品中文字幕 | 欧美日韩视频在线一区 | 91久久国产综合精品女同国语 | 丁香高清视频在线看看 | 99这里有精品 | 久久久毛片 | 亚洲精品久久久久999中文字幕 | 丁香六月伊人 | 超碰在97| 天天综合色| 在线免费观看国产精品 | 在线观看日韩视频 | 日本高清中文字幕有码在线 | 九九九九九九精品任你躁 | 免费福利片2019潦草影视午夜 | 中文字幕4 | 六月色丁 | 综合色中色 | 在线激情网 | 精品久久中文 | 国产69精品久久久久99 | 久久艹中文字幕 | 91精品国产成人www | 日本在线中文在线 | 日韩专区在线 | 欧美大香线蕉线伊人久久 | 久久人人97超碰国产公开结果 | 午夜影视一区 | 永久免费毛片在线观看 | 日韩亚洲国产中文字幕 | 九九九电影免费看 | 九九影视理伦片 | 国产成人精品一区二区三区免费 | 黄色一级在线免费观看 | www.狠狠| 菠萝菠萝在线精品视频 | 国产乱对白刺激视频在线观看女王 | 国产日韩欧美精品在线观看 | 日韩午夜三级 | 国产精品毛片久久久久久久 | 国产三级视频 | 国内精品久久久久久久影视麻豆 | 天天综合网入口 | 午夜视频一区二区三区 | 国产免费av一区二区三区 | 97免费在线观看视频 | 免费看污污视频的网站 | 九九热精品视频在线播放 | 四虎成人精品 | 国产精品爽爽久久久久久蜜臀 | 9i看片成人免费看片 | 久久伊99综合婷婷久久伊 | 五月激情久久 | 色视频国产直接看 | 不卡的一区二区三区 | 狠狠操在线 | 在线影院 国内精品 | 97国产超碰 | 日韩成人av在线 | 欧美九九九 | 日韩一级电影在线观看 | 丁香婷婷激情 | 国产精品一区二区在线观看 | 一区二区中文字幕在线播放 | 大胆欧美gogo免费视频一二区 | 99中文字幕视频 | 波多野结衣视频一区二区 | 欧美日韩国产色综合一二三四 | 天天干天天操天天拍 | 国产中文字幕在线 | 日韩在线免费小视频 | 中文字幕国产视频 | 国产一区二区免费看 | 国产精品99久久久久久久久久久久 | 国产韩国日本高清视频 | 伊人五月| 色在线国产 | 天堂av在线 | 天天插狠狠插 | 日本久久久亚洲精品 | 日韩国产精品毛片 | 精品欧美一区二区在线观看 | 日韩av播放在线 | 久久精品五月 | 国产精品21区 | 国产亚洲精品久久久久久大师 | 中文字幕免费一区 | 久久只精品99品免费久23小说 | 2021av在线 | 日本久久久精品视频 | 久久综合之合合综合久久 | 国产精品久久久久久99 | av免费在线播放 | 午夜美女福利 | 国产我不卡 | 91正在播放 | 国产日韩精品一区二区三区在线 | av电影免费看 | 人人澡澡人人 | 国产在线更新 | 国产精品入口麻豆 | 国产一区在线免费观看 | 色妞色视频一区二区三区四区 | 日韩理论在线播放 | 国产在线专区 | 精品久久久久久久久久久院品网 | 亚洲一二视频 | 天天干天天碰 | 久久免费成人 | 久久天天拍| 美女精品网站 | 91色欧美 | 国产一区 在线播放 | 天天操天天曰 | 国产在线视频一区 | 久久黄色免费 | 亚洲一区二区精品 | 国产高清久久 | 色综合中文综合网 | 在线观看免费视频你懂的 | 日韩视频专区 | 91精品对白一区国产伦 | 丰满少妇在线 | av久久在线 | 亚洲成av人片在线观看香蕉 | 97日日 | 瑞典xxxx性hd极品 | av在线超碰 | 91中文字幕在线观看 | av电影一区 | 国产精品久久久久免费a∨ 欧美一级性生活片 | 欧美日韩电影在线播放 | 在线播放国产一区二区三区 | 国产欧美精品一区二区三区四区 | 久久99久国产精品黄毛片入口 | 色久综合 | 草久在线播放 | 日日麻批40分钟视频免费观看 | 在线观看中文字幕av | 国产a精品 | 亚洲一区免费在线 | 欧美91在线| 91大神精品视频在线观看 | 日韩精品播放 | 97在线精品 | 久久99爱视频 | 深夜免费网站 | 大胆欧美gogo免费视频一二区 | 亚洲精品一区二区三区在线观看 | 99精品国产福利在线观看免费 | 狠狠干天天色 | 三级在线视频观看 | 精品国产乱码久久久久久浪潮 | 亚洲国产激情 | 亚洲国产操 | 人人爽人人澡人人添人人人人 | 国产亚洲精品久久网站 | 精品一区欧美 | 国产精品欧美久久 | aaa日本高清在线播放免费观看 | 国产一在线精品一区在线观看 | 91九色视频在线观看 | 国产 日韩 在线 亚洲 字幕 中文 | 久久久影片 | 日韩欧美一区二区在线播放 | 91粉色视频| 国产色黄网站 | 日韩国产欧美在线视频 | www免费黄色 | 久久综合九色综合97_ 久久久 | 激情婷婷av | 国产精品永久在线 | 香蕉久久久久久av成人 | 青青久视频 | 久热爱 | 91激情视频在线观看 | 2019av在线视频 | 91九色蝌蚪视频网站 | 日韩一级理论片 | 性色av免费看| 久草精品网| 麻豆国产视频下载 | 一区二区精品在线视频 | 国产探花视频在线播放 | 久久国产精品视频观看 | 亚洲日本三级 | 超碰97网站| 在线视频欧美日韩 | 国产精品18久久久久久久 | 天海翼一区二区三区免费 | 亚洲国产日韩一区 | 精品免费一区二区三区 | 在线播放视频一区 | 国产精品久久久久久久电影 | 久久神马影院 | 久久国产精品99久久久久久进口 | 999国内精品永久免费视频 | 超碰在线人人艹 | 日韩精品一区二区三区视频播放 | 日本论理电影 | 视频 国产区 | av电影亚洲 | www..com毛片| av网站免费线看精品 | 亚洲精品99 | 超碰在线94| 99热国产在线观看 | 伊人狠狠色丁香婷婷综合 | 国产精品久久久久久久婷婷 | 麻花豆传媒一二三产区 | 粉嫩一二三区 | 久久伊人精品一区二区三区 | 久久久久久国产精品亚洲78 | 亚洲电影成人 | www狠狠操 | 日韩欧美电影在线 | 亚洲激情一区二区三区 | 97超视频 | 日本中文字幕在线视频 | 免费福利片 | 午夜精品久久久久久久99 | 国产专区第一页 | 国产亚洲日 | 欧美污污视频 | 国产精品一级视频 | 久久精品中文字幕 | 国产精品二区在线 | 亚洲精品无 | 成人91在线 | 开心色激情网 | 色综合久久久久久中文网 | 日韩色中色 | 天天干,天天插 | 在线国产91 | 日韩 国产 | 国产精品第2页 | 日韩中文字幕免费视频 | 波多野结衣视频在线 | 国产不卡在线观看视频 | 特级毛片网| 久久区二区 | 亚洲a资源| 麻豆影视网站 | 国产999在线观看 | 日韩欧美电影在线 | 久久成电影 | 日韩精品一区二区三区免费视频观看 | 五月婷亚洲 | 色插综合| 911久久香蕉国产线看观看 | 91香蕉视频在线下载 | 国产又黄又爽又猛视频日本 | 久久精品国产亚洲aⅴ | 亚洲精品777 | 一区三区在线欧 | 亚洲精品色婷婷 | 国产高清在线一区 | 久久久久婷 | 免费亚洲视频在线观看 | 亚洲最新视频在线播放 | 曰韩精品 | 精品亚洲免费视频 | 午夜精品久久久久久久久久久久久久 | 97成人在线 | 成人精品国产免费网站 | 国产一级一级国产 | www.狠狠插.com | 中文字幕国产精品一区二区 | 天天性天天草 | 欧美日韩中文视频 | 91免费高清在线观看 | 国产精品一区二区视频 | 国色天香永久免费 | 久久综合九色综合久久久精品综合 | 国产精品女人久久久 | 操操日日 | 国产欧美三级 | 麻豆久久一区二区 | 中文字幕有码在线观看 | 国产69精品久久99不卡的观看体验 | 欧洲高潮三级做爰 | 成人免费观看网站 | 亚洲精品在 | 久草在线高清视频 | 丁香花中文在线免费观看 | 免费黄在线观看 | 极品久久久 | 精品亚洲va在线va天堂资源站 | 在线播放 一区 | 国产免费高清 | 色爽网站 | 成人午夜剧场在线观看 | 色婷婷国产 | 99精品国产99久久久久久福利 | 又污又黄网站 | 国内丰满少妇猛烈精品播放 | 中文字幕大全 | 97精品国产91久久久久久 | 中文字幕在线视频网站 | 亚洲精品在线一区二区三区 | 九九久久免费视频 | 日韩免费电影网 | 婷婷日日 | 不卡在线一区 | 日日夜夜精品视频天天综合网 | 中文字幕 国产 一区 | 日韩视频免费看 | 中文字幕亚洲高清 | 一级黄网 | 最近2019年日本中文免费字幕 | 免费日韩视| 黄色三级在线看 | 在线精品观看国产 | 午夜性生活 | 午夜精品一区二区三区在线播放 | 免费看的黄色网 | 国产精品一区二区三区免费视频 | 91色国产 | 丁香伊人网 | 国产在线不卡一区 | 国产丝袜网站 | 狠色狠色综合久久 | 高清国产午夜精品久久久久久 | 日韩欧美精品在线 | 国产日韩欧美视频在线观看 | 亚洲在线黄色 | 中文字幕在线观看网址 | 91资源在线免费观看 | 国产精品毛片久久久久久久 | 国内精品久久久久影院优 | 狠狠88综合久久久久综合网 | 国产成人精品久久 | 亚洲精品五月天 | 免费a视频 | 视频在线观看入口黄最新永久免费国产 | 超碰在线观看av.com | 日韩欧美在线观看一区二区 | 天堂av网在线 | 97人人澡人人添人人爽超碰 | 人人爽人人爽人人爽学生一级 | 一本一道久久a久久精品 | 欧美精品久久人人躁人人爽 | 中文字幕在线观看第二页 | 五月婷婷网站 | 黄免费在线观看 | 深爱激情五月综合 | 中文字幕在线视频第一页 | 精品99在线观看 | 久久精品免费观看 | 中文字幕乱码亚洲精品一区 | 国产传媒一区在线 | 69国产成人综合久久精品欧美 | 黄色一集片 | 一区二区视频在线观看免费 | 久久精品二区 | 中文字幕之中文字幕 | 天天摸天天干天天操天天射 | av 一区 二区 久久 | 欧美一进一出抽搐大尺度视频 | 免费精品视频在线 | 丁香婷婷综合激情五月色 | 欧美孕交vivoestv另类 | 欧美日韩高清不卡 | 亚洲最新视频在线 | 一区中文字幕电影 | 91高清一区| 国产黄色精品 | 在线免费视频你懂的 | 欧美一级网站 | 在线观看岛国片 | 天天搞天天干天天色 | 久久视频免费在线 | 天堂视频中文在线 | 99成人免费视频 | 玖玖爱国产在线 | 国产精品成人免费 | 黄色特级毛片 | 韩国av一区二区三区 | 少妇bbbb搡bbbb搡bbbb | 国产精品久久片 | 日韩理论在线观看 | 日韩欧美视频免费在线观看 | 亚洲精品视频在线观看免费视频 | 婷婷综合激情 | 亚洲天堂网在线播放 | 国产欧美精品一区二区三区 |