Using chmod in a C program - c

I have a program where I need to set the permissions of a file (say /home/hello.t) using chmod and I have to read the permissions to be set from a file. For this I first read the permissions into a character array and then try to modify the permissions of the file. But I see that permissions are set in a weird manner.
A sample program I have written:
main()
{
char mode[4]="0777";
char buf[100]="/home/hello.t";
int i;
i = atoi(mode);
if (chmod (buf,i) < 0)
printf("error in chmod");
}
I see that the permissions of the file are not set to 777. Can you please help me out on how to set the permissions of the file after reading the same from a character array.

The atoi() function only translates decimal, not octal.
For octal conversion, use strtol() (or, as Chris Jester-Young points out, strtoul() - though the valid sizes of file permission modes for Unix all fit within 16 bits, and so will never produce a negative long anyway) with either 0 or 8 as the base. Actually, in this context, specifying 8 is best. It allows people to write 777 and get the correct octal value. With a base of 0 specified, the string 777 is decimal (again).
Additionally:
Do not use 'implicit int' return type for main(); be explicit as required by C99 and use int main(void) or int main(int argc, char **argv).
Do not play with chopping trailing nulls off your string.
char mode[4] = "0777";
This prevents C from storing a terminal null - bad! Use:
char mode[] = "0777";
This allocates the 5 bytes needed to store the string with a null terminator.
Report errors on stderr, not stdout.
Report errors with a newline at the end.
It is good practice to include the program name and file name in the error message, and also (as CJY pointed out) to include the system error number and the corresponding string in the output. That requires the <string.h> header (for strerror()) and <errno.h> for errno. Additionally, the exit status of the program should indicate failure when the chmod() operation fails.
Putting all the changes together yields:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
int main(int argc, char **argv)
{
char mode[] = "0777";
char buf[100] = "/home/hello.t";
int i;
i = strtol(mode, 0, 8);
if (chmod (buf,i) < 0)
{
fprintf(stderr, "%s: error in chmod(%s, %s) - %d (%s)\n",
argv[0], buf, mode, errno, strerror(errno));
exit(1);
}
return(0);
}
Be careful with errno; it can change when functions are called. It is safe enough here, but in many scenarios, it is a good idea to capture errno into a local variable and use the local variable in printing operations, etc.
Note too that the code does no error checking on the result of strtol(). In this context, it is safe enough; if the user supplied the value, it would be a bad idea to trust them to get it right.
One last comment: generally, you should not use 777 permission on files (or directories). For files, it means that you don't mind who gets to modify your executable program, or how. This is usually not the case; you do care (or should care) who modifies your programs. Generally, don't make data files executable at all; when files are executable, do not give public write access and look askance at group write access. For directories, public write permission means you do not mind who removes any of the files in the directory (or adds files). Again, occasionally, this may be the correct permission setting to use, but it is very seldom correct. (For directories, it is usually a good idea to use the 'sticky bit' too: 1777 permission is what is typically used on /tmp, for example - but not on MacOS X.)

Related

How to read file permission bits using only the open and read system calls?

I can examine a file's permission bits using the stat() system call, which returns a struct, which contains a field that in turn contains the file type and mode. Is there a way to do the same using nothing but the open and read syscalls? I.e. by analyzing each bit? For example the following code reads a file (the first four bytes) and determines whether it's an ELF file or not ..
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd = open("main", O_RDONLY);
char *buf = malloc(sizeof (char) * 4);
read (fd, buf, 4);
if (strcmp(buf, "ELF"))
printf("It is an ELF file.\n");
free(buf);
return 0;
}
Is there a similar way to read a file to extract the information bit-by-bit?
File permissions are not part of the file's contents but part of its directory entry, therefore you can't read the permissions using open or read on the file.
Using stat is the proper way to do this.
I saw that you mentioned in another comment that you're doing this for learning purposes only. Anyone else reading this for production work...DONT. It'll be non-portable! You probably just want to use stat on the containing directory.
You're going to want to take a look at your systems definition of the stat function. Here is one example of the stat function implementation. Its definitely not as easy as just calling stat. But if you study this source and follow links in it, you'll get an idea of how it works.
Unfortunately I'm sane enough to not study the source, and am unsure if it can be done with just combinations of open and read. My guess is no, though (just a guess)

