📄 libfat.c
字号:
lfnlast++; } /* LFNLAST must be the first entry in the lfn chain */ if ( lfnlast != 1 ) { return -1; // malformed lfn chain } } i++; count++; /* Checking cluster boundaries to see if we reached end of the cluster */ if ( (res = check_cluster_bound(V, Cluster, Offset)) != 0) { perror("fetch_next_direntry(): nothing left to read"); return -1; // Nothing left to read } if (lastclus != *Cluster) { D->off2 = byte_offset(V, *Cluster, *Offset); D->len1 = i; } D->direntoff = byte_offset(V, *Cluster, *Offset); if ( fetch_entry(V, Cluster, Offset, &((D->entry)[i])) < 0) { return -1; } if ( (res = analyze_dirent(&((D->entry)[i]))) < 0) { return -1; } } /* We are out of the 2 whiles. So we got an SFN. Or an error :) */ if ( ! (LIBFAT_DIRENT_ISSFN(res)) ) { return -1; // we got the error, whatever this is } else { i++; // Please note (note for me haha) that i increasings are correct. D->len = i; D->len2 = i - D->len1; /* Now we have to check beyond the sfn entry to see if anything else may be present or not. */ /* Checking cluster boundaries to see if we reached end of the cluster */ if ( (res = check_cluster_bound(V, Cluster, Offset)) != 0) { D->last = 1; } else { off64_t off; char buf; off = byte_offset(V, *Cluster, *Offset); if ( (res = lseek64(V->blkDevFd, off, SEEK_SET)) < 0 ) { perror("lseek() error in fetch_lfn():"); return -1; } /* Reading the byte after */ if ( (res = readn(V->blkDevFd, &buf, 1)) != 1) { fprintf(stderr,"readn() error in fetch_next_direntry() at %d",__LINE__); return -1; } if (buf != 0) { D->last = 0; } else { D->last = 1; } } return i; } return 0;}/* Check if all the lfn entries in the chain are in the correct order */int check_lfn_order(LfnEntry_t *Buffer, int bufsize) { BYTE andmask = 0x3F; // 00111111, to and with the lastlfnentry, who is or'ed with 01000000 // or 0x40 int i = 1; int limit = bufsize - 1; for (i=1; (limit - i) >=0; i++) { // we check only lfn entries. if ( ( Buffer[(limit - i)].LDIR_Ord & andmask ) != i ) return -1; // entries not in order } return 0;} /* check if all the lfn entries are related to the sfn in the buffer */int check_lfn_checksum(LfnEntry_t *Buffer, int bufsize) { int i; BYTE checksum; checksum = lfn_checksum(((DirEntry_t *) &Buffer[bufsize -1])->DIR_Name ); for (i=(bufsize -2); i>=0; i--) { if ( Buffer[i].LDIR_Chksum != checksum ) return -1; } /* Ok, all the lfn entries are related to the sfn entry in the buffer according to the checksum */ return 0;}/* Return the i-th unicode character from the lfn entry. (i from 0 to 12) */WORD fetch_lfn_char(LfnEntry_t *D, int n) { int i = (n % 13) ; // only 13 char in the lfn entries if ( i <= 4 ) return D->LDIR_Name1[i]; if ( i <= 10 ) return D->LDIR_Name2[i-5]; return D->LDIR_Name3[i-11];}/* Return the i-th unicode character from the lfn entry. (i from 0 to 12) */static int set_lfn_char(LfnEntry_t *D, WORD c , int n) { int i = (n % 13) ; // only 13 char in the lfn entries if (D==NULL) return -1; if ( i <= 4 ) { D->LDIR_Name1[i] = c; } else if ( i <= 10 ) { D->LDIR_Name2[i-5] = c; } else D->LDIR_Name3[i-11] = c; return 0;}/* Return the length of a long file name *including the terminator* in WORDs */int find_lfn_length( LfnEntry_t *D, int bufsize) { WORD buf; int size = (bufsize - 2) * 13; int i=0; if (bufsize <= 1) return -1; // only sfn present // it checks only the last lfn entry! for (i=0; i<13; i++) { buf = fetch_lfn_char(&(D[0]),i); if ( LFN_ISNULL(buf) ) return ( ( i + 1 ) + size); } /* i should be 13 now */ return ((i + 1) + size); //yes, we need space for the terminator as well}/* Extract the long file name from a lfnentry chain to a buffer of WORDS (UTF-16) */int extract_lfn_name( LfnEntry_t *Buffer, int bufsize, WORD *dest, int length) { int i; int entry; for (i=0; i < (length - 1); i++) { entry = (bufsize - 2) - (i / 13); // bufsize -1 is sfn entry /* if we got a terminator before the end --> Error */ dest[i] = fetch_lfn_char(&(Buffer[entry]) ,i); } // we add the terminator by ourselves dest[length - 1] = LFN_NULL; return 0;}/* Return the length of a short file name including the terminator in chars */int find_sfn_length( DirEntry_t *D, int bufsize) { // to fix: strip trailing spaces from filename and extension. int i; int count=0; if ( D[bufsize - 1].DIR_Name[0] == 0x20 ) return -1; // illegal first char for (i=0; i <8; i++) if (D[bufsize - 1].DIR_Name[i] != 0x20 ) count++; // there must be no lead 0 in the sfn extension. count++; if ( D[bufsize - 1].DIR_Name[8] == 0x20 ) return count; for (i=8; i <11; i++) if ( D[bufsize - 1].DIR_Name[i] != 0x20 ) count++; return ++count;}/* Extract the short file name from a sfn entry in a direntry block (in some kind of codepage)*//* return name length. including null terminator */int extract_sfn_name(DirEntry_t *D, int bufsize, char *name) { int i; int count = 0; if ( D[bufsize - 1].DIR_Name[0] == 0x20 ) return -1; // illegal first char for (i=0; i <8; i++) { if ( D[bufsize - 1].DIR_Name[i] != 0x20 ) { name[count] = D[bufsize - 1].DIR_Name[i]; count++; } } // there must be no lead 0 in the sfn extension. if ( D[bufsize - 1].DIR_Name[8] == 0x20 ) { name[count] = 0; return count; } name[count] = '.'; count ++; for (i=8; i <11; i++) { if ( D[bufsize - 1].DIR_Name[i] != 0x20 ) { name[count] = D[bufsize - 1].DIR_Name[i]; count++; } } name[count] = 0;// fprintf(stderr, "-- - - -- - -extract_sfn: name extracted: %s\n", name); return count;}/* Generates a dirent structure from fat directory entries. */int fatentry_to_dirent(Volume_t *V, DirEnt_t *D, struct dirent *dirp) { int res; LfnEntry_t *Buffer; int bufsize; WORD utf16buf[261]; char utf8buf[521]; int namelen; Buffer= D->entry; bufsize = D->len; memset(dirp, 0, sizeof(struct dirent)); // bzeroing the fields. memset(utf8buf,0,521); if ( bufsize < 2 ) { // we have only sfn namelen = find_sfn_length( (DirEntry_t *) Buffer, bufsize);// fprintf(stderr," fatentry to dirent: bufsize: %s\n\n", ((DirEntry_t *) Buffer)[0].DIR_Name ); if ( (namelen = res = extract_sfn_name( (DirEntry_t *) Buffer, bufsize, utf8buf)) <= 0) return res; memcpy(dirp->d_name, utf8buf, namelen); } else { // lfn namelen = find_lfn_length( Buffer, bufsize); if ( (res = extract_lfn_name( Buffer, bufsize, utf16buf, namelen)) != 0) return res; if ( (res = utf16to8(utf16buf, utf8buf)) != 0) return res; memcpy(dirp->d_name, utf8buf, 255); // copying at most 255 utf8 bytes. the last is already 0 } dirp->d_ino = get_fstclus(V, (DirEntry_t *) &(Buffer[bufsize -1]) ); if (ATTR_ISDIR(((DirEntry_t *) Buffer)[bufsize-1].DIR_Attr)) { // the direntry refers to a directory dirp->d_type = DT_DIR; } else { // the direntry refers to a file dirp->d_type = DT_REG; } dirp->d_reclen = sizeof(struct dirent); return 0;}/* find the direntry related to the given name in the given directory container specified by *cluster* and offset *//* it overwrites cluster and offset fields with the values of the direntry related to name in the current directory *//* container (the one before). On fail, it returns -1 and it doesnt overwrite anything. 0 on success */int find_direntry(Volume_t *V, char *name, DWORD *Cluster, DWORD *Offset) { int res; DWORD bkclu, bkoff; DirEnt_t D; LfnEntry_t *buffer = D.entry; //fprintf(stderr,"find_direntry. filename: %s\n",name); for (;;) { bkclu = *Cluster; bkoff = *Offset; res = fetch_next_direntry(V, &D, Cluster, Offset); if (res <= 0) return -1; // this is the condition to exit : nothing left to fetch or something strange found by fetch_next_direntry. /* checking if this is what we are lookin for */ if (res == 1 ) { // only sfn name is present. int i; char *entryname; i = find_sfn_length( (DirEntry_t *)buffer, res); entryname = alloca(i); extract_sfn_name((DirEntry_t *)buffer, res, entryname); res = utf8_stricmp(name, entryname); // possible because Ascii are utf8 if (res == 0) { // we won! good. *Cluster = bkclu; *Offset = bkoff; return 0; } } else { // we use lfn name. int i; WORD *entryname; char *utf8name; i = find_lfn_length( buffer, res); entryname = alloca(i * sizeof(WORD)); //is it Possible to use alloca()? utf8name = alloca(i * sizeof(WORD)); memset(utf8name, 0, i * sizeof(WORD)); memset(entryname, 0, i * sizeof(WORD)); extract_lfn_name(buffer, res, entryname, i); // res = size of buffer utf16to8(entryname, utf8name); //fprintf(stderr,"found 1 lfn direntry. name:%s\n",utf8name); res = utf8_stricmp(name, utf8name); if (res == 0) { // we won! good. *Cluster = bkclu; *Offset = bkoff; return 0; } } } return -2;}/* It takes a path of directories (it must exist in the volume V) and the Cluster of *//* the root dir. it return the cluster of the last dir in the path. *//* It returns 0 on success, -1 if the path does not exist. *//* Offset probably not needed */int traverse_path(Volume_t *V, gchar **parts, guint parts_len, DWORD *Cluster) { DWORD Offset = 0; DWORD Clus;// char nextdir[255]; int i,res; DirEnt_t D; LfnEntry_t *buffer = D.entry; if (V->FatType == FAT32) { Clus = EFD(V->Bpb.BPB_RootClus); } else { Clus = 1; } for (i=1; i < (int) (parts_len - 1); i++) { if ( (res = find_direntry(V, (char *) parts[i] , &Clus, &Offset)) != 0 ) { //looking for the directory return -1; //error: part of the path not found. } else { // parth of the path found: we have to fetch the related direntry DirEntry_t *sfnentry; if ( ( res = fetch_next_direntry(V, &D, &Clus, &Offset)) <= 0) { return -1 ; //fetching the dirent } sfnentry = (DirEntry_t *) &(buffer[res - 1]); if ( ! (ATTR_ISDIR(sfnentry->DIR_Attr))) { return -1; //name found but not directory } Offset = 0; Clus = get_fstclus(V,sfnentry); } } *Cluster = Clus; return 0;}/* find the file (direntry) related to the given path. N.B. The path must exist exactly in the volume V.*//* it sets adequately Cluster and Offset. the caller has to fetch the direntry of the file of interest */int find_file(Volume_t *V, const char *path, File_t *F, DWORD *Cluster, DWORD *Offset) { char *filename; int res; /* splitting the path */ gchar **parts = g_strsplit(path, "/", -1); guint parts_len = g_strv_length(parts); filename=(char *) parts[parts_len - 1]; /* Initialising Cluster and Offset with root directory values. Passed by the caller */ /* not needed! done in traverse_path() */ /* walking through the directory tree until we find the parent directory of our file/directory */ /* FIrst DIR = Rootdir */ /* We take one by one directories in the path, and look for them in the current dir. If it */ /* exists, we put DIR=that dir. If not throws an exception */ if ( (res=traverse_path(V, parts, parts_len, Cluster)) != 0) { g_strfreev(parts); return -1; } //error if (F!=NULL) { F->ParentFstClus = *Cluster; F->ParentOffset = 0; // not used atm. } *Offset = 0; res = find_direntry(V, filename , Cluster, Offset); //looking for the directory g_strfreev(parts); if (res != 0 ) { return -1; //error: part of the path not found. } else { // file found. Cluster and offset are related to its direntry. F->DirEntryClus = *Cluster; F->DirEntryOffset = *Offset; return 0; }}/* Read into the buffer data belonging to the file in which is the cluster Cluster. This is low level function. *//* it returns number of bytes read or -1 on error. It does not make any change to Cluster and Offset */int fat_read_data(Volume_t *V, DWORD *Cluster, DWORD *Offset, char *buf, size_t count ) { off64_t off = 0; int datasize = count; int dataread = 0; int clustersz= EFW(V->Bpb.BPB_BytsPerSec) * V->Bpb.BPB_SecPerClus; int byteleftperclus = clustersz - *Offset; int res; off64_t seekres; int i=0;// fprintf(stderr,"off: %u, bytleft %d, count: %d\n",*Offset, byteleftperclus,count); if (*Offset > clustersz) { fprintf(stderr,"Offset too big\n"); return -1; } else if (*Offset == clustersz) { DWORD c = *Cluster; *Offset = 0; fat_read_entry(V, c, 0, Cluster); // if this is the last cluster in the chain, the function will allocate a new cluster. if (fat_isfree(V,*Cluster)) { // must be eoc or a valid cluster number fprintf(stderr,"fat_write_data wrote on an unlinked cluster\n"); return -1; /* workaround to avoid to return an invalid *Cluster */ // lets check if wehave reached EOC. in this case this function allocate a free cluster to the file even if it does not need it. } else if (fat_iseoc(V,*Cluster)) { //we reached EOC. so we have to get a free cluster and link it to the file fprintf(stderr,"read_data error: EOC reached.\n"); return -1; } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -