I am new to this forum, so please bear with me.
I am currently writing a program that will back up specific files and each time it is backed up a date and time stamp are added to the file name. At this stage I am writing a function that will identify all the files in the present working directory and will then read it into an array and will later on back up each of those files. The backing up works fine.
The problem I am having is that each time I attempt to pass a folder name to my function (as a string) I get a segmentation fault the debugger gives this error:
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400b48 in backupAllFiles (
dirname=) at processing.c:52
52 {
The backupAllFiles() function is written inside the processing.c source file (it is a multi-source file project). Here is a snippet of the code that is the main concern:
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <time.h>
#include <getopt.h>
void backupAllFiles(char *dirname)
{
int numfiles = 0;
char allFiles[MAXPATHLEN][MAXPATHLEN];
DIR *dirp;
struct dirent *dp;
dirp = opendir(dirname);
if(dirp == NULL)
{
fprintf(stderr, "Could not open: %s.\n",dirname);
exit(EXIT_FAILURE);
}
while((dp = readdir(dirp)) != NULL)
{
struct stat stat_buffer;
if(stat(allFiles[numfiles], &stat_buffer) != 0)
{
fprintf(stderr,"An error occurred while reading the directory: %s\n",dirname);
}
else if(S_ISREG(stat_buffer.st_mode))
{
sprintf(allFiles[numfiles], "%s%s", dirname, dp->d_name);
}
numfiles++;
}
for(int i = 0; i < numfiles; i++)
{
fprintf(stderr,"All the files are (%i) %s.\n",i ,allFiles[i]);
}
}
int main(int argc, char *argv[])
{
fprintf(stderr, "Entered main, passing argument %s to backupAllFiles()\n",argv[1]);
backupAllFiles(argv[1]);
return 0;
}
Please note this is not part of the main program yet, this is merely a separated segment which is used for individual testing.
I am really stumped as to why the segfault occurs and I have tried strdup for argv[1], however to no avail.
I appreciate all and any help. :)
P.S. There will be extra inclusions that are not necessary, this is for the rest of the program, I was too lazy to find which would exclusively apply to this function.
Just to recap some of the discussion into an answer: there are at least two problems with this code.
Declaring char allFiles[MAXPATHLEN][MAXPATHLEN] on the stack tries to allocate more memory than the system is willing to permit, which is why the segmentation fault is raised as soon as the code enters backupAllFiles.
The stat call in backupAllFiles accesses the allFiles[numfiles] element before initializing it. This should probably be
if(stat(dp->d_name, &stat_buffer) != 0)
rather than
if(stat(allFiles[numfiles], &stat_buffer) != 0)
Thanks to #wildplasser and #Carl Norum for identifying that the underlying problem here was, literally, stack overflow.
Related
I am trying to print the file name given the inode number. I am using the execlp system call to run the find command. My code is shown below:
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
char str[8];
char *ptr;
ptr=str;
long x=9306140;
snprintf(str,8,"%ld", x);
execlp("find"," ","~"," ","-inum"," ",str,NULL);
}
But I get an error: Invalid argument '9306140' to -inum.
Could someone please help?
As noted in comments, you don't want the " " (space) arguments in the execlp() argument list. The shell takes a string with spaces separating the arguments, but it treats what's between the spaces as words that are passed to the command; it doesn't ordinarily pass the spaces to the command. (That's a simple and sufficient explanation for this exercise; there are caveats and weasel-words galore that could be added.)
The shell also expands ~ to match the value in the $HOME environment variable (in contrast to ~user which gets the home directory of user from the password file — they're usually, but not necessarily, the same).
You also have little margin for error in the size of string you've allocated for the number. Frankly, though, you should simply use a string. All these changes lead to:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
const char *home = getenv("HOME");
const char inode[] = "9306140";
execlp("find", "find", home, "-inum", inode, (char *)NULL);
fprintf(stderr, "failed to execute 'find' command\n");
return(EXIT_FAILURE);
}
Note the repeated "find"; the first is the name of the file to be executed after searching along the path; the second is the value to be provided as argv[0]. You could put "hypochondriac" as the second occurrence and it is likely that find would work the same, at worst reporting its error messages as coming from the program 'hypochondriac'.
The next step would be to take the inode number from a command line argument:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
if (argc != 2)
{
fprintf(stderr, "Usage: %s inode\n", argv[0]);
exit(EXIT_FAILURE);
}
const char *home = getenv("HOME");
const char *inode = argv[1];
execlp("find", "find", home, "-inum", inode, (char *)NULL);
fprintf(stderr, "%s: failed to execute 'find' command\n", argv[0]);
return(EXIT_FAILURE);
}
The step after that would be to handle multiple inode values; at that point, you use execvp() instead of execlp(), though (or, if you're desparate and lazy, you loop and fork() and execlp() once per inode number, but that's slamming your system for no good reason at all).
I have attempted to make a script that creates a file and then sets it as immutable similar to the chattr +i command for linux. The script compiles (with gcc), runs and the file is created. However the file itself is not immutable and can be removed with a simple rm -f. I have attempted to stacktrace where chattr is called and I found a function called ioctl. I then used what little information I could gather and came up with what I have below. I narrowed it down from ext2_fs.h but it just doesn't seem to work. I've clearly overlooked something.
Updates to previous entry: Compiles but returns -1 on ioctl() function. Bad address shown with perror().
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
int main()
{
FILE *fp;
char shovel[16] = "I have a shovel!";
fp = fopen("/shovel.txt", "w+");
fwrite(shovel, sizeof(shovel[0]), sizeof(shovel)/sizeof(shovel[0]), fp);
ioctl(fileno(fp), FS_IOC_SETFLAGS, 0x00000010);
fclose(fp);
}
Any help appreciated.
You are using the right ioctl command, but you're passing it the wrong arguments.
The manpage for ioctl_list(2) shows that FS_IOC_SETFLAGS expects to receive a pointer to int (an int *), yet you're passing it an integer literal (hence the Bad Address error).
The fact that you don't to any error checking whatsoever is also not helping.
The correct flag to pass to FS_IOC_SETFLAGS is a pointer holding the value EXT2_IMMUTABLE_FL, which is defined in ext2fs/ext2_fs.h (some older / different Linux distributions seem to have it under linux/ext2_fs.h), so you'll need to #include <ext2fs/etx2_fs.h>. Make sure to install e2fslibs-dev (and probably you'll need linux-headers too).
This code is working:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <ext2fs/ext2_fs.h>
int main()
{
FILE *fp;
char shovel[16] = "I have a shovel!";
if ((fp = fopen("shovel.txt", "w+")) == NULL) {
perror("fopen(3) error");
exit(EXIT_FAILURE);
}
fwrite(shovel, sizeof(shovel[0]), sizeof(shovel)/sizeof(shovel[0]), fp);
int val = EXT2_IMMUTABLE_FL;
if (ioctl(fileno(fp), FS_IOC_SETFLAGS, &val) < 0)
perror("ioctl(2) error");
fclose(fp);
return 0;
}
Remember to run this as root.
UPDATE:
As Giuseppe Guerrini suggests in his answer, you might want to use FS_IMMUTABLE_FL instead, and you won't need to include ext2_fs.h:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
int main()
{
FILE *fp;
char shovel[16] = "I have a shovel!";
if ((fp = fopen("shovel.txt", "w+")) == NULL) {
perror("fopen(3) error");
exit(EXIT_FAILURE);
}
fwrite(shovel, sizeof(shovel[0]), sizeof(shovel)/sizeof(shovel[0]), fp);
int val = FS_IMMUTABLE_FL;
if (ioctl(fileno(fp), FS_IOC_SETFLAGS, &val) < 0)
perror("ioctl(2) error");
fclose(fp);
return 0;
}
The main problem is that the ioctl wants a pointer to the mask, not a direct constant. You have to define a int variable, store the mask (0x10) in it and pass its address as third argument of ioctl.
Also, I'd add some hints:
other programs to change attributes are used to use low-level I/O directly (open, close...). Also, the file is usually opened with O_RDONLY.
Use FS_IMMUTABLE_FL istead the raw constant.
Get the current attribute mask first (FS_IOC_SETFLAGS) and mask it with the new flag, so other settings are not lost by the service.
I'm trying to write a program to open an archive file from Unix, read the files in and print what files are inside the archive and just the filename. Below is my code -- it compiles, but I got some weird output in the terminal -- e.g. ?;?U?. It should just display 2 txt file names. Can someone take a look at my code and give me some guidance on what I'm missing? Thank you!
EDIT: I made some changes to my code, but it's still not working. Any suggestion or help is greatly appreciated.
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/utsname.h>
#include <ctype.h>
#include <string.h>
#include <ar.h>
int main (int argc, char **argv)
{
int input_fd;
//char buffer[25];
struct ar_hdr my_ar;
if (argc != 2) {
printf("Error", argv[0]);
exit(EXIT_FAILURE);
}
//open the archive file (e.g., hw.a)
input_fd = open(argv[1], O_RDONLY);
if (input_fd == -1)
{
perror("Can't open input file\n");
exit(-1);
}
while (read(input_fd, my_ar.ar_name, sizeof(buffer)) > 0) {
printf("%s\n", my_ar.ar_name);
}
close(input_fd);
return 0;
}
I don't see anywhere that you are defining what a my_hdr is, yet you are printing one of its supposed members here:
printf("%s\n", my_ar.ar_name);
You also never set ar_name to anything either.
I see you read some data into a variable named buffer, but you never actually copy that buffer into ar_name which I am assuming is your intent.
while (read(input_fd, buffer, sizeof(buffer)) > 0) {
printf("%s\n", my_ar.ar_name);
}
The POSIX documentation explicitly states that the archive file format is not described, on the grounds that several incompatible such formats already exist, so you'll definitely need to study the documentation provided by your OS if you're going to use <ar.h> instead of the ar command-line utility.
Here are some details that might help, based on the Solaris implementation:
An archive file starts with a magic cookie string which you are neither verifying nor skipping,
Each header object is followed by the content of the actual object in the archive, which you are not stepping over,
The first object in the archive is an unnamed object containing the archive's symbol table,
If an object's name is more than fifteen bytes long, it will be stored in the archive's string table, which appears to be part of the symbol table.
Note that these points, or details thereof, may vary on your OS. As I said at the beginning, study the documentation provided by your OS.
I have a really weird problem here, and haven't managed to find an answer online.
It appears after debugging with printf statements that a segfault ocurred when trying to read errno. Commenting problemed lines out one by one as they cause segfault resulted in having to comment out every reference to errno, after a readdir() call reaches the end of the directory stream and returns NULL.
Even then, the code then segfaults later when trying to access another automatic variable, file_count.
What is going on? Is this a stack overflow? How do I make it stop?
The code is below, if you feel the need to wade through it. All the problematic references to errno are removed, and the program segfaults after successfully executing the third second last line: printf("printing file_count\n");.
EDIT1: Here's a GDB backtrace:
#0 0xc95bf881 in strcpy () from /usr/lib/libc.so.1
#1 0x08051543 in dir_get_list (user=0x8047b88 "user1") at maildir.c:231
#2 0x08050f3e in main (argc=4, argv=0x80479f4) at maildir.c:43
END EDIT1
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#define MAX_FILENAME_LENGTH 255
#define MAX_USERNAME_LENGTH 40
#define MAX_PASSWORD_LENGTH 20
typedef int bool;
#define true 1
#define false 0
struct files_struct{
/*The number of email messages in a maildir.*/
int count;
/*A pointer to an array of pointers to the strings of the filenames. */
char **FileNames;
/*A pointer to an array of ints that give the corresponding size of the file.*/
int *FileSize;
};
typedef struct files_struct FilesStruct;
void dir_set_path(char* path);
bool check_user(char* username, char* pass);
FilesStruct* dir_get_list(char* user);
void delete_mail(char* user, char* filename);
char* get_file(char* user, char* filename);
FilesStruct* dir_get_list(char* user){
char maildir_name[MAX_FILENAME_LENGTH];
DIR * maildir_fd;
struct dirent *maildir_p;
strcpy(maildir_name,"./");
strncat(maildir_name,user,MAX_USERNAME_LENGTH);
strcat(maildir_name,"/");
if((pthread_mutex_lock(&maildir_root_mutex))<0)
perror("ERROR on locking maildir_root_mutex");
printf("Opening directory ""%s""\n",maildir_name);
if((maildir_fd = opendir(maildir_name))==NULL)
perror("ERROR on opendir");
int file_count = 0;
/* scan over entire directory, counting number of files to that data can be properly malloced */
while(1){
if((maildir_p = readdir(maildir_fd))==NULL){
closedir(maildir_fd);
printf("breaking loop\n");
break;
}
char file[MAX_FILENAME_LENGTH+1];
strcpy(file,maildir_p->d_name);
printf("File %d: '%s'\n",file_count+1,maildir_p->d_name);
/* if the file is a file other than an email */
if(!strcmp(".",file)||!strcmp("..",file)||!strcmp("pass",file)||!strcmp(".svn",file)){
printf("Continuing without incrementing file_count\n");
continue;
}
file_count++;
}
printf("%u\n",maildir_fd);
printf("printing file_count\n");
printf("%d",file_count);
printf("file_count printed successfully");
/* Additional code OMITTED */
I came across this recently. In my instance it was that another module had declared:
int errno = 0;
as a global, instead of #including errno.h. Any code that used the "proper" errno would immediately segfault.
I'm completely new to C and I use it very rarely. This time i need it for a university project. I have to write a small c app that tests some modifications we made on the Linux kernel (on the scheduler).
Inside the script I'd like to switch to another user to see the distribution of CPU times among the different users. So I start my small C prog with root rights (i.e. with sudo ./myapp). Inside the prog - after I performed some operations which need root rights - I would like to switch back to another uid by calling seteuid(1000) or setuid(1000) where 1000 is the ID of an existing user (the one I used to log on). However the call doesn't seem to have any effect, it doesn't throw any exception neither.
Here's a sample I wrote, just to test the uid switching:
#define _POSIX_SOURCE
#include <pwd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <unistd.h>
#include <string>
#include <time.h>
using namespace std;
int main(int argc, char *argv[])
{
int uid;
struct passwd *p;
if ((p = getpwuid(uid = getuid())) == NULL){
perror("getpwuid() error");
exit(1);
}
printf("***************************************\n");
printf("Executing user: %s (%d)\n", p->pw_name, p->pw_uid);
printf("***************************************\n");
seteuid(1000);
if ((p = getpwuid(uid = getuid())) == NULL){
perror("getpwuid() error");
exit(1);
}
printf("***************************************\n");
printf("Executing user: %s (%d)\n", p->pw_name, p->pw_uid);
printf("***************************************\n");
return 0;
}
Does anyone know why it won't work?? Any help is highly appreciated! Thx
//Edit:
Corrected code as mentioned by chsh
I think it is working just fine, there's just a problem with the logic in the code because you're capturing the value of getuid() into the passwd struct, and then just displaying it twice without retrieving it again after calling seteuid().