I need to fill a buffer space with file descriptors of files from a defined source directory. So I have the startup code:
int main(int argc, char* argv[])
{
DIR *src=opendir(argv[1]);
struct dirent *DirEntry;
char* buffer[200];
do {
DirEntry = readdir(src);
if(DirEntry != NULL) {
//put file into buffer
}
}while(DirEntry!=NULL);
}
How do I complete this loop to place all file descriptors of a given directory into the array called 'buffer'? Should I use an object of DirEntry like DirEntry->d_name to return a file descriptor that I then put into the array?
If you need to move files from a source directory to a destination directory, you are going to need file names much more than you need file descriptors. With the names, you can open and close descriptors whenever needed; without the names, you can't create the files in the target directory sensibly. However, we can handle file descriptors too.
So, assuming you have strdup(), you might use:
typedef struct File
{
char *name;
int fd;
} File;
And in your loop:
if (DirEntry != NULL)
{
buffer[i].name = strdup(DirEntry->d_name);
if (buffer[i].name != 0)
buffer[i].fd = open(buffer[i].name, O_RDONLY);
i++;
}
where buffer is an array of File and i is a convenient integer:
enum { MAX_FILES = 4096 };
int i;
File buffer[MAX_FILES];
You should also add a condition to the main condition to ensure no overflow (or replace the fixed size buffer with a dynamically allocated one):
if (DirEntry != NULL && i < MAX_FILES)
You could sensibly break the loop if i reaches the limit. You could test whether the name represents a file (as opposed to FIFO, block device, character device, socket, symlink, directory, ...); you'd probably use stat() or lstat() for that. The file descriptor would be negative (-1) if the open() call failed. You might conserve entries by not incrementing i if the memory allocation fails, but it is probably not worth worrying about. If the memory allocation for a file name fails, there isn't going to be much else that works.
A file descriptor is "int" typed value that returned by open() system call. It's not in DirEntry structure. So, firstly, you should define the buffer array as int type, then you can open each file in the loop with open() system call, and save open() returned file descriptor in buffer array.
You question doesn't quite make sense. If you want file descriptors you have to open the file or directory with open() to get a file descriptor.
If you just want to store the names of the files in an array, then you can create a two dimensional array using malloc or calloc and copy the d_name member to the next available slot in the array. Alternatively you can use what you have and use a function such as strdup to copy the string to your array, but be careful because you'll need to free it later using free().
If you actually do want file descriptors, you will need to create an array of int rather than char *.
Related
In my code, I create a file with a random name using mkstemp() function (Im on Linux). What this function returns is an int being a file descriptor.
int fd;
char temp[] = "tempXXXXXX";
fd = mkstemp(temp);
Later I can access the file using fdopen() through that int file descriptor.
FILE *file_ptr = NULL;
file_ptr = fdopen(fd);
But at the end of my program, I would like to see if the file still exists with the random name it was given when I created it (the program should change that file name if successful). I can set a flag if the rename() function run on that file is successful, but I still don't know how to delete it when I only have its file descriptor.
if rename files => remove the temp file
How can I do that? Or is there a way to get the files name if I have its file descriptor?
Neither C nor POSIX (since you are using POSIX library functions) defines a way to delete a file via an open file descriptor. And that makes sense, because the kind of deletion you're talking about is actually to remove a directory entry, not the file itself. The same file can be hard linked into the directory tree in multiple places, with multiple names. The OS takes care of removing its data from storage, or at least marking it as available for reuse, after the last hard link to it is removed from the directory tree and no process any longer has it open.
A file descriptor is associated directly with a file, not with any particular path, notwithstanding the fact that under many circumstances, you obtain one via a path. This has several consequences, among them that once a process opens a file, that file cannot be pulled out from under it by manipulating the directory tree. And that is the basis for one of the standard approaches to your problem: unlink (delete) it immediately after opening it, before losing its name. Example:
#include <stdlib.h>
#include <unistd.h>
int make_temp_file() {
char filename[] = "my_temp_file_XXXXXX";
int fd;
fd = mkstemp(filename);
if (fd == -1) {
// handle failure to open ...
} else {
// file successfully opened, now unlink it
int result = unlink(filename);
// ... check for and handle error conditions ...
}
return fd;
}
Not only does that (nearly) ensure that the temp file does not outlive the need for it, but it also prevents the contents from being accessible to users and processes to which the owning process does not explicitly grant access.
Even though this doesn't exactly answer the question you're asking about mkstemp, consider creating a temporary file that will automatically be deleted, unless you rename it.
Instead of mkstemp you could call open combined with the creation flag O_TMPFILE to create a temporary, unnamed file that is automatically deleted when file is closed.
See open(2):
O_TMPFILE (since Linux 3.11)
Create an unnamed temporary regular file. The pathname argu‐
ment specifies a directory; an unnamed inode will be created
in that directory's filesystem. Anything written to the
resulting file will be lost when the last file descriptor is
closed, unless the file is given a name.
Instead of a filename, you call open with the path where you prefer to place the temporary file, like:
temp_fd = open("/path/to/dir", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
If you like to give the temporary file a permanent location/name, you can call linkat on it later:
linkat(temp_fd, NULL, AT_FDCWD, "/path/for/file", AT_EMPTY_PATH);
Note: Filesystem support is required for O_TMPFILE, but mainstream Linux filesystems do support it.
readlink provide you the name of your file depending of the file descriptor if you use the path /proc/self/fd/ adding you fd.
Then use remove for deleting the file passing the name readlink gave you
ssize_t readlink(const char *path, char *buf, size_t bufsiz); (also load ernno)
int remove(const char *filename); (returns zero is successful, otherwise nonzero)
I hope something like that could helped you ?
⚠ Don't copy/past this you must edit "filename"; _BUFFER, _BUFSIZE ⚠
#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
int delete_file(int fd) {
char *str_fd = itoa(fd, str_fd, 10);
char *path = strcat("/proc/self/fd/", str_fd);
if (read_link(path, buffer, bufsize) == -1)
return -1;
int del = remove(filename);
if (!del)
printf("The file is Deleted successfully");
else
printf("The file is not Deleted");
return 0;
}
(feel free to edit this, i didn't test the code and i let you handel the buffer and buffer size)
I thought I understood fstat, I was wrong.
I need to know the size of the file then read from the file.
fileN Has the path of the file and the name of the file. It looks like this. 0.txt
struct stat fileStat;
FILE *fp;
int fd = 0;
int i;
for(i = 0; i < 100; i++)
{
fp = fopen(fileN, "r");
fd = open(fileN, "r"); // I think this is eating my files and making them 0 size.
fstat(fd, $fileStat);
printf("%d", fileStat.st_size);
fclose(fp);
}
Why do I need to use fd = open(fileN, "r"); To use fstat? It feels like I am opening the file twice.
If fileStat is a struct why do I use fileStat.st_size instead of fileStat->st_size like I do when I create my own structs? Example: myString->characters;
And why is the code above printing 0s when printf is executed? And yes the files have a size bigger than 0 and I have the correct file name and path.
All code above is my interpretation of code that was Googled and mixed with my trial and error implementation. That's why I have so many questions.
EDIT: SOLUTION: open() was being called wrong and affecting the files.
Why do I need to use fd = open(fileN, "r"); To use fstat? It feels like I am opening the file twice.
Because fstat() requires a file descriptor. You could use stat() instead, which takes a filename. Or you could use fd = fileno(fp) to get the file descriptor from the stdio FILE.
BTW, you need to call close(fd) in the loop.
If fileStat is a struct why do I use fileStat.st_size instead of fileStat->st_size like I do when I create my own structs? Example: myString->characters;
You use -> when the variable on the left is a pointer to a struct, you use . when it's the struct itself. This is basic C syntax, nothing specific to fstat.
And why is the code above printing 0s when printf is executed? And yes the files have a size bigger than 0 and I have the correct file name and path.
You're not calling open() correctly. The second argument is supposed to be an int containing flags. Since you give a string instead, the pointer to the string is being coerced to an int, and the bits in this are probably not valid open flags. You need to check the return value of open to see if it succeeded -- it will return -1 if there's an error. Similarly with fstat -- it's probably returning an error because fd is not valid.
Disclaimer: this is for an assignment. I am not asking for explicit code. Rather, I only ask for enough help that I may understand my problem and correct it myself.
I am attempting to recreate the Unix ar utility as per a homework assignment. The majority of this assignment deals with file IO in C, and other parts deal with system calls, etc..
In this instance, I intend to create a simple listing of all the files within the archive. I have not gotten far, as you may notice. The plan is relatively simple: read each file header from an archive file and print only the value held in ar_hdr.ar_name. The rest of the fields will be skipped over via fseek(), including the file data, until another file is reached, at which point the process begins again. If EOF is reached, the function simply terminates.
I have little experience with file IO, so I am already at a disadvantage with this assignment. I have done my best to research proper ways of achieving my goals, and I believe I have implemented them to the best of my ability. That said, there appears to be something wrong with my implementation. The data from the archive file does not seem to be read, or at least stored as a variable. Here's my code:
struct ar_hdr
{
char ar_name[16]; /* name */
char ar_date[12]; /* modification time */
char ar_uid[6]; /* user id */
char ar_gid[6]; /* group id */
char ar_mode[8]; /* octal file permissions */
char ar_size[10]; /* size in bytes */
};
void table()
{
FILE *stream;
char str[sizeof(struct ar_hdr)];
struct ar_hdr temp;
stream = fopen("archive.txt", "r");
if (stream == 0)
{
perror("error");
exit(0);
}
while (fgets(str, sizeof(str), stream) != NULL)
{
fscanf(stream, "%[^\t]", temp.ar_name);
printf("%s\n", temp.ar_name);
}
if (feof(stream))
{
// hit end of file
printf("End of file reached\n");
}
else
{
// other error interrupted the read
printf("Error: feed interrupted unexpectedly\n");
}
fclose(stream);
}
At this point, I only want to be able to read the data correctly. I will work on seeking the next file after that has been finished. I would like to reiterate my point, however, that I'm not asking for explicit code - I need to learn this stuff and having someone provide me with working code won't do that.
You've defined a char buffer named str to hold your data, but you are accessing it from a separate memory ar_hdr structure named temp. As well, you are reading binary data as a string which will break because of embedded nulls.
You need to read as binary data and either change temp to be a pointer to str or read directly into temp using something like:
ret=fread(&temp,sizeof(temp),1,stream);
(look at the doco for fread - my C is too rusty to be sure of that). Make sure you check and use the return value.
I have opened one file with following way:
fp = fopen("some.txt","r");
Now in this file the 1st some bytes lets say 40 bytes are unnecessary junk of data so I want to remove them. But I cannot delete that data from that file, modify or
create duplicates of that file without that unnecessary data.
So I want to create another dummy FILE pointer which points to the file and when I pass this dummy pointer to any another function that does the following operation:
fseek ( dummy file pointer , 0 , SEEK_SET );
then it should set the file pointer at 40th position in my some.txt.
But the function accepts a file descriptor so i need to pass a file descriptor which will treat the file as those first 40 bytes were never in the file.
In short that dummy descriptor should treat the file as those 40 bytes were not in that file and all positioning operations should be with respect to that 40th byte counting as the is 1st byte.
Easy.
#define CHAR_8_BIT (0)
#define CHAR_16_BIT (1)
#define BIT_WIDTH (CHAR_8_BIT)
#define OFFSET (40)
FILE* fp = fopen("some.txt","r");
FILE* dummy = NULL;
#if (BIT_WIDTH == CHAR_8_BIT)
dummy = fseek (fp, OFFSET*sizeof(char), SEEK_SET);
#else
dummy = fseek (fp, OFFSET*sizeof(wchar_t), SEEK_SET);
#endif
The SEEK_SET macro indicates beginning of file, and depending on whether you are using 8-bit characters (ASCI) or 16-bit characters (eg: UNICODE) you will step 40 CHARACTERS forward from the beginning of your file pointer, and assign that pointer/address to dummy.
Good luck!
These links will likely be helpful as well:
char vs wchar_t
http://www.cplusplus.com/reference/clibrary/cstdio/fseek/
If you want, you can just convert a file descriptor to a file pointer via the fdopen() call.
http://linux.die.net/man/3/fdopen
fseek ( dummy file pointer , 0 , SEEK_SET );
In short that dummy pointer should treat the file as there is no that 40 byte in that file and all position should be with respect to that 40th byte as counting as it is 1st byte.
You have conflicting requirements, you cannot do this with the C API.
SEEK_SET always refers to the absolute position in the file, which means if you want that command to work, you have to modify the file and remove the junk.
On linux you could write a FUSE driver that would present the file like it was starting from the 40th byte, but that's a lot of work. I'm only mentioned this because it's possible to solve the problem you've created, but it would be quite silly to actually do this.
The simplest thing of course would be just to abandon this emulating layer idea you're looking for, and write code that can handle that extra header junk.
If you want to remove the first 40 bytes of a file on the disk without creating another file, then you can copy the content from the 41th byte and onwards into a buffer, then write it back at offset -40. Then use ftruncate (a POSIX library in unistd.h) to truncate at (filesize - 40) offset.
I wrote a small code with what i understood from your question.
#include<stdio.h>
void readIt(FILE *afp)
{
char mystr[100];
while ( fgets (mystr , 100 , afp) != NULL )
puts (mystr);
}
int main()
{
FILE * dfp = NULL;
FILE * fp = fopen("h4.sql","r");
if(fp != NULL)
{
fseek(fp,10,SEEK_SET);
dfp = fp;
readIt(dfp);
fclose(fp);
}
}
The readIt() is reading the file from the 11 byte.
Is this what you are expecting or something else?
I haven't actually tried this, but I think you should be able to use mmap (with the MAP_SHARED option) to get your file mapped into your address space, and then fmemopen to get a FILE* that refers to all but the first 40 bytes of that buffer.
This gives you a FILE* (as you describe in the body of your question), but I believe not a file descriptor (as in the title and elsewhere in the question). The two are not the same, and AFAIK the FILE* created with fmemopen does not have an associated file descriptor.
Here's how I open a file for writing+ :
if( fopen_s( &f, fileName, "w+" ) !=0 ) {
printf("Open file failed\n");
return;
}
fprintf_s(f, "content");
If the file doesn't exist the open operation fails. What's the right way to fopen if I want to create the file automatically if the file doesn't already exist?
EDIT: If the file does exist, I would like fprintf to overwrite the file, not to append to it.
To overwrite any existing file, use the creat call:
#include <fcntl.h>
#include <stdio.h>
int fd = creat (fileName, 0666); // creates file if not exist, overwrite existing
FILE *f = fdopen (fd, "w"); // optional, if FILE * type desired
Did you try just doing fopen(name, "w")? Also, you should perhaps extend your code to report what error is being signalled, using e.g. perror().
Note
Incidentally, I would avoid (at least most of) MSVC's _s functions despite the warnings. There's very little point in the first place except when:
The original function either writes to a passed-in buffer, but does not have a parameter to specify the size of the buffer (e.g. strcat()), or
The original function was permitted/required to return a pointer to a static buffer (e.g. strerror()), which makes
and these functions are non-portable. In short, most of these functions (including fopon_s()) are gratuitously non-portable -- using them makes your program less portable but gives no benefit. (The incompatible addendum for C can only make things worse -- unless MS implements it, in which case it might only make things more confusing.)