| /* |
| * dirent.c |
| * |
| * Include functions implementing directory entry. |
| * implementation file. |
| * |
| * Copyright (C) knightray@gmail.com |
| * |
| * SPDX-License-Identifier: GPL-2.0+ |
| */ |
| #include "dirent.h" |
| #include "debug.h" |
| #include "fat.h" |
| #include "common.h" |
| #include "dir.h" |
| |
| #ifndef DBG_DIRENT |
| #undef DBG |
| #define DBG nulldbg |
| #endif |
| |
| /* Private routins declaration. */ |
| |
| static int32 |
| _get_dirent( |
| IN tdir_t * pdir, |
| OUT dir_entry_t * pdirent |
| ); |
| |
| static void |
| _parse_file_name( |
| IN tdir_t * pdir, |
| IN dir_entry_t * pdirent, |
| OUT tdir_entry_t * pdir_entry |
| ); |
| |
| static BOOL |
| _get_long_file_name( |
| IN dir_entry_t * pdirent, |
| OUT byte * long_name |
| ); |
| |
| static BOOL |
| _convert_short_fname( |
| IN ubyte * dir_name, |
| OUT byte * d_name |
| ); |
| |
| static BOOL |
| _convert_to_short_fname( |
| IN byte * fname, |
| OUT ubyte * short_fname |
| ); |
| |
| static ubyte |
| _get_check_sum( |
| IN ubyte * fname |
| ); |
| |
| /*----------------------------------------------------------------------------------------------------*/ |
| |
| int32 |
| dirent_find( |
| IN tdir_t * pdir, |
| IN byte * dirname, |
| OUT tdir_entry_t * pdir_entry) |
| { |
| int32 ret; |
| |
| pdir->cur_clus = pdir->start_clus; |
| pdir->cur_sec = 0; |
| pdir->cur_dir_entry = 0; |
| if (dir_read_sector(pdir) != DIR_OK) |
| return ERR_DIRENTRY_NOT_FOUND; |
| |
| ASSERT(pdir_entry->pdirent == NULL); |
| ret = DIRENTRY_OK; |
| |
| while(1) { |
| dir_entry_t dirent; |
| |
| if ((ret = _get_dirent(pdir, &dirent)) == DIRENTRY_OK) { |
| |
| if (dirent.dir_name[0] == 0x00) { |
| ret = ERR_DIRENTRY_NOT_FOUND; |
| break; |
| } |
| else if (dirent.dir_name[0] == 0xE5) { |
| //FixMe do with 0x5 |
| continue; |
| } |
| |
| Memset(pdir_entry->long_name, 0, LONG_NAME_LEN); |
| Memset(pdir_entry->short_name, 0, SHORT_NAME_LEN); |
| _parse_file_name(pdir, &dirent, pdir_entry); |
| |
| if (!Strcmp(pdir_entry->long_name, dirname)) { |
| break; |
| } |
| } |
| else if (ret == ERR_DIRENTRY_NOMORE_ENTRY){ |
| ret = ERR_DIRENTRY_NOT_FOUND; |
| break; |
| } |
| else { |
| ret = ERR_DIRENTRY_DEVICE_FAIL; |
| break; |
| } |
| } |
| |
| return ret; |
| } |
| |
| BOOL |
| dirent_is_empty( |
| IN tdir_t * pdir) |
| { |
| BOOL ret; |
| tdir_entry_t * pdir_entry; |
| |
| pdir->cur_clus = pdir->start_clus; |
| pdir->cur_sec = 0; |
| pdir->cur_dir_entry = 0; |
| if (dir_read_sector(pdir) != DIR_OK) |
| return ERR_DIRENTRY_NOT_FOUND; |
| |
| pdir_entry = dirent_malloc(); |
| ASSERT(pdir_entry->pdirent == NULL); |
| ret = TRUE; |
| |
| while(1) { |
| dir_entry_t dirent; |
| |
| if ((ret = _get_dirent(pdir, &dirent)) == DIRENTRY_OK) { |
| |
| if (dirent.dir_name[0] == 0x00) { |
| break; |
| } |
| else if (dirent.dir_name[0] == 0xE5) { |
| //FixMe do with 0x5 |
| continue; |
| } |
| |
| Memset(pdir_entry->long_name, 0, LONG_NAME_LEN); |
| Memset(pdir_entry->short_name, 0, SHORT_NAME_LEN); |
| _parse_file_name(pdir, &dirent, pdir_entry); |
| |
| if (!Strcmp(pdir_entry->long_name, ".") || !Strcmp(pdir_entry->long_name, "..")) { |
| continue; |
| } |
| else { |
| ret = FALSE; |
| break; |
| } |
| } |
| else if (ret == ERR_DIRENTRY_NOMORE_ENTRY){ |
| break; |
| } |
| else { |
| ERR("%s(): get next direntry failed. ret = %d\n", __FUNCTION__, ret); |
| ret = FALSE; |
| break; |
| } |
| } |
| |
| dirent_release(pdir_entry); |
| return ret; |
| } |
| |
| /* Marvell fixed: when a new cluster is being allocated to a directory |
| the contents of all dirent's should be 0 */ |
| static void |
| dirent_clear_new_cluster(tdir_t *pdir) |
| { |
| Memset(pdir->secbuf, 0, pdir->ptffs->pbs->byts_per_sec); |
| for (pdir->cur_sec = 0; |
| pdir->cur_sec < pdir->ptffs->pbs->sec_per_clus; |
| pdir->cur_sec++) |
| if (dir_write_sector(pdir) != DIR_OK) |
| break; |
| pdir->cur_sec = 0; |
| pdir->cur_dir_entry = 0; |
| /* Leave secbuf as is, so no need to read it again */ |
| } |
| |
| /* Marvell fixed: when a new entry is appended at the end of a directory, |
| all the entries should be allocated in the same sector, as they are later |
| accessed in the sector buffer. This function: |
| - checks if the required number of entries (dirent_num) is available; |
| - otherwise, pads the current sector with E5 (deleted) entries, |
| and lets the search continue into the following sector. */ |
| static int32 dirent_space_check_extend(tdir_t *pdir, int32 dirent_num) |
| { |
| int32 nent = pdir->ptffs->pbs->byts_per_sec / sizeof(dir_entry_t); |
| dir_entry_t *pde = (dir_entry_t *)pdir->secbuf; |
| int32 i, n0, ne5; |
| for (i = n0 = ne5 = 0; i < nent; i++) { |
| unsigned char c = pde[i].dir_name[0]; |
| if (n0) |
| ASSERT(c == 0); |
| if (c == 0) |
| n0++; |
| else if (c == 0xE5) |
| ne5++; |
| else |
| n0 = ne5 = 0; |
| if ((n0 + ne5) == dirent_num) { |
| pdir->cur_dir_entry = i + 1 - dirent_num; |
| return DIRENTRY_OK; |
| } |
| } |
| |
| /* not found the free entry sequence in this sector */ |
| if (n0) { |
| /* replace 0 entries with E5 as dir continues to next sector */ |
| for (i = nent - n0; i < nent; i++) |
| pde[i].dir_name[0] = 0xE5; |
| if (dir_write_sector(pdir) != DIR_OK) |
| return ERR_DIRENTRY_DEVICE_FAIL; |
| } |
| pdir->cur_dir_entry = nent; /* sector ended */ |
| return ERR_DIRENTRY_NOMORE_ENTRY; |
| } |
| |
| int32 |
| dirent_find_free_entry( |
| IN tdir_t * pdir, |
| IN tdir_entry_t * pdir_entry) |
| { |
| int32 ret; |
| tffs_t * ptffs = pdir->ptffs; |
| |
| ret = DIRENTRY_OK; |
| |
| while(1) { |
| dir_entry_t dirent; |
| |
| if ((ret = _get_dirent(pdir, &dirent)) == DIRENTRY_OK) { |
| /* Marvell fixed: revised search for free entries */ |
| if (dirent_space_check_extend(pdir, pdir_entry->dirent_num) == DIRENTRY_OK) |
| break; /* enough free entries in this sector */ |
| } |
| else if (ret == ERR_DIRENTRY_NOMORE_ENTRY) { |
| uint32 new_clus; |
| |
| if (dir_write_sector(pdir) != DIR_OK) { |
| ret = ERR_DIRENTRY_DEVICE_FAIL; |
| break; |
| } |
| if ((ret = fat_malloc_clus(ptffs->pfat, pdir->cur_clus, &new_clus)) == FAT_OK) { |
| pdir->cur_clus = new_clus; |
| pdir->cur_sec = 0; |
| pdir->cur_dir_entry = 0; |
| /* Marvell fixed: zero the new cluster */ |
| dirent_clear_new_cluster(pdir); |
| } |
| else { |
| ret = ERR_DIRENTRY_NOMORE_ENTRY; |
| break; |
| } |
| } |
| else { |
| break; |
| } |
| } |
| |
| return ret; |
| } |
| |
| int32 |
| dirent_get_next( |
| IN tdir_t * pdir, |
| OUT tdir_entry_t * pdir_entry) |
| { |
| int32 ret; |
| |
| ret = DIRENTRY_OK; |
| |
| while(1) { |
| dir_entry_t dirent; |
| |
| //print_sector(pdir->secbuf, 1); |
| DBG("%s():pdir->cur_clus = %d, pdir->cur_dir_entry = %d\n", __FUNCTION__, pdir->cur_clus, pdir->cur_dir_entry); |
| if ((ret = _get_dirent(pdir, &dirent)) == DIRENTRY_OK) { |
| |
| if (dirent.dir_name[0] == 0x00) { |
| ret = ERR_DIRENTRY_NOMORE_ENTRY; |
| break; |
| } |
| else if (dirent.dir_name[0] == 0xE5) { |
| //FixMe do with 0x5 |
| continue; |
| } |
| |
| Memset(pdir_entry->long_name, 0, LONG_NAME_LEN); |
| Memset(pdir_entry->short_name, 0, SHORT_NAME_LEN); |
| _parse_file_name(pdir, &dirent, pdir_entry); |
| |
| break; |
| } |
| else { |
| break; |
| } |
| } |
| return ret; |
| } |
| |
| void |
| dirent_release( |
| IN tdir_entry_t * pdir_entry) |
| { |
| Free(pdir_entry->pdirent); |
| Free(pdir_entry); |
| } |
| |
| tdir_entry_t * |
| dirent_malloc() |
| { |
| tdir_entry_t * pdir_entry; |
| |
| pdir_entry = (tdir_entry_t *)Malloc(sizeof(tdir_entry_t)); |
| Memset(pdir_entry, 0, sizeof(tdir_entry_t)); |
| return pdir_entry; |
| } |
| |
| BOOL |
| dirent_init( |
| IN byte * fname, |
| IN ubyte dir_attr, |
| IN byte use_long_name, |
| OUT tdir_entry_t * pdir_entry) |
| { |
| long_dir_entry_t * plfent; |
| dir_entry_t * pdirent; |
| uint32 lfent_num; |
| int32 lfent_i; |
| byte * pfname; |
| |
| if (Strlen(fname) > LONG_NAME_LEN || |
| (!use_long_name && Strlen(fname) > SHORT_NAME_LEN)) |
| return FALSE; |
| |
| if (use_long_name) { |
| lfent_num = Strlen(fname) / 13 + 2; |
| } |
| else { |
| lfent_num = 1; |
| } |
| |
| pfname = fname; |
| |
| plfent = (long_dir_entry_t *)Malloc(sizeof(long_dir_entry_t) * lfent_num); |
| Memset(plfent, 0, sizeof(long_dir_entry_t) * lfent_num); |
| pdirent = (dir_entry_t *)(&plfent[lfent_num - 1]); |
| |
| _convert_to_short_fname(fname, pdirent->dir_name); |
| DBG("%s(): %s=>%s\n", __FUNCTION__, fname, pdirent->dir_name); |
| pdirent->dir_attr = dir_attr; |
| pdirent->dir_ntres = 0; |
| pdirent->dir_crt_time_tenth = dirent_get_cur_time_tenth(); |
| pdirent->dir_crt_time = dirent_get_cur_time(); |
| pdirent->dir_crt_date = dirent_get_cur_date(); |
| pdirent->dir_lst_acc_date = pdirent->dir_crt_date; |
| pdirent->dir_wrt_time = pdirent->dir_crt_time; |
| pdirent->dir_wrt_date = pdirent->dir_crt_date; |
| pdirent->dir_fst_clus_hi = 0; |
| pdirent->dir_fst_clus_lo = 0; |
| pdirent->dir_file_size = 0; |
| |
| if (use_long_name) { |
| for (lfent_i = lfent_num - 2; lfent_i >= 0; lfent_i--) { |
| ubyte fname_line[13]; |
| |
| Memset(fname_line, 0xFF, 13); |
| Memcpy(fname_line, pfname, min(fname + Strlen(fname) - pfname + 1, 13)); |
| |
| if (lfent_i == 0) { |
| plfent[lfent_i].ldir_ord = (lfent_num - 1 - lfent_i) | LAST_LONG_ENTRY; |
| } |
| else { |
| plfent[lfent_i].ldir_ord = lfent_num - 1 - lfent_i; |
| } |
| copy_to_unicode(fname_line, 5, plfent[lfent_i].ldir_name1); |
| copy_to_unicode(fname_line + 5, 6, plfent[lfent_i].ldir_name2); |
| copy_to_unicode(fname_line + 11, 2, plfent[lfent_i].ldir_name3); |
| pfname += 13; |
| |
| plfent[lfent_i].ldir_attr = ATTR_LONG_NAME; |
| plfent[lfent_i].ldir_type = 0; |
| plfent[lfent_i].ldir_chksum = _get_check_sum(pdirent->dir_name); |
| plfent[lfent_i].ldir_fst_clus_lo = 0; |
| } |
| } |
| |
| pdir_entry->pdirent = (dir_entry_t *)plfent; |
| pdir_entry->dirent_num = lfent_num; |
| Strcpy(pdir_entry->long_name, fname); |
| _convert_short_fname(pdirent->dir_name, pdir_entry->short_name); |
| |
| return TRUE; |
| } |
| |
| uint16 |
| dirent_get_cur_time() |
| { |
| tffs_sys_time_t curtm; |
| uint16 ret; |
| |
| Getcurtime(&curtm); |
| |
| ret = 0; |
| ret |= (curtm.tm_sec >> 2) & 0x1F; |
| ret |= (curtm.tm_min << 5) & 0x7E0; |
| ret |= (curtm.tm_hour << 11) & 0xF800; |
| |
| return ret; |
| } |
| |
| uint16 |
| dirent_get_cur_date() |
| { |
| tffs_sys_time_t curtm; |
| uint16 ret; |
| |
| Getcurtime(&curtm); |
| |
| ret = 0; |
| ret |= (curtm.tm_mday) & 0x1F; |
| ret |= ((curtm.tm_mon + 1) << 5) & 0x1E0; |
| ret |= ((curtm.tm_year - 80) << 9) & 0xFE00; |
| |
| return ret; |
| } |
| |
| ubyte |
| dirent_get_cur_time_tenth() |
| { |
| tffs_sys_time_t curtm; |
| uint16 ret; |
| |
| Getcurtime(&curtm); |
| |
| ret = (curtm.tm_sec & 1) == 0 ? 0 : 100; |
| return ret; |
| } |
| |
| /*----------------------------------------------------------------------------------------------------*/ |
| |
| static int32 |
| _get_dirent( |
| IN tdir_t * pdir, |
| OUT dir_entry_t * pdirent) |
| { |
| int32 ret; |
| |
| ret = DIRENTRY_OK; |
| |
| if (pdir->cur_dir_entry < |
| (pdir->ptffs->pbs->byts_per_sec / sizeof(dir_entry_t))) { |
| Memcpy(pdirent, (dir_entry_t *)pdir->secbuf + pdir->cur_dir_entry, sizeof(dir_entry_t)); |
| pdir->cur_dir_entry++; |
| } |
| else { |
| if (fat_get_next_sec(pdir->ptffs->pfat, &pdir->cur_clus, &pdir->cur_sec)) { |
| pdir->cur_dir_entry = 0; |
| if ((ret = dir_read_sector(pdir)) == DIR_OK) { |
| Memcpy(pdirent, (dir_entry_t *)pdir->secbuf + pdir->cur_dir_entry, sizeof(dir_entry_t)); |
| pdir->cur_dir_entry++; |
| } |
| else { |
| ret = ERR_DIRENTRY_DEVICE_FAIL; |
| } |
| } |
| else { |
| ret = ERR_DIRENTRY_NOMORE_ENTRY; |
| } |
| } |
| |
| return ret; |
| } |
| |
| static void |
| _parse_file_name( |
| IN tdir_t * pdir, |
| IN dir_entry_t * pdirent, |
| OUT tdir_entry_t * pdir_entry) |
| { |
| int32 lf_entry_num; |
| |
| /* |
| * Marvell fixed: pdir_entry->pdirent is allocated below, while |
| * the function is called in a loop, e.g. in dirent_find(), so |
| * the previously allocated memory should be released to prevent |
| * a memory leak: with a lot of files in the working dir, searches |
| * exhausted the heap and following allocations failed. |
| */ |
| if (pdir_entry->pdirent) { |
| /* This is 0 after dirent_init() unless object re-used */ |
| Free(pdir_entry->pdirent); |
| pdir_entry->pdirent = NULL; |
| } |
| lf_entry_num = 0; |
| /* Marvell fixed: was "& ATTR_LONG_NAME", however one of the bits |
| is ATTR_VOLUME_ID, which alone indicates a volume id, short */ |
| if (pdirent->dir_attr == ATTR_LONG_NAME) { |
| uint32 lf_i; |
| dir_entry_t dirent; |
| |
| lf_entry_num = pdirent->dir_name[0] & ~(LAST_LONG_ENTRY); |
| pdir_entry->pdirent = (dir_entry_t *)Malloc((lf_entry_num + 1) * sizeof(dir_entry_t)); |
| |
| _get_long_file_name(pdirent, pdir_entry->long_name + (lf_entry_num - 1) * 13); |
| Memcpy(pdir_entry->pdirent, pdirent, sizeof(dir_entry_t)); |
| |
| for (lf_i = 1; lf_i < lf_entry_num; lf_i++) { |
| _get_dirent(pdir, &dirent); |
| Memcpy(pdir_entry->pdirent + lf_i, &dirent, sizeof(dir_entry_t)); |
| _get_long_file_name(&dirent, pdir_entry->long_name + (lf_entry_num - lf_i - 1) * 13); |
| } |
| |
| _get_dirent(pdir, &dirent); |
| Memcpy(pdir_entry->pdirent + lf_i, &dirent, sizeof(dir_entry_t)); |
| } |
| else { |
| pdir_entry->pdirent = (dir_entry_t *)Malloc(sizeof(dir_entry_t)); |
| |
| _convert_short_fname(pdirent->dir_name, pdir_entry->long_name); |
| Memcpy(pdir_entry->pdirent, pdirent, sizeof(dir_entry_t)); |
| } |
| |
| _convert_short_fname(pdirent->dir_name, pdir_entry->short_name); |
| pdir_entry->dirent_num = lf_entry_num + 1; |
| } |
| |
| static ubyte |
| _get_check_sum( |
| IN ubyte * fname) |
| { |
| int16 fname_len; |
| ubyte sum; |
| |
| sum = 0; |
| for (fname_len = 11; fname_len != 0; fname_len--) { |
| sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1) + *fname++; |
| } |
| return sum; |
| } |
| |
| static BOOL |
| _convert_to_short_fname( |
| IN byte * fname, |
| OUT ubyte * short_fname) |
| { |
| byte * pfname; |
| byte * pcur; |
| uint16 sf_i; |
| static uint16 num_tail = 0; |
| |
| for (sf_i = 0; sf_i < 11; sf_i++) |
| short_fname[sf_i] = ' '; |
| |
| if (!Strcmp(fname, ".") || !Strcmp(fname, "..")) { |
| /* Marvell fixed: names should not be 0-terminated */ |
| Memcpy((byte *)short_fname, fname, Strlen(fname)); |
| return TRUE; |
| } |
| |
| pfname = dup_string(fname); |
| pcur = pfname; |
| sf_i = 0; |
| |
| trip_blanks(pfname); |
| |
| while (sf_i < 8) { |
| if (*pcur == '\0' || *pcur == '.') { |
| break; |
| } |
| short_fname[sf_i++] = Toupper(*pcur++); |
| } |
| |
| if (*pcur == '.') { |
| pcur++; |
| } |
| else { |
| if (*pcur != '\0') { |
| byte str_tail[8]; |
| |
| while(*pcur && *pcur != '.') |
| pcur++; |
| |
| if (*pcur == '.') |
| pcur++; |
| |
| Sprintf(str_tail, "~%d", num_tail++); |
| Memcpy(short_fname + (8 - Strlen(str_tail)), str_tail, Strlen(str_tail)); |
| |
| if (*pcur == '\0') |
| goto _release; |
| } |
| } |
| |
| sf_i = 8; |
| |
| while (sf_i < 11) { |
| if (*pcur == '\0') |
| break; |
| short_fname[sf_i++] = Toupper(*pcur++); |
| } |
| |
| _release: |
| Free(pfname); |
| return TRUE; |
| } |
| |
| static BOOL |
| _get_long_file_name( |
| IN dir_entry_t * pdirent, |
| OUT byte * long_name) |
| { |
| long_dir_entry_t * pldirent; |
| |
| pldirent = (long_dir_entry_t *)pdirent; |
| |
| copy_from_unicode(pldirent->ldir_name1, 5, long_name); |
| copy_from_unicode(pldirent->ldir_name2, 6, long_name + 5); |
| copy_from_unicode(pldirent->ldir_name3, 2, long_name + 11); |
| return TRUE; |
| } |
| |
| static BOOL |
| _convert_short_fname( |
| IN ubyte * dir_name, |
| OUT byte * d_name) |
| { |
| uint32 i; |
| |
| Memset(d_name, 0, 11); |
| for (i = 0; i < 8; i++) { |
| if (dir_name[i] == ' ') |
| break; |
| d_name[i] = dir_name[i]; |
| } |
| |
| if (dir_name[8] != ' ') { |
| uint32 j; |
| |
| d_name[i++] = '.'; |
| for (j = 0; j < 3; j++) { |
| if (dir_name[8 + j] == ' ') |
| break; |
| d_name[i + j] = dir_name[8 + j]; |
| } |
| } |
| return TRUE; |
| } |
| |