Open function : how to protect against directory opening? - c

I am using the open function like this in my file to get some coordinates from a file :
t_coo *get_buffer(char **av, t_coo **head)
{
int ret;
int fd;
char *line;
int y;
t_coo *cur;
cur = NULL;
*head = NULL;
y = 0;
ret = 0;
fd = open(av[1], O_RDONLY);
while ((ret = get_next_line(fd, &line) > 0))
{
head = get_map(line, head, y);
y++;
}
close(fd);
cur = *head;
return (cur);
}
It is working perfectly but the problem is when I try to make it open a directory, my program segfault. I want to protect it against directory opening so that I dont segault anymore. I tried to look at the flags on the internet and tried many of them but I could not find this one. Can anybody tell me which one it is? Thank you.

You need to use the lstat function to tell you whether the given file name represents a regular file or a directory.
struct stat statbuf;
int rval;
rval = lstat(argv[1], &statbuf);
if (rval == -1) {
perror("error getting file status");
exit(1);
}
if (S_ISREG(statbuf.st_mode)) {
printf("%s is a regular file\n", argv[1]);
} else if (S_ISDIR(statbuf.st_mode)) {
printf("%s is a directory\n", argv[1]);
} else {
printf("%s is something else\n", argv[1]);
}

I would suggest to open the file (which could be a directory) so get a file descriptor, then use fstat(2) on that file descriptor (and check the result of fstat by using statresult.st_mode & S_IFMT == S_IFDIR ...)
This would avoid an (improbable) race condition with the lstat (or stat) then open approach (suggested in Dbush's answer): some other process might (with very bad luck) remove or rename the file between these two system calls. You might also opendir or else open (but that also suffers from a similar race condition).
PS. The race conditions I am suggesting here are so improbable that we can normally ignore them... But they might be a security flaw (than an attacker could use)

Related

How can I detect that I open()ed a directory in C, without using stat()?

I've written a simplified "cat" function in C. It is working fine, except when one of my argument is the name of a directory.
As it is an assignement, I'm only allowed to use "open", "read" and "close" functions in my code.
When "-1" is returned by function open(file, O_RDONLY), I call function ft_display_error to display error messages such as "No such file or directory".
Yet it doesn't work when "file" is a directory: in this case open will not return "-1". It will go on some kind of infinite loop.
void ft_display_file(char *file)
{
int fd;
char buf[BUF_SIZE + 1];
int ret;
fd = open(file, O_RDONLY);
if (fd == -1)
ft_display_error(file);
else
{
ret = read(fd, buf, BUF_SIZE);
while(ret)
{
buf[ret] = 0;
write(1, buf, ret);
ret = read(fd, buf, BUF_SIZE);
}
}
close(fd);
}
int main(int ac, char **av)
{
int i;
i = 1;
while (i < ac)
{
ft_display_file(av[i]);
i++;
}
}
Instead, I would like my program to identify that my argument is a directory, and then display the following message "cat: file: Is a directory.
Opening a directory for reading with open is the low level way of accessing its contents. Not very useful for you, but it doesn't allow to test for a directory.
If you cannot use stat (which is the best option) there seems to be another trick:
According to the documentation of open
The open() function shall fail if:
...
EISDIR
The named file is a directory and oflag includes O_WRONLY or O_RDWR.
So first try to open your file with O_RDWR (read-write) and if it fails, check if errno is equal to EISDIR
Code (untested)
fd = open(file, O_RDWR);
if ((fd == -1) && (errno == EISDIR))
{
// this is a directory
}

Issue with program involving sockets and client-server communication in C

I am working on a program in C that involves client-server connections and communication between the two parties.
The program involves the client sending a letter to the server and the server getting the letter. The server then searches through the current file directory (in linux) for a file beginning with that letter and sends the client the number of bytes of the file and the text of the file.
The overall program is very long and for the assignment the instructor already did much of the code such as setting up the sockets and creating the entire program for the client side of operations.
For the server side I had to write code for:
getting the file descriptor from the passed memory and casting it
-getting the letter from the client
-Attempting to open the current directory
-Iterating through the directory looking for a file that starts with the letter
-Attempting to open the file and sending the size of the file and number of bytes of file to the client in network endian
-Closing the file and directory after finishing
-Error checking: there are error checking statements if the directory cannot be opened, the file cannot be opened, or no matching file is found
The following is my code with comments
void* handleClient (void* vPtr
)
{
// I. Application validity check:
int fd = *((int *) vPtr);
//casting vPtr to an int//
free(vPtr);
// II. Handle the client:
char buffer[BUFFER_LEN+1];
read(fd, buffer, BUFFER_LEN+1);
//read the letter into a buffer//
const char* dirNamePtr = ".";
DIR* dirPtr = opendir(dirNamePtr);
// Open the current directory
if (dirPtr == NULL)
{
int toSend = htonl(CANT_READ_DIR_CODE);
write(fd,&toSend,sizeof(toSend));
printf("Cannot read directory\n");
return(NULL);
}
// If current directory cannot be opened, it sends a error message in network // endian to the client
struct dirent* entryPtr;
char path[BUFFER_LEN];
struct stat statBuffer;
//implements struct dirent to get info on the directory
//iterates through the directory
while ((entryPtr=readdir(dirPtr)) != NULL)
{
stat(entryPtr->d_name, &statBuffer);
//puts in metaddata of the current directory into statbuffer
if (!S_ISREG(statBuffer.st_mode))
continue;
//if the entry is not a file, continue
// if the first letter of the file is not the character received from the //client, send an error mesage
if(entryPtr->d_name[0]!=buffer[0]) {
int toSend2 = htonl(NO_MATCH_CODE);
write(fd,&toSend2,sizeof(toSend2));
printf("No matching file\n");
return(NULL);
}
int ab;
int numRead;
int numBytes;
char buffer[BUFFER_LEN];
//open the file and send bytes of file and file size to client
if (entryPtr->d_name[0]==buffer[0] &(S_ISREG(statBuffer.st_mode)))
{
ab=open(entryPtr->d_name,O_RDONLY,0660);
if(ab<0) {
int toSend3 = htonl(CANT_READ_FILE_CODE);
write(fd,&toSend3, sizeof(toSend3));
printf("Cannot read <filename>\n");
return(NULL);
}
numBytes=htonl(statBuffer.st_size);
write(fd, &numBytes, sizeof(numBytes));
printf("Sending %s, %d bytes\n",entryPtr >d_name,statBuffer.st_size);
while((numBytes=read(ab,buffer,BUFFER_LEN))>0)
{
printf("We read %d bytes\n", numBytes);
write(fd, buffer, numBytes);
}
//close the fiel
close(ab);
}
break;
//leave the loop
}
// III. Finished:
//
closedir(dirPtr);
return(NULL);
}
My code compiles but does not send the file to the client when I try running it. I have tried several different letters and it has not worked for any of them. I do not quite know what the issue is which makes it difficult to fix my mistakes.
I am not asking for the answer or anything, just help in seeing where I am wrong. I appreciate any help.
Your logic for when to send vs. when to send no-file status seems wrong. I think it should be like this (fair warning, I didn't test this, or even compile it beyond basic syntax checking, but you should get the idea):
void* handleClient(void* vPtr)
{
// I. Application validity check:
int fd = *((int *) vPtr);
free(vPtr);
// II. Handle the client:
char buffer[BUFFER_LEN+1];
read(fd, buffer, BUFFER_LEN+1);
//read the letter into a buffer//
const char* dirNamePtr = ".";
DIR* dirPtr = opendir(dirNamePtr);
// Open the current directory
if (dirPtr == NULL)
{
int toSend = htonl(CANT_READ_DIR_CODE);
write(fd,&toSend,sizeof(toSend));
printf("Cannot read directory\n");
return(NULL);
}
struct dirent* entryPtr;
char path[BUFFER_LEN];
struct stat statBuffer;
//implements struct dirent to get info on the directory
//iterates through the directory
while ((entryPtr=readdir(dirPtr)) != NULL)
{
stat(entryPtr->d_name, &statBuffer);
//puts in metaddata of the current directory into statbuffer
// if this isn't a regular file OR the first char doesn't match...
if (!S_ISREG(statBuffer.st_mode) || entryPtr->d_name[0]!=buffer[0])
continue;
int ab;
int numRead;
int numBytes;
char buffer[BUFFER_LEN];
//open the file and send bytes of file and file size to client
ab = open(entryPtr->d_name,O_RDONLY,0660);
if(ab<0) {
int toSend3 = htonl(CANT_READ_FILE_CODE);
write(fd,&toSend3, sizeof(toSend3));
printf("Cannot read <filename>\n");
closedir(dirPtr);
return(NULL);
}
numBytes=htonl(statBuffer.st_size);
write(fd, &numBytes, sizeof(numBytes));
printf("Sending %s, %d bytes\n",entryPtr >d_name,statBuffer.st_size);
while((numBytes=read(ab,buffer,BUFFER_LEN))>0)
{
printf("We read %d bytes\n", numBytes);
write(fd, buffer, numBytes);
}
//close the file and leave
close(ab);
break;
}
// if this is NULL it means we dind't send anything. we break the loop
// when a file to send it discovered.
if (entryPtr == NULL)
{
printf("No matching file\n");
int toSend2 = htonl(NO_MATCH_CODE);
write(fd, &toSend2, sizeof(toSend2));
}
// III. Finished:
closedir(dirPtr);
return(NULL);
}

Handle Spaces in File Path Unix programmatically

I'm trying to open files at random from my disk. Problem Is Most have Spaces in their path and won't
open unless I add backslashes on the fly, which is proving more difficult than I thought. While doing research I saw
how does unix handle full path name with space and arguments?
which say's that you can use backslashes or quotes but that was referring to passing arguments to the shell. Is it possible to do something like...
static int removeSpaces(char **inBuf, char **outBuf)
{
/* Declarations */
int rtrn;
char *pos;
/* Null terminate incoming buffer */
if((pos=strchr(*inBuf, '\n')) != NULL)
{
*pos = '\0';
}
rtrn = asprintf(outBuf, "\"%s\"", *inBuf);
if(rtrn < 0)
{
printf("Memory Error\n");
return -1;
}
return 0;
}
int main(void)
{
int rtrn;
char *file, *parsed;
rtrn = getFile(&file);
if(rtrn < 0)
{
printf("Can't Get File\n");
return -1;
}
rtrn = removeSpaces(&file, &parsed);
if(rtrn < 0)
{
printf("Can't Get File\n");
return -1;
}
printf("Parsed: %s\n", parsed);
fd = open(parsed, O_RDONLY);
if(fd < 0)
{
perror("Can't Open File\n");
return -1;
}
return 0;
}
I currently get no such file or directory, but i was wondering can I pass quoted file paths to open() or fopen(), or is that only the case with passing file paths to the shell. I'm asking because I saw very little information on the web specific to unix and unix-like systems, most was about windows (at least what i saw), and what i did find for unix was about the shell and not system calls.

read system call error (bad file descriptor)

I am having this code in which I am getting BAD FILE DESCRIPTOR problem at the read system call. However my write call with the same file descriptor is working fine. Please suggest
void Update_Log( )
{
struct logDetails update,update1[30];
struct stat fileData,fileData1;
int file;
int index;
//pthread_t pid;
char writeBuffer[MAX_BUFFER_SIZE];
char readBuffer[MAX_BUFFER_SIZE];
char mBuf[MAX_BUFFER_SIZE],mBuf1[MAX_BUFFER_SIZE];
if((access("/home/team02/DMS/Server/",F_OK))==0) //checking the file/dir existence
puts("file found");
else
puts("file not found");
if((file=open("/home/team02/DMS/Server/filename.txt",O_RDONLY|O_WRONLY|O_APPEND,S_IRWXU))==-1)
perror("file not opened");
if((fstat(file, &fileData))==-1)
perror("structure not filled");
if((stat("/home/team02/DMS/Server/f1",&fileData1))==-1)
perror("structure not filled");
//printf("%d/n",fileData.st_mtime);
//printf("%d",fileData.st_ctime);
struct tm *mytm = localtime(&fileData.st_mtime);
struct tm *mytime=localtime(&fileData1.st_mtime);
strftime(mBuf1,18,"%I:%M:%S-%m%d%y",mytime);
strftime(mBuf, 18, "%I:%M:%S-%m/%d/%y", mytm);
puts(mBuf);
if((strcmp(mBuf,mBuf1)==0))
puts("equal");
else
puts("not equal");
strcpy(update.timestamp,mBuf);
strcpy(update.clientName,mBuf);
strcpy(update.filename,mBuf1);
snprintf(writeBuffer,MAX_BUFFER_SIZE,"%s %s %s",update.clientName,update.filename,update.timestamp);
//printf("%s",writeBuffer);
//if((pthread_create(&pid,&thread_handler,NULL))!=0)
//perror("Thread not created");
if((write(file,writeBuffer,strlen(writeBuffer)))==-1)
perror("write unsuccessful");
**if((read(file,readBuffer,MAX_BUFFER_SIZE))==-1)
perror("read unsuccessful");**
for(index=0;index<strlen(readBuffer);index++)
{
sscanf(readBuffer,"%s %s %s",update1[index].clientName,update1[index].filename,update1[index].timestamp);
printf("%s",update1[index].clientName);
}
close(file);
}
Depending on the run time library, the open mode O_RDONLY|O_WRONLY may be problematic. You probably want O_RDWR to replace that part.
Also, you can get the errno value to find out exactly what the problem is. You are calling perror() in the case of an error. That should be telling you what the issue is. What output does the program generate?

Why does writing to a file descriptor after the target file has been deleted succeed?

code:
int main(int argc, char **argv)
{
int fd = open("test.txt", O_CREAT|O_RDWR, 0200|0400);
if(fd == -1)
{
printf("failure to oepn");
exit(-1);
}
int iRet = write(fd, "aaaaaaaaaa", 10);
if(iRet == -1)
{
printf("failure to writer");
exit(-1);
}
sleep(10);
printf("You must remove");
iRet = write(fd, "bbbbbbbbbb", 10);
if(iRet == -1)
{
printf("failure to after writer");
exit(-1);
}
exit(0);
}
during the sleep(), you delete the test.txt, but the process write successful!why?
if a log ”Singleton“ instance, you remove the file on the disk.write is successful, but you can get nothing.
class log
{
public:
void loggerWriter(std::string str);
int fd;
};
log::log(std::string filename):fd(-1)
{
fd = open(filename.c_str(), O_CREAT|)
//...
}
log::loggerWriter(std::string str)
{
writer(fd, str.c_str(), str.size());
}
int main()
{
log logger("text.txt");
//...
//I want to know the text.txt the text.txt have delete on the disk or not.
//if delete i can create another file to log.
}
"unlink" cann't solve this problem.
The manual page for unlink(2) states clearly:
unlink() deletes a name from the file system. If that name was the
last link to a file and no processes have the file open the file is
deleted and the space it was using is made available for reuse.
If the name was the last link to a file but any processes still have
the file open the file will remain in existence until the last file
descriptor referring to it is closed.
As caf excellently notes in the comments:
The write() is successful because it writes to the file, which still
exists at this point even though it no longer has a name. The filename
and the file itself are distinct, and have separate lifetimes.

Resources