1 /*-----------------------------------------------------------------------*/
2 /*Seek File R/W Pointer*/
3 /*-----------------------------------------------------------------------*/
4
5 FRESULT f_lseek (6 FIL *fp, /*Pointer to the file object*/
7 DWORD ofs /*File pointer from top of file*/
8 )9 {10 FRESULT res;11
12
13 res = validate(fp->fs, fp->id); /*Check validity of the object*/
14 if (res != FR_OK) LEAVE_FF(fp->fs, res);15 if (fp->flag & FA__ERROR) /*Check abort flag*/
16 LEAVE_FF(fp->fs, FR_INT_ERR);17
18 #if _USE_FASTSEEK
19 if (fp->cltbl) { /*Fast seek*/
20 DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl;21
22 if (ofs == CREATE_LINKMAP) { /*Create CLMT*/
23 tbl = fp->cltbl;24 tlen = *tbl++; ulen = 2; /*Given table size and required table size*/
25 cl = fp->sclust; /*Top of the chain*/
26 if(cl) {27 do{28 /*Get a fragment*/
29 tcl = cl; ncl = 0; ulen += 2; /*Top, length and used items*/
30 do{31 pcl = cl; ncl++;32 cl = get_fat(fp->fs, cl);33 if (cl <= 1) ABORT(fp->fs, FR_INT_ERR);34 if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);35 } while (cl == pcl + 1);36 if (ulen <= tlen) { /*Store the length and top of the fragment*/
37 *tbl++ = ncl; *tbl++ =tcl;38 }39 } while (cl < fp->fs->n_fatent); /*Repeat until end of chain*/
40 }41 *fp->cltbl = ulen; /*Number of items used*/
42 if (ulen <=tlen)43 *tbl = 0; /*Terminate table*/
44 else
45 res = FR_NOT_ENOUGH_CORE; /*Given table size is smaller than required*/
46
47 } else { /*Fast seek*/
48 if (ofs > fp->fsize) /*Clip offset at the file size*/
49 ofs = fp->fsize;50 fp->fptr = ofs; /*Set file pointer*/
51 if(ofs) {52 fp->clust = clmt_clust(fp, ofs - 1);53 dsc = clust2sect(fp->fs, fp->clust);54 if (!dsc) ABORT(fp->fs, FR_INT_ERR);55 dsc += (ofs - 1) / SS(fp->fs) & (fp->fs->csize - 1);56 if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) { /*Refill sector cache if needed*/
57 #if !_FS_TINY
58 #if !_FS_READONLY
59 if (fp->flag & FA__DIRTY) { /*Write-back dirty sector cache*/
60 if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) !=RES_OK)61 ABORT(fp->fs, FR_DISK_ERR);62 fp->flag &= ~FA__DIRTY;63 }64 #endif
65 if (disk_read(fp->fs->drv, fp->buf, dsc, 1) != RES_OK) /*Load current sector*/
66 ABORT(fp->fs, FR_DISK_ERR);67 #endif
68 fp->dsect =dsc;69 }70 }71 }72 } else
73 #endif
74
75 /*Normal Seek*/
76 {77 DWORD clst, bcs, nsect, ifptr;78
79 if (ofs > fp->fsize /*In read-only mode, clip offset with the file size*/
80 #if !_FS_READONLY
81 && !(fp->flag &FA_WRITE)82 #endif
83 ) ofs = fp->fsize;84
85 ifptr = fp->fptr;86 fp->fptr = nsect = 0;87 if(ofs) {88 bcs = (DWORD)fp->fs->csize * SS(fp->fs); /*Cluster size (byte)*/
89 if (ifptr > 0 &&
90 (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /*When seek to same or following cluster,*/
91 fp->fptr = (ifptr - 1) & ~(bcs - 1); /*start from the current cluster*/
92 ofs -= fp->fptr;93 clst = fp->clust;94 } else { /*When seek to back cluster,*/
95 clst = fp->sclust; /*start from the first cluster*/
96 #if !_FS_READONLY
97 if (clst == 0) { /*If no cluster chain, create a new chain*/
98 clst = create_chain(fp->fs, 0);99 if (clst == 1) ABORT(fp->fs, FR_INT_ERR);100 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);101 fp->sclust =clst;102 }103 #endif
104 fp->clust =clst;105 }106 if (clst != 0) {107 while (ofs > bcs) { /*Cluster following loop*/
108 #if !_FS_READONLY
109 if (fp->flag & FA_WRITE) { /*Check if in write mode or not*/
110 clst = create_chain(fp->fs, clst); /*Force stretch if in write mode*/
111 if (clst == 0) { /*When disk gets full, clip file size*/
112 ofs = bcs; break;113 }114 } else
115 #endif
116 clst = get_fat(fp->fs, clst); /*Follow cluster chain if not in write mode*/
117 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);118 if (clst <= 1 || clst >= fp->fs->n_fatent) ABORT(fp->fs, FR_INT_ERR);119 fp->clust =clst;120 fp->fptr +=bcs;121 ofs -=bcs;122 }123 fp->fptr +=ofs;124 if (ofs % SS(fp->fs)) {125 nsect = clust2sect(fp->fs, clst); /*Current sector*/
126 if (!nsect) ABORT(fp->fs, FR_INT_ERR);127 nsect += ofs / SS(fp->fs);128 }129 }130 }131 if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) { /*Fill sector cache if needed*/
132 #if !_FS_TINY
133 #if !_FS_READONLY
134 if (fp->flag & FA__DIRTY) { /*Write-back dirty sector cache*/
135 if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) !=RES_OK)136 ABORT(fp->fs, FR_DISK_ERR);137 fp->flag &= ~FA__DIRTY;138 }139 #endif
140 if (disk_read(fp->fs->drv, fp->buf, nsect, 1) != RES_OK) /*Fill sector cache*/
141 ABORT(fp->fs, FR_DISK_ERR);142 #endif
143 fp->dsect =nsect;144 }145 #if !_FS_READONLY
146 if (fp->fptr > fp->fsize) { /*Set file change flag if the file size is extended*/
147 fp->fsize = fp->fptr;148 fp->flag |=FA__WRITTEN;149 }150 #endif
151 }152
153 LEAVE_FF(fp->fs, res);154 }