c++embeddedfatfs

How do I ensure that the read index of the directory object is rewound in FATFS?


I'm guessing this is a basic question: I'm using the f_readdir function in FatFs to read the contents of a directory from a command line, but I'm having trouble getting the function to run multiple times. It works fine the first go around: I type "directory" into the CLI and it displays every file in the directory on line at time. Asking it to repeat the f_readdir operation again, however, (i.e. by typing in the "directory" command again, after the first one has successfully completed) outputs nothing.

I believe this is because the file read object isn't being rewound back to zero at the end of f_readdir operation, and subsequent requests to read the directory are starting at a portion of the index that doesn't exist. That's the best explanation I can see at this point, at least. It says on Elm Chan's FatFs website that "When all directory items have been read and no item to read, a null string is stored into the fno->fname[] without any error. When a null pointer is given to the fno, the read index of the directory object is rewinded."

Here's my code. Ignore the function arguments, they're RTOS stuff to run the command:

void Cmd_directory::run(XString param, XRTOS::XCLI::userIO* io){
        DIR dj;         /*[IN] Directory search object */
        FILINFO fno;    /*[OUT] File information structure */
        FRESULT res;

        res = f_opendir(&dj, "0:");

        while (res == FR_OK && fno.fname[0]) {
                res = f_readdir(&dj, &fno);
                io->print((const char*)fno.fname);
                io->print("\r\n");
        }

        f_closedir(&dj);
}

This while loop was something I found on the web, so unfortunately I don't fully understand how the fname index works, as many times as I've read the detailed explanation. Perhaps it's not realizing it's hitting the end of the directory based on the conditions of my while loop, though it certainly completes and closes the directory successfully. When I run the function again, I can see that the fno object still has the file information stored in it from the previous go around.

Things I've tried (and at this point it's worth adding I'm quite new to the world of programming):

&fno = nullptr;   //produces "error: lvalue required as left operand of assignment"
FILINFO *p = nullptr;  //produces all sorts of errors
fno = p;
 //these were shots in the dark
fno.fname[0] = 0;      
memset(fno.fname, 0, sizeof(fno.fname));

I imagine this is some basic stuff that I'm juts not getting, though forgive me if I'm way off on this. I don't have access to a real programmer IRL unfortunately so I'm forced to poll the community.

Oh right, and I'm using the Eclipse IDE, GNU ARM Build tools with an STM32L.


Solution

  • Here:

        while (res == FR_OK && fno.fname[0]) {
    

    fno.fname[0] is unitialised - you get lucky the first time as it contains non-zero junk. The second time it likely contains whatever the function previously left in it - i.e. the NUL from the previous call - which terminates the loop immediately.

    The following should work:

        res = f_readdir( &dj, &fno ) ;
        while( res == FR_OK && fno.fname[0] ) 
        {
            io->print((const char*)fno.fname);
            io->print("\r\n");
            res = f_readdir(&dj, &fno);
        } 
    

    From your previous attempts fno.fname[0] = 0; was close, if only you'd not explicitly set to the loop termination value! The following minor change to your code should work too:

    fno.fname[0] = 1;      
    while (res == FR_OK && fno.fname[0]) {
            res = f_readdir(&dj, &fno);
            io->print((const char*)fno.fname);
            io->print("\r\n");
    }
    

    but it will print a blank line if the directory is empty.

    To be honest the semantics of the ELM FatFs f_readdir() are somewhat odd. You might consider a wrapper to give it a more consistent and conventional interface:

    FRESULT readdir( DIR* dp,      /* [IN] Directory object */
                     FILINFO* fno  /* [OUT] File information structure */ )
    {
        FRESULT res = f_readdir( dp, fno ) ;
        if( res == FR_OK && fno->fname[0] == 0 ) 
        {
            res = FR_NO_FILE ;
        }
    
        return res ;
    }
    

    Then you can write (for example):

    while( readdir( &dj, &fno ) == FR_OK )
    {
        io->print((const char*)fno.fname);
        io->print("\r\n");
    }