Removing permissions on a file? C Programming - c

With chmod I can assign permissions like
chmod(file, S_IRUSR);
Is there a way to only take away the read permission from the user?
I've tried
chmod(file, !S_IRUSR);
and
chmod(file, -S_IRUSR);
Neither work.

You can't change individual permission bits using chmod(2) like you can with the command-line utility. You can only set a new complete set of permissions.
To implement a change, you need to read them first, with stat(2), toggle the desired bits from the st_mode field, and then set them with chmod(2).
The following code will remove clear the S_IRUSR bit for test.txt, and set the S_IXUSR bit. (For brevity, error checking has been omitted.)
#include <sys/stat.h>
int main(void)
{
struct stat st;
mode_t mode;
const char *path = "test.txt";
stat(path, &st);
mode = st.st_mode & 07777;
// modify mode
mode &= ~(S_IRUSR); /* Clear this bit */
mode |= S_IXUSR; /* Set this bit */
chmod(path, mode);
return 0;
}

Related

In a C program in Linux OS, how to check if a user has read permission on a file?

The function would be something like this.
int GetFilePermission(char* pcUsername, char* pcFilePath)
{
/*return TRUE if 'pcUsername' has read permission on file 'pcFilePath'.*/
}
I don't want to use stat(). Since it would be little bit long. I have to check file's permissions, it's owners and compare them in different combinations. Is there any short and simple way/trick of doing this?
Please help. Thanks in advance.
Using the access function we can get the user having a permission. See the man page of access.
int access(const char *pathname, int mode);
If you stat function, you getting all information about that file. In access we have these four only. R_OK, W_OK, X_OK and F_OK. Using this we can get easily. Return value is zero if success.
R_OK = read permission
W_OK = Write permission
X_OK = Execute permission
F_OK = file is existing.
You can use stat
Prototype for stat
#include <sys/stat.h>
int stat(const char *restrict path, struct stat *restrict buf);

Why are the file permissions changed when creating a file with the open system call on Linux?

I am creating a file with full permission (777) using the open system call, but when I do ls -l I can see only permission as (755). Could you please tell why file permission is not 777?
Code
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int main()
{
int fd;
/* Creates a file with full permission*/
fd = open("test", O_CREAT | O_RDWR | O_APPEND, 0777);
if (fd = -1)
{
return -1;
}
close(fd);
}
Output
$ ls -l
-rwxr-xr-x 1 ubuntu ubuntu 0 2012-09-19 11:55 test
There's a value maintained by the system called the umask; it is a property of the process, just like the PID (process ID) or EUID (effective user ID) is. It will be set to 022 (octal), which indicates that the system should remove the group and other write permission from files that are created.
You can call umask(0); before using open() so that the mode you specify in open() won't be altered. You should certainly do this to demonstrate that umask is the issue. However, it is generally best to let the user's choice of umask prevail — I for one get very stroppy if a program doesn't obey my umask setting; it tends not to be used again after I spot and verify the problem.
The shell also has a (built-in) command umask which you can use. The 022 value is a sensible default; most of the time, you do not want just anybody writing to your files.
The permissions of a created file are restricted by the process's current umask -- your current umask is 022, so group and world write are always disabled by default. (Which is a good thing, in most cases.) If you really want a group- and world-writable file, you will need to temporarily set umask(0) while creating this file (make sure to save the old value returned by the system call, and set it back afterwards!), or "manually" set the file's permissions using chmod().
umask will return the original value of the mask, so to reset it temporarily you just need to do
#include <sys/types.h>
#include <sys/stat.h>
mode_t old_mask = umask(0);
...
umask(old_mask);
though it would be perhaps preferable to use fchmod after opening the file - this will avoid the problem of changing process-global state:
fd = open("test", O_CREAT | O_RDWR | O_APPEND, 0777);
if (fd < 0) { ... }
int rv = fchmod(fd, 0777);
if (rv < 0) { /* fchmod errored */ }

Store st_mode (from stat) in a file and reuse it in chmod

