Skip to content

f_readdir 在使用长文件名时的问题

结论先行

FRESULT f_readdir (DIR* dp, FILINFO* fno)

在使用了长文件名后,在操作f_readdir前,需要初始化fno.lfsize

    FILINFO info;
    TCHAR name[64];
    int res;
    DIR file_dir;

    info.lfname = name;
    info.lfsize = 64;  //init lfsize

    f_opendir(&file_dir, PICTURE_FILE_DIR);
    f_readdir(&file_dir, &info);

分析过程

在使能了长文件名后,通过f_readdir遍历目录下的文件,发现获取到的lfname为空,代码如下

[DBG]-[NORMAL]\USER\main.c\list_for_each_pic\read dir, res=0, fname=PIC201~1.BMP, lfname=
[DBG]-[NORMAL]\USER\main.c\list_for_each_pic\read dir, res=0, fname=PIC201~2.BMP, lfname=
[DBG]-[NORMAL]\USER\main.c\list_for_each_pic\read dir, res=0, fname=PIC201~3.BMP, lfname=
static void list_for_each_pic(char *debug)
{
    FILINFO info;
    TCHAR name[64];
    int res;
    DIR file_dir;

    info.lfname = name;

    DBG_TO_SERIAL(DBG_NORMAL, "%s", debug);

    res = f_opendir(&file_dir, PICTURE_FILE_DIR);
    if (res) {
        DBG_TO_SERIAL(DBG_NORMAL, "can not open dir:%s, res=%d", PICTURE_FILE_DIR, res);
        my_msg.sf_lock = 0;
        return;
    }

    /* read picture file */
    while (1) {
        res = f_readdir(&file_dir, &info);
        DBG_TO_SERIAL(DBG_NORMAL, "read dir, res=%d, fname=%s, lfname=%s", res, info.fname, info.lfname);
        if (res || info.fname[0] == 0)
            break;
        if (info.fname[0] == '.')
            continue;
    }
    f_closedir(&file_dir);
}

追踪fatfs代码,发现有如下机制

