Setting Immutable Flag using ioctl() in C - c

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.

Related

Call to fdopendir() corrupts file descriptor

I stumbled upon a problem in a program I was working on. The following reproduces my issue:
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(int argc, char *argv[])
{
int fd, ret_fd;
DIR *dirp;
fd = open("./", O_RDONLY);
#if 1
if ((dirp = fdopendir(fd)) == NULL) {
perror("dirp");
return 1;
}
closedir(dirp);
#endif
ret_fd = openat(fd, "Makefile", O_RDONLY);
if (ret_fd == -1) {
perror("ret_fd");
return 1;
}
return 0;
}
Basically, the call to openat(), which has been preceeded by fdopendir(), fails with: Bad file descriptor. However, this does not happen if fdopendir() is omitted.
I know that fdopendir() makes internal use of the file descriptor, but shouldn't it revert any changes to it after calling closedir()?
What can I do to prevent openat() from failing in this case?
The POSIX description of fdopendir() says:
Upon calling closedir() the file descriptor shall be closed.
So the descriptor is likely to be closed by the time you call openat().
And this is from a typical Linux man page for fdopendir():
After a successful call to fdopendir(), fd is used internally by the
implementation, and should not otherwise be used by the application.

How to make a function to determine if something is a built in command for the shell I'm writing

I am wrting a shell. I need a function to determine if the command entered in the shell by
the user is a valid builtin command. I'm not sure how to go about doing this.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
int is_builtin(command_t* command) {
// TODO: Iterate through `valid_builtin_commands`
while (valid_builtin_commands[i] != NULL )
i++
if(valid_builtin_commands[i] == command){
return true
}
return -1;
}
im trying to accomplish more along these lines in limited in the libraries i can use.
I have a magic crystal ball which says:
int is_builtin(command_t* command) {
return (command->flags & CMD_BUILT_IN) != 0;
}
Try that! You might have to define flags in the command_t structure, and populate that at the time the command_t object is instantiated from parsing the command input. Also, to supply the CMD_BUILT_IN constant in some header file somewhere.

What's wrong with my code? (Printing File names inside an Archive File)

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.

write() returns -1 when writing to I2C_SLAVE device

I've read through the Linux kernel documents on i2c and written a code to try to replicate the command i2cset -y 0 0x60 0x05 0xff
The code that I've written is here:
#include <stdio.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdint.h>
#include <string.h>
int main(){
int file;
file = open("/dev/i2c-0", O_RDWR);
if (file < 0) {
exit(1);
}
int addr = 0x60;
if(ioctl(file, I2C_SLAVE, addr) < 0){
exit(1);
}
__u8 reg = 0x05;
__u8 res;
__u8 data = 0xff;
int written = write(file, &reg, 1);
printf("write returned %d\n", written);
written = write(file, &data, 1);
printf("write returned %d\n", written);
}
When I compile and run this code I get:
write returned -1
write returned -1
I've tried to follow exactly what the docs tell me, my understanding is that the address is set first with the call to ioctl, then I need to write() the register and then the data that I want sent to the register.
I've also tried to use use SMbus, but I can't get my code to compile using this, it complains at the linking stage that it can't find the functions.
Have I made any mistakes in this code? I'm a beginner to i2c and don't have a lot of experience with c either.
EDIT: errno give the following message: Operation not supported. I am logged in as root on this machine though, so I don't think it can be a permissions thing, although I may be wrong.
The way I got around this problem was to use SMBus, in particular the functions i2c_smbus_write_byte_data and i2c_smbus_read_byte_data. I was able to use these functions to successfully read and write to the device.
I did have a little trouble finding these functions, I kept trying to download libraries using apt-get to install the appropriate header files. In the end I simply downloaded the files smbus.c and smbus.h.
Then the code I needed was:
#include <stdio.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include "smbus.h"
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
int main(){
int file;
file = open("/dev/i2c-0", O_RDWR);
if (file < 0) {
exit(1);
}
int addr = 0x60;
if(ioctl(file, I2C_SLAVE, addr) < 0){
exit(1);
}
__u8 reg = 0x05; /* Device register to access */
__s32 res;
res = i2c_smbus_write_byte_data(file, reg, 0xff);
close(file);
}
Then if I compile the smbus.c file: gcc -c smbus.c and myfile: gcc -c myfile.c, then link them: gcc smbus.o myfile.o -o myexe I get a working executable that runs my I2C command. Ofcourse, I have smbus.c and smbus.h in the same directory as myfile.c.
In C, you can check the content of the errno variable to get more details into what went wrong. It is automatically declared when including errno.h and you can get a more descriptive text by calling strerror(errno).
Have you checked that you had write access to /dev/i2c-0 ?

seteuid() not working. Reason?

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().

Resources