i'm going crazy for this.
I'm trying to write an archive library which can store and extract files. An archive file looks like this:
<0,/home/user/file.txt,154,0755>
file contents (154 byte)
Each file is identified by an header ( <...> ), with four "tags" (separated by commas), file type (0 for file and 1 for directory), path, size in bytes, permissions (in octal).
I retrieve the size and the permissions with the stat system call (i'm on Linux).
My problem is that i have to convert the octal value from st_mode to a string, store it in the archive file (the fourth tag in the header),then extract it and use it with the chmod syscall.
To convert it to string i use:
char mode[6];
sprintf (mode, "%o", statr.st_mode);
and to retrieve it i use atoi, but it does not seem to work. For example, the value stored in the 4th tag is 100644, but chmod set the permissions wrong (file not readable by my user).
I'm not sure if i explained well, i wil post the whole code if is needed (but it don't think there are implementation problem, is just a problem between conversion from octal to string)
EDIT: Solved! Actually the strtol method worked, but i had forgotten to apply it to the directories too (so extracting a directory with files inside caused Segfault because of the bad folder's permission mask). Thanks everybody for help!
As far as I know, there's not a unix standard function to do this.
You will most likely have to mask st_mode to get each permissions bit that you want and translate into something that makes sense to you.
related post
There's maybe an easier way to do this, but I've including a simple example which shows how to get each bit that you are interested in. This does compile and seemed to run as expected.
st_mode.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#define SET_UID_BIT 0x4000
#define SET_GID_BIT 0x2000
#define STICKY_BIT 0x1000
// Readable USER bits defined here
#define USR_READ_BIT 0x400
#define USR_WRITE_BIT 0x200
#define USR_EXEC_BIT 0x100
// Readable GROUP bits defined here
#define GRP_READ_BIT 0x040
#define GRP_WRITE_BIT 0x020
#define GRP_EXEC_BIT 0x010
// Readable OTHER bits defined here
#define OTH_READ_BIT 0x004
#define OTH_WRITE_BIT 0x002
#define OTH_EXEC_BIT 0x001
int main()
{
// The file I'm opening
const char fp[] = "myfile.txt"; // -rw-rw-r--
struct stat stats;
// Get the stats
if ( 0 == stat( fp, &stats ) )
{
// store st_mode
mode_t mode = stats.st_mode;
// init printable_mode
unsigned int printable_mode = 0;
// Test for each permission bit in mode.
// If the bit is present, mark the corresponding bit in printable_mode
// using defined bits above.
if( mode & S_ISUID )
printable_mode |= SET_UID_BIT;
if( mode & S_ISGID )
printable_mode |= SET_GID_BIT;
if( mode & S_ISVTX )
printable_mode |= STICKY_BIT;
if( mode & S_IRUSR )
printable_mode |= USR_READ_BIT;
if( mode & S_IWUSR )
printable_mode |= USR_WRITE_BIT;
if( mode & S_IXUSR )
printable_mode |= USR_EXEC_BIT;
if( mode & S_IRGRP )
printable_mode |= GRP_READ_BIT;
if( mode & S_IWGRP )
printable_mode |= GRP_WRITE_BIT;
if( mode & S_IXGRP )
printable_mode |= GRP_EXEC_BIT;
if( mode & S_IROTH )
printable_mode |= OTH_READ_BIT;
if( mode & S_IWOTH )
printable_mode |= OTH_WRITE_BIT;
if( mode & S_IXOTH )
printable_mode |= OTH_EXEC_BIT;
// Let's see what it looks like....
printf("%04x\n", printable_mode);
printf("%x\n", mode);
}
return 0;
}
Use strtoul. For example, the following test program correctly inteprets "100644" (octal).
After the conversion, *endptr points to the first char not converted. If *endptr == 0, the whole string has been consumed, else there have been errors (for example, calling strtoul with "1234X" stops at 'X'). Note that octal 10644 equals 33188 (decimal).
#include <stdlib.h>
#include <stdio.h>
int main() {
char *s = "100644";
char *endptr;
unsigned long result = strtoul(s, &endptr, 8);
if (*endptr)
printf("conversion error!\n");
printf("result: %lu == octal %lo\n", result, result);
return 0;
}
If you write your data in octal with a leading 0 you can then read it with strtol:
char mode[8];
snprintf(mode, 8, "0%o", statr.st_mode);
strtol(mode, NULL, 0);
Alternatively you can read it with strtol supplying 8 as the base argument, in which case the leading 0 is unnecessary:
char mode[7];
snprintf(mode, 8, "%o", statr.st_mode);
strtol(mode, NULL, 8);

How to create directory with right permissions using C on Posix

I am trying to write a simple C program that creates directories (a mkdir clone.). This is what I have so far:
#include <stdlib.h>
#include <sys/stat.h> // mkdir
#include <stdio.h> // perror
mode_t getumask()
{
mode_t mask = umask(0);
umask (mask);
return mask;
}
int main(int argc, const char *argv[])
{
mode_t mask = getumask();
printf("%i",mask);
if (mkdir("trial",mask) == -1) {
perror(argv[0]);
exit(EXIT_FAILURE);
}
return 0;
}
This code creates directory with d--------- but I want it to create it with drwxr-xr-x like mkdir do? What am I doing wrong here?
You seem to be misunderstanding what umask is used for. It sets/retrieves the process's file mode creation mask, which in turn is used to turn off bits in the file mode you specify in calls like mkdir, like this (pseduo-code):
real_mode = requested_mode & ~umask
So in your code, since you pass in the value of the umask itself, you end up specifying permissions as zero, which is exactly what you see.
Instead, you should specify the permissions you want in the call to mkdir, like this:
mkdir("trial", 0755)
As Eric says, umask is the complement of the actual permission mode you get. So instead of passing mask itself to mkdir(), you should pass 0777-mask to mkdir().
Disclaimer: I extracted this answer from the OPs question. Answers should not be contained in the question itself.
Answer provided by yasar:
This is the working solution for me:
int main(int argc, const char *argv[])
{
if (mkdir("trial",0777) == -1) {
perror(argv[0]);
exit(EXIT_FAILURE);
}
return 0;
}
Setting right permissions according to umask is automatically handled. So I only needed to call mkdir with full permissions, and that gets chopped according to current umask.

"Permission denied" error when writing to a file, but only if it does not exist yet

I generally don't like asking "what's wrong with my code" questions, but this is my last hope.
I'm doing a project in which I have to write to files, and I'm trying to do it using system calls (Linux). So I'm using unistd.h, which provides the functions: int open( char* filename, int flags, int mode ) and int write( int fd, void* ptr, int numbytes ). I use write all the time with no problem, but that's normally with the standard out and standard error file descriptors.
So I use this code to open the file:
int flags = O_WRONLY;
if( !exists( "testfile2.txt" ) ) {
flags |= O_CREAT;
}
int mode = S_IROTH | S_IWOTH | S_IXOTH;
int filedesc = open( "testfile2.txt", flags, mode );
And then this line to write to the file:
int written = write(filedesc, "abcdefghijklmnopqrstuvwxyz",
strlen("abcdefghijklmnopqrstuvwxyz" ) );
And finally, I close the file with this:
int closed = close( filedesc );
The problem is that when I try to write to the file when it didn't already exist, I get a message saying "permission denied". When I open it in vi and ignore the permissions, the file appears to be empty. However, if the file existed initially, it writes to it and I can read it just fine. Does anyone have any clue what's going wrong or if I'm missing something?
Thanks in advance.
According to the docs, O_CREAT uses the permission bits only if the file does not exists. You pass a permission mask of 007, denying "owner" and "group" all rights on the file. Use at least S_IRUSR | S_IWUSR in the mode flags:
x#y:~/tmp$ touch test
x#y:~/tmp$ cat test
x#y:~/tmp$ chmod 007 test
x#y:~/tmp$ cat test
cat: test: Permission denied
You are setting the mode to give permission to others, but not yourself. Just set mode = 0777.

Resources