if (dp->sect && fno->lfsize && dp->lfn_idx != 0xFFFF) { /* Get LFN if available */

通过添加打印,发现出现获取不到lfname时的lfzise为0

/*-----------------------------------------------------------------------*/
/* Get file information from directory entry                             */
/*-----------------------------------------------------------------------*/
#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2
static
void get_fileinfo (     /* No return code */
    DIR* dp,        /* Pointer to the directory object */
    FILINFO* fno        /* Pointer to the file information to be filled */
)
{
    UINT i;
    TCHAR *p, c;
    BYTE *dir;
#if _USE_LFN
    WCHAR w, *lfn;
#endif

    p = fno->fname;
    if (dp->sect) {     /* Get SFN */
        dir = dp->dir;
        i = 0;
        while (i < 11) {        /* Copy name body and extension */
            c = (TCHAR)dir[i++];
            if (c == ' ') continue;         /* Skip padding spaces */
            if (c == RDDEM) c = (TCHAR)DDEM;    /* Restore replaced DDEM character */
            if (i == 9) *p++ = '.';         /* Insert a . if extension is exist */
#if _USE_LFN
            if (IsUpper(c) && (dir[DIR_NTres] & (i >= 9 ? NS_EXT : NS_BODY)))
                c += 0x20;          /* To lower */
#if _LFN_UNICODE
        if (IsDBCS1(c) && i != 8 && i != 11 && IsDBCS2(dir[i]))
            c = c << 8 | dir[i++];
        c = ff_convert(c, 1);   /* OEM -> Unicode */
        if (!c) c = '?';
#endif
#endif
            *p++ = c;
        }
        fno->fattrib = dir[DIR_Attr];               /* Attribute */
        fno->fsize = LD_DWORD(dir + DIR_FileSize);  /* Size */
        fno->fdate = LD_WORD(dir + DIR_WrtDate);    /* Date */
        fno->ftime = LD_WORD(dir + DIR_WrtTime);    /* Time */
    }
    *p = 0;     /* Terminate SFN string by a \0 */

#if _USE_LFN
    if (fno->lfname) {
        i = 0; p = fno->lfname;
        if (dp->sect && fno->lfsize && dp->lfn_idx != 0xFFFF) { /* Get LFN if available */
            lfn = dp->lfn;
            while ((w = *lfn++) != 0) {     /* Get an LFN character */
#if !_LFN_UNICODE
                w = ff_convert(w, 0);       /* Unicode -> OEM */
                if (!w) { i = 0; break; }   /* No LFN if it could not be converted */
                if (_DF1S && w >= 0x100)    /* Put 1st byte if it is a DBC (always false on SBCS cfg) */
                    p[i++] = (TCHAR)(w >> 8);
#endif
                if (i >= fno->lfsize - 1) { i = 0; break; } /* No LFN if buffer overflow */
                p[i++] = (TCHAR)w;
            }
        }
        p[i] = 0;   /* Terminate LFN string by a \0 */
    }
#endif
}
#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */



/*-----------------------------------------------------------------------*/
/* Read Directory Entries in Sequence                                    */
/*-----------------------------------------------------------------------*/

FRESULT f_readdir (
    DIR* dp,            /* Pointer to the open directory object */
    FILINFO* fno        /* Pointer to file information to return */
)
{
    FRESULT res;
    DEFINE_NAMEBUF;


    res = validate(dp);                     /* Check validity of the object */
    if (res == FR_OK) {
        if (!fno) {
            res = dir_sdi(dp, 0);           /* Rewind the directory object */
        } else {
            INIT_BUF(*dp);
            res = dir_read(dp, 0);          /* Read an item */
            if (res == FR_NO_FILE) {        /* Reached end of directory */
                dp->sect = 0;
                res = FR_OK;
            }
            if (res == FR_OK) {             /* A valid entry is found */
                get_fileinfo(dp, fno);      /* Get the object information */
                res = dir_next(dp, 0);      /* Increment index for next */
                if (res == FR_NO_FILE) {
                    dp->sect = 0;
                    res = FR_OK;
                }
            }
            FREE_BUF();
        }
    }

    LEAVE_FF(dp->fs, res);
}

对lfsize进行搜索,看在什么位置进行的赋值

Ff.c (\fat_fs\src): if (fno->lfname && fno->lfsize) {
Ff.c (\fat_fs\src):             if (i >= fno->lfsize - 1) { i = 0; break; } /* Buffer overflow, no LFN */
Ff.c (\fat_fs\src):         fno.lfsize = i;
Ff.h (\fat_fs\inc): UINT    lfsize;         /* Size of LFN buffer in TCHAR */

发现其实没有任何位置对此变量进行赋值

#if _FS_RPATH >= 2
FRESULT f_getcwd (
    TCHAR *path,    /* Pointer to the directory path */
    UINT sz_path    /* Size of path */
)
{
    FRESULT res;
    DIR dj;
    UINT i, n;
    DWORD ccl;
    TCHAR *tp;
    FILINFO fno;
    DEF_NAMEBUF;


    *path = 0;
    res = chk_mounted((const TCHAR**)&path, &dj.fs, 0); /* Get current volume */
    if (res == FR_OK) {
        INIT_BUF(dj);
        i = sz_path;        /* Bottom of buffer (dir stack base) */
        dj.sclust = dj.fs->cdir;            /* Start to follow upper dir from current dir */
        while ((ccl = dj.sclust) != 0) {    /* Repeat while current dir is a sub-dir */
            res = dir_sdi(&dj, 1);          /* Get parent dir */
            if (res != FR_OK) break;
            res = dir_read(&dj);
            if (res != FR_OK) break;
            dj.sclust = LD_CLUST(dj.dir);   /* Goto parent dir */
            res = dir_sdi(&dj, 0);
            if (res != FR_OK) break;
            do {                            /* Find the entry links to the child dir */
                res = dir_read(&dj);
                if (res != FR_OK) break;
                if (ccl == LD_CLUST(dj.dir)) break; /* Found the entry */
                res = dir_next(&dj, 0); 
            } while (res == FR_OK);
            if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */
            if (res != FR_OK) break;
#if _USE_LFN
            fno.lfname = path;
            fno.lfsize = i;
#endif
            get_fileinfo(&dj, &fno);        /* Get the dir name and push it to the buffer */
            tp = fno.fname;
            if (_USE_LFN && *path) tp = path;
            for (n = 0; tp[n]; n++) ;
            if (i < n + 3) {
                res = FR_NOT_ENOUGH_CORE; break;
            }
            while (n) path[--i] = tp[--n];
            path[--i] = '/';
        }
        tp = path;
        if (res == FR_OK) {
            *tp++ = '0' + CurrVol;          /* Put drive number */
            *tp++ = ':';
            if (i == sz_path) {             /* Root-dir */
                *tp++ = '/';
            } else {                        /* Sub-dir */
                do      /* Add stacked path str */
                    *tp++ = path[i++];
                while (i < sz_path);
            }
        }
        *tp = 0;
        FREE_BUF();
    }

    LEAVE_FF(dj.fs, res);
}
#endif /* _FS_RPATH >= 2 */
#endif /* _FS_RPATH >= 1 */

结论

// lfsize仅有一个作用,就是用来限制i,防止内存越界,lfsize需要在使用f_readdir之前自己手动赋值

#if _USE_LFN
    if (fno->lfname) {
        i = 0; p = fno->lfname;
        if (dp->sect && fno->lfsize && dp->lfn_idx != 0xFFFF) { /* Get LFN if available */
            lfn = dp->lfn;
            while ((w = *lfn++) != 0) {     /* Get an LFN character */
#if !_LFN_UNICODE
                w = ff_convert(w, 0);       /* Unicode -> OEM */
                if (!w) { i = 0; break; }   /* No LFN if it could not be converted */
                if (_DF1S && w >= 0x100)    /* Put 1st byte if it is a DBC (always false on SBCS cfg) */
                    p[i++] = (TCHAR)(w >> 8);
#endif
                if (i >= fno->lfsize - 1) { i = 0; break; } /* No LFN if buffer overflow */
                p[i++] = (TCHAR)w;
            }
        }
        p[i] = 0;   /* Terminate LFN string by a \0 */
    }
#endif