Is any operation needed prior to write(fd,...?

I wrote code below
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main() {
int fd = 3;
char c[100] = "Testing\n";
ssize_t nbytes = write(fd, (void *) c, strlen(c));
return 0;
}
compiled/linked, and executed
$ ./io
$ ./io 3> io_3.txt
The first line produced no output. The second line gave me file io_3.txt containing Testing.
This is all expected behaviour (I guess).
Even if in my tests it produced the expected output,
I am not certain if, to avoid potential problems, undefined behavior, etc., I should do anything prior to the first write, like checking if fd=3 is in use (and in that case, how... this may apply), if it is suitably open, etc.
And I am not certain if I should perform some action after the last write, for the same reasons.
Perhaps the way I did is "non-risky", the only potential issue being that nothing is written, which I could detect by checking the value of nbytes... I wouldn't know.
Any clarification is welcome.
If you write a program like this, executing it without fd 3 open is a usage bug. Normally the only file descriptors that should be used by number without having opened them yourself are 0 (stdin), 1 (stdout), and 2 (stderr). If a program needs to take additional pre-opened file descriptors as input, the standard idiom is to pass the fd numbers on the command line or environment variables rather than hard-coding them. For example:
int main(int argc, char **argv) {
if (argc<2 || !isdigit(argv[1][0])) return 1;
int fd = strtol(argv[1], 0, 0);
char c[100] = "Testing\n";
ssize_t nbytes = write(fd, (void *) c, strlen(c));
return 0;
}
In practice, a trivial program like yours is probably safe with the write just failing if fd 3 wasn't open. But as soon as you do anything that might open file descriptors (possibly internal to the implementation, like syslog, or date/time functions opening timezone data, or message translation catalogs, etc.), it might happen that fd 3 now refers to such an open file, and you wrongly attempt a write to it. Using file descriptors like this is a serious bug.

fgetws can't read non-English characters on Linux

I have a basic C program that reads some lines from a text file containing hundreds of lines in its working directory. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
#include <string.h>
#include <locale.h>
#include <wchar.h>
#include <wctype.h>
#include <unistd.h>
int main(int argc, const char * argv[]) {
srand((unsigned)time(0));
char *nameFileName = "MaleNames.txt";
wchar_t line[100];
wchar_t **nameLines = malloc(sizeof(wchar_t*) * 2000);
int numNameLines = 0;
FILE *nameFile = fopen(nameFileName, "r");
while (fgetws(line, 100, nameFile) != NULL) {
nameLines[numNameLines] = malloc(sizeof(wchar_t) * 100);
wcsncpy(nameLines[numNameLines], line, 100);
numNameLines++;
}
fclose(nameFile);
wchar_t *name = nameLines[rand() % numNameLines];
name[wcslen(name) - 1] = '\0';
wprintf(L"%ls", name);
int i;
for (i = 0; i < numNameLines; i++) {
free(nameLines[i]);
}
free(nameLines);
return 0;
}
It basically reads my text file (defined as a macro, it exists at the working directory) line by line. Rest is irrelevant. It runs perfect and as expected on my Mac (with llvm/Xcode). When I try to compile (nothing fancy, again, gcc main.c) and run it on a Linux server, it either:
Exists with error code 2 (meaning no lines are read).
Reads only first 3 lines from my file with hundreds of lines.
What causes this indeterministic (and incorrect) behavior? I've tried commenting out the first line (random seed) and compile again, it always exits with return code 2.
What is the relation between the random methods and reading a file, and why I'm getting this behavior?
UPDATE: I've fixed malloc to sizeof(wchar_t) * 100 from sizeof(wchar_t) * 50. It didn't change anything. My lines are about 15 characters at most, and there are much less than 2000 lines (it is guaranteed).
UPDATE 2:
I've compiled with -Wall, no issues.
I've compiled with -Werror, no issues.
I've run valgrind didn't find any leaks too.
I've debugged with gdb, it just doesn't enter the while loop (fgetws call returns 0).
UPDATE 3: I'm getting a floating point exception on Linux, as numNameLines is zero.
UPDATE 4: I verify that I have read permissions on MaleNames.txt.
UPDATE 5: I've found that accented, non-English characters (e.g. Â) cause problems while reading lines. fgetws halts on them. I've tried setting locale (both setlocale(LC_ALL, "en.UTF-8"); and setlocale(LC_ALL, "tr.UTF-8"); separately) but didn't work.
fgetws() is attempting to read up to 100 wide characters. The malloc() call in the loop allocates 50 wide characters.
The wcscpy() call copies all the wide characters read. If more than 50 wide characters have been read (including the terminating nul) then wcscpy() will overrun the allocated buffer. That results in undefined behaviour.
Instead of multiplying by 50 in the loop, multiply by 100. Or, better yet, compute the length of string read and use that.
Independently of the above, your code will also overrun a buffer if the file contains more than 2000 lines. Your loop needs to check for that.
A number of the functions in your code can fail, and will return a value to indicate that. Your code is not checking for any such failures.
Your code running under OS X is happenstance. The behaviour is undefined, which means there is potential to fail on any host system, when built with any compiler. Appearing to run correctly on one system, and failing on another system, is actually a valid set of responses to undefined behaviour.
Found the solution. It was all about the locale, from the beginning. After experimenting and hours of research, I've stumbled upon this: http://cboard.cprogramming.com/c-programming/142780-arrays-accented-characters.html#post1066035
#include < locale.h >
setlocale(LC_ALL, "");
Setting locale to empty string solved my problem instantly.

Reading a directory file in C

I'm trying to write a small program to show me the internal representation of a directory in linux (debian, specifically). The idea was a small C program using open(".", O_RDONLY), but this seems to give no output. The program is the following:
#include <stdio.h>
#include <fcntl.h>
int main(int argc, char** argv)
{
int fd = open(argv[1],O_RDONLY,0 );
char buf;
printf("%i\n",fd);
while(read(fd, &buf, 1) > 0)
printf("%x ", buf);
putchar('\n');
}
When I run it on regular files it works as expected, but on a directory such as ".", it gives no output. The value of fd is 3 (as expected) but the call to read returns -1.
Why isn't this working, and how could I achieve to read the internal representation?
Thanks!
For handling directories, you need to use opendir/readdir/closedir. Read the corresponding man pages for more infos.
To check whether a filename corresponds to a directory, you first need to call stat for the filename and check whether it's a directory (S_ISDIR(myStatStruc.st_mode)).
Directories are a filesystem specific representation and are part of the file system. On extfs, they are a table of string/inode pairs, unlike files which have blocks of data(that you read using your code above).
To read directory-specific information in C, you need to use dirent.h .
Look at this page for more information
http://pubs.opengroup.org/onlinepubs/7908799/xsh/dirent.h.html
On POSIX systems, the system call "stat" would give you all the information about an inode on the filesystem(file/directory/etc.)

Recover a deleted file using undelete() in C

I'm just trying to recover a file in C on an HFS+ formatted Volumn. According to
man undelete
NAME
undelete -- attempt to recover a deleted file
LIBRARY
Standard C Library (libc, -lc)
SYNOPSIS
#include
int
undelete(const char *path);
DESCRIPTION
The undelete() system call attempts to recover the deleted file named
by path. Currently, this works only when the named object is a
whiteout in a union file system. The system call removes the whiteout
causing any objects in a lower layer of the union stack to become
visible once more.
Eventually, the undelete() functionality may be expanded to other file
systems able to recover deleted files such as the log-structured file
system.
RETURN VALUES
The undelete() function returns the value 0 if successful; otherwise
the value -1 is returned and the global variable errno is set to
indicate the error.
so the Program is simple:
The current directory (pwd) is /Users/Leo/Desktop/ and I'm on a Mac 10.7.2 with HFS+ Filesystem.
#include <unistd.h>
int main()
{
char a="/Users/Leo/Desktop/test/a.out";//I delete a.out using rm for testing
const char* pa=&a;
return undelete(pa);
}
But when I run the program, I got shell returned 255.
Any idea? Thanks
undelete is failing. To find out why, check errno. For example:
#include <unistd.h>
int main( int argc, char **argv )
{
char *path = argc > 1 ? argv[ 1 ] : "a.out";
if( undelete(path))
perror( path );
return 0;
}
Although it appears your problem is that you have a char instead of a char pointer.
You should have gotten a compiler warning.
First, you need to check the return value and evaluate it. If it is -1 then print an error message, for example with perror or by formatting an error message and using strerror(errno).
But you've also got a major bug before you even attempt to call undelete:
char a="/Users/Leo/Desktop/test/a.out";
const char* pa=&a;
This will first assign a value (the pointer to your string) to a char, a single byte value. The compiler even warns about that with warning: initialization makes integer from pointer without a cast. In my case, a then had the value D/0x44, but it could have been anything. You then store the pointer to that single byte in pa. What you want instead is:
const char* pa="/Users/Leo/Desktop/test/a.out";

Resources