Find the oldest file within a directory in C on Windows - c

I am working on a C project and I am trying to find the oldest file within a directory so that that once the oldest file has been found, it is then deleted. I can not find anything on how to do this in C using windows, have found ways to do it in Linux but I need a version for Windows.

Basically you scan the directory, same as in Linux (but you could check out the Boost library also).
The data about time and date are already available in the directory scan structure
HANDLE fh;
FILETIME oldest = {-1U, -1U};
// Buffer to hold file name
oldestFile = malloc(MAX_PATH);
fd = malloc(sizeof(WIN32_FIND_DATA));
if (INVALID_HANDLE_VALUE == (fh = FindFirstFile(directory_name, fd)))
// Signal error, free memory, (and return an error code?)
// OK to proceed
do
{
if(fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
continue;
//
if ((fd->ftCreationTime.dwHighDateTime < oldest.dwHighDateTime)
|| (fd->ftCreationTime.dwHighDateTime == oldest.dwHighDateTime
&& fd->ftCreationTime.dwLowDateTime < oldest.dwLowDateTime))
{
oldest.dwHighDateTime = fd->ftCreationTime.dwHighDateTime; // ftLastAccessTime? ftLastWriteTime?
oldest.dwLowDateTime = fd ->ft CreationTime.dwLowDateTime;
strncpy(oldestFile, MAX_PATH, fd->cFileName);
}
} while(FindNextFile(fh, fd));
FindClose(fh);
free(fd); fd = NULL;

You'll want to use the FindFirstFile/FindNextFile combination on Windows to get the files in the directory. You can then either use stat as you would in Linux, or GetFileAttributesEx to check the dates.

Since windows is POSIX compliant, you should be able to read a directory and do a stat() on the files.

You could use the GetFileAttributesEx() function which populates a WIN32_FILE_ATTRIBUTE_DATA struct which has three time related members:
ftCreationTime
ftLastAccessTime
ftLastWriteTime
You can compare whichever of these is more relevant and keep track of the oldest file found during iteration. Once iteration is over, delete it using DeleteFile(). The time members are of type FILETIME and can be compared using CompareFileTime().
Or use the GetFileTime() to obtain the relevant time attribute, as commented by BeyondSora.

For finding the details of file in windows you'll have to refer to File Allocation Table which includes all the details about the files.
Check here for the coding part to read FAT

Related

readdir is returning lots of all-0xFF 8.3 names from fat32 filesystem

I'm working on an ESP32 platform with IDF 4.1, using code like this:
struct dirent * dirent;
while((dirent = readdir(dir)) != nullptr) {
ESP_LOGI("ConfigServer", "Found %s, id %d, type %d", dirent->d_name, dirent->d_ino, dirent->d_type);
if(dirent->d_name[0] == '\377') {
++invalid_ctr;
} else {
// do something with the file info
}
}
closedir(dir);
I had to add the bit where invalid_ctr is incremented, because I started getting loads of iterations where dirent->d_name was "\377\377\377\377\377\377\377\377.\377\377\377" (rendered as inverse-video "?" characters in my terminal). The code not shown involves feeding that name to stat(), which would return the same values as the last valid file encountered. The log entry would look like this:
I (608261) ConfigServer: Found ��������.���, id 0, type 2
Type 2 represents a directory. This is happening on a partition on the onboard flash, formatted by the IDF library's "format if mount failed" option at mount. So perhaps my assumption of FAT32 is invalid. I do know that IDF uses FatFs internally.
Is this indicative of an error on the filesystem? Is it expected to need to filter out such trash on a typical iteration with readdir()?
The FAT component in ESP IDF has support for long file names disabled by default. Run idf.py menuconfig, then "Component config → FAT Filesystem support → Long filename support" to enable it.

External file ressource on embedded system (C language with FAT)

My application/device is running on an ARM Cortex M3 (STM32), without OS but with a FatFs) and needs to access many resources files (audio, image, etc..)
The code runs from internal flash (ROM, 256Kb).
The resources files are stored on external flash (SD card, 4Gb).
There is not much RAM (32Kb), so malloc a complete file from package is not an option.
As the user has access to the resources folder for atomic update, I would like to package all theses resources files in a single (.dat, .rom, .whatever)
So the user doesn't mishandle theses data.
Can someone point me to a nice solution to do so?
I don't mind remapping fopen, fread, fseek and fclose in my application, but I would not like starting from scratch (coding the serializer, table of content, parser, etc...). My system is quite limited (no malloc, no framework, just stdlib and FatFs)
Thanks for any input you can give me.
note: I'm not looking for a solution where the resources are embedded IN the code (ROM) as obviously they are way too big for that.
It should be possible to use fatfs recursively.
Drive 0 would be your real device, and drive 1 would be a file on drive 0. You can implement the disk_* functions like this
#define BLOCKSIZE 512
FIL imagefile;
DSTATUS disk_initialize(BYTE drv) {
UINT r;
if(drv == 0)
return SD_initialize();
else if(drv == 1) {
r = f_open(&image, "0:/RESOURCE.DAT", FA_READ);
if(r == FR_OK)
return 0;
}
return STA_NOINIT;
}
DRESULT disk_read(BYTE drv, BYTE *buff, DWORD sector, DWORD count) {
UINT br, r;
if(drv == 0)
return SD_read_blocks(buff, sector, count);
else if(drv == 1) {
r = f_seek(&imagefile, sector*BLOCKSIZE);
if(r != FR_OK)
return RES_ERROR;
r = f_read(&imagefile, buff, count*BLOCKSIZE, &br);
if((r == FR_OK) && (br == count*BLOCKSIZE))
return RES_OK;
}
return RES_ERROR;
}
To create the filesystem image on Linux or other similar systems you'd need mkfs.msdos and the mtools package. See this SO post on how to do it. Might work on Windows with Cygwin, too.
To expand on what Joachim said above:
Popular choices of uncompressed (sometimes) archive formats are cpio, tar, and zip. Any of the 3 would work just fine.
Here are a few more in-depth comments on using TAR or CPIO.
TAR
I've used tar before for the exact purpose, on an stm32 with FatFS, so can tell you it works. I chose it over cpio or zip because of its familiarity (most developers have seen it), ease of use, and rich command line tools.
GNU Tar gives you fine-grained control over order in which the files are placed in the archive and regexes to manipulate file names (--xform) or --exclude paths. You can pretty much guarantee you can get exactly the archive you're after with nothing more than GNU Tar and a makefile. I'm not sure the same can be said for cpio or zip.
This means it worked well for my build environment, but your requirements may vary.
CPIO
The cpio has a much worse/harder to use set of command line tools than tar in my opinion. Which is why I steer clear of it when I can. However, its file format is a little lighter-weight and might be even simpler to parse (not that tar is hard).
The Linux kernel project uses cpio for initramfs images, so that's probably the best / most mature example on the internet that you'll find on using it for this sort of purpose.
If you grab any kernel source tree, the tool usr/gen_init_cpio.c can used to generate a cpio from a cpio listing file format described in that source file.
The extraction code is in init/initramfs.c.
ZIP
I've never used the zip format for this sort of purpose. So no real comment there.
Berendi found a very clever solution: use the existing fat library to access it recursively!
The implementation is quite simple, and after extensive testing, I'd like to post the code to use FatFs recursively and the commands used for single file fat generation.
First, lets generate a 100Mb FAT32 file:
dd if=/dev/zero of=fat.fs bs=1024 count=102400
mkfs.vfat -F 32 -r 112 -S 512 -v fatfile.fs
Create/push content into it:
echo HelloWorld on Virtual FAT >> helloworld.txt
mcopy -i fatfile.fs helloworld.txt ::/
Change the diskio.c file, to add Berendi's code but also:
DSTATUS disk_status ()
{
DSTATUS status = STA_NOINIT;
switch (pdrv)
{
case FATFS_DRIVE_VIRTUAL:
printf("disk_status: FATFS_DRIVE_VIRTUAL\r\n" );
case FATFS_DRIVE_ATA: /* SD CARD */
status = FATFS_SD_SDIO_disk_status();
}
}
Dont forget to add the enum for the drive name, and the number of volumes:
#define _VOLUMES 2
Then mount the virtual FAT, and access it:
f_mount(&VirtualFAT, (TCHAR const*)"1:/", 1);
f_open(&file, "1:/test.txt", FA_READ);
Thanks a lot for your help.

