blob: 07c5ca9b1c911e629c8f879f82deac7c8e107f21 [file] [log] [blame]
/*
* 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;
}