c programming: program reads 'Desktop' folder as 'DEsktop' with capital E

So the C program i am building has to be placed within the PA_mobile file on the desktop of my MAC computer (OS X 10.9.4) so it can access the scripts and text file caches that are associated with it.
now when my program launches, it validates the current working directory, comparing it to a string variable which contains the correct working directory, and requests a working directory change if the program is not located in the desktop.
here is the code associated with the directory path validation:
char* cwd_2;
char buff[PATH_MAX + 1];
cwd_2 = getcwd( buff, PATH_MAX + 1 );
char desired_working_directory[50] = "/Users/haydn/Desktop/PA_mobile";
if( cwd_2 != NULL )
{
printf("\n-----------------------------------");
printf("\nCurrent working directory: [%s]\n", cwd_2 );
usleep(10000);
printf("Desired working directory: [%s]\n",desired_working_directory);
usleep(50000);
if(strcmp(desired_working_directory, cwd_2) == 0)
{
fflush(stdout);
printf("Appropriate Working Directory achieved!\n");
printf("-----------------------------------");
}
else
{
printf("Critical Error: inapropriate working directory!\n");
printf("please relocate the 'PA_mobile' file to [/users/user/Desktop] and re-launch\n");
getchar();
exit();
}
}
The program compares the current working directory path as determined by the cwd_2 variable and compares it to the desired working directory path as described in the desired_working_directory variable.
Now here is what the program displays when run:
-----------------------------------
Current working directory: [/users/haydn/DEsktop/PA_mobile]
Desired working directory: [/Users/haydn/Desktop/PA_mobile]
Critical Error: inapropriate working directory!
please relocate file to [/users/haydn/Desktop] and re-launch
Notice that the current working directory displayed (which is determined by the getcwd( buff, PATH_MAX + 1 ); function describes the desktop file as DEsktop instead of Desktop as it is meant to be. i have checked the actual desktop folder, it is spelt without a capital E, and the capital E appears to be causing the problem, and exists solely within this code, can anyone help me?
If you run diskutil list / (or diskutil info / on latter variants of Mac OSX) you'll see whether the file system is case sensitive or not. You may need to do it on a file system lower in the hierarchy than / if those directories are in a separate file system.
You could also try the following sequence of commands:
touch /Users/haydn/Desktop/xyzzy
touch /Users/haydn/Desktop/xyzzY
ls /Users/haydn/Desktop/xyzz*
and see how many files are there. One means case-insensitive, two means case sensitive.
It's the file system personality that dictates this and, unless it mentions case sensitivity, you can assume it's not.
In which case, there is no distinction between these:
/users/haydn/DEsktop/PA_mobile
/Users/haydn/Desktop/PA_mobile
^ ^
despite the differing letters.
If you've established that this is the problem, your code should really be adjusted to handle both cases. This question (and its answers) offer ways to do this from within your code.

DeleteFile() or unlink() calls succeed but doesn't remove file

I am facing this strange problem.
To delete a file unlink() API is called in my code. This call removes the file and succeeds on non-windows platforms. On windows it succeeds (returns 0) but doesn't remove the file.
To experiment I added a loop to call same API repeatedly. In second iteration I got an Permission denied error, Error code =13. Though read/write attributes are set on file and program has full permission to access the file.
I then called DeleteFile() instead of unlink() API. To my surprise I see the same result,call succeeded i.e. returned 1 but file is not removed physically.
I checked through unlocker utility, no other program is accessing the file except the program which is trying to remove this file.
Does anyone has idea what else could be wrong ?
Edit1:
Just to ensure file was not opened at the time of removing it. I saved the handle when file was created and tried to close before removing the file but I got error "'UNOPENED' (Errcode: 9 - Bad file descriptor)". Thus I conclude the file was not open at the time of removing it.
Edit2
As requested, here is the simplified version of code used to create and remove the file.
// Code to create the file
int create_file(const char* path)
{
HANDLE osfh; /* OS handle of opened file */
DWORD fileaccess; /* OS file access (requested) */
DWORD fileshare; /* OS file sharing mode */
DWORD filecreate; /* OS method of opening/creating */
DWORD fileattrib; /* OS file attribute flags */
SECURITY_ATTRIBUTES SecurityAttributes;
SecurityAttributes.nLength= sizeof(SecurityAttributes);
SecurityAttributes.lpSecurityDescriptor= NULL;
SecurityAttributes.bInheritHandle= !(oflag & _O_NOINHERIT);
fileaccess= GENERIC_WRITE;
fileshare= FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
filecreate= CREATE_NEW;
if ((osfh= CreateFile(path, fileaccess, fileshare, &SecurityAttributes,
filecreate, fileattrib, NULL)) == INVALID_HANDLE_VALUE)
{
// error handling
}
}
//Code to delete the file -
int remove_file (const char* name)
{
if ((err = unlink(name)) == -1)
{ //Error handling }
}
Edit3
As pointed by Joachim Pileborg and icabod, that DeleteFile() does not remove file if it is still open. As suggested by Remy Lebeau, to use process explorer. I found that one handle to file was indeed open when I closed that from process explorer file deleted like a charm :)
I had also mentioned in the Edit1 when I tried to close the file I got an error. It happened because the file descriptor I get from createfile() is not the actual handle returned by CreateFile() API instead a logical mapped handle due to underlying code complexities to support other non-windows platforms. Anyways, now I understood the root cause of problem but I was expecting if a file with open handle is passed to DeleteFile() API then it should fail in first attempt rather succeed and wait for open handles to close.
Assuming that you call your Createfile function, then later call your remove_file function... you still have a handle open to the file. The WinAPI function CreateFile, if it succeeds, keeps a handle open on the file. In your provided code, you don't close that handle.
From the documentation on DeleteFile:
The DeleteFile function marks a file for deletion on close. Therefore, the file deletion does not occur until the last handle to the file is closed. Subsequent calls to CreateFile to open the file fail with ERROR_ACCESS_DENIED.
My guess is that you still have a handle open, and when you close that handle the file will be deleted.
However, your sample code is incomplete, so it is difficult to tell.

Writing my own HTTP Server - How to find relative path of a file

I'm currently writing an HTTP Server over UNIX Sockets in C, and I'm about to implement the part of the GET request that checks the requested file to make sure it has appropriate permissions.
Before I knew anything about HTTP servers, I set up an Apache server, and it is my understanding that there is a single directory which the HTTP server looks to find a requested file. I do not know if this is because the server somehow has no permissions outside of the directory, or if it actually validates the path to ensure it is inside the directory.
Now that I am about to implement this on my own, I'm not sure how to properly handle this. Is there a function in C that will allow me to determine if a path is inside a given directory (e.g. is foo/bar/../../baz inside foo/)?
In python, I would use os.path.relpath and check if the result starts with .., to ensure that the path is not outside the given directory.
For example, if the directory is /foo/bar/htdocs, and the given path is index.html/../../passwords.txt, I want ../passwords.txt, so I can see from the leading .. that the file is outside the /foo/bar/htdocs directory.
You'd be surprised how much of Python's I/O functionality more or less maps directly to what POSIX can do. :)
In other words, look up realpath().
It's awesome when POSIX has the more descriptive name for a function, with that extra letter included! :)
How to get the absolute path for a given relative path programmatically in Linux?
#include <stdlib.h>
#include <stdio.h>
int main()
{
char resolved_path[100];
realpath("../../", resolved_path);
printf("\n%s\n",resolved_path);
return 0;
}
You can try that. As the same ser (unwind) answered there.
The way it works is much simpler: once the server receives a request, it ONLY looks at its htdoc (static contents) directory to check if the requested resource exists:
char *htdoc = "/opt/server/htdoc"; // here a sub-directory of the server program
char *request = "GET /index.html"; // the client request
char *req_path = strchr(request, ' ') + 1; // the URI path
char filepath[512]; // build the file-system path
snprintf(filepath, sizeof(filepath) - 1, "%s/%s", htdos, req_path);
FILE *f = fopen(filepath, "r"); // try to open the file
...
Note that this code is unsafe because it does not check if the request ventures in the file system by containing "../" patterns (and other tricks). You should also use stat() to make sure that the file is a regular file and that the server has permissions to read it.
As a simple (but incomplete) solution, I just decided to write a bit of code to check the file path for any ...
int is_valid_fname(char *fname) {
char *it = fname;
while(TRUE) {
if (strncmp(it, "..", 2) == 0) {
return FALSE;
}
it = strchr(it, '/');
if (it == NULL) break;
it++;
}
return TRUE;
}

Resources