How to detect opening of a file using a C program?
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
How to detect opening of a file using a C program?
Can anyone tell me how to detect if a file is being opened by any application so that i can block it just before opening it...
Actually i am working on android and using iNotify in JNI to detect the OPENING of a file...but the file is getting opened before the iNotify detects it..and therefore its displaying the contents of the file unmodified ..
The actual requirement is that if anyone is trying to open the file it should be modified by my program and then the modified file should be opened.
Get a lease on the target file using fcntl(), and your process will receive a signal (SIGIO by default) when another process tries to open the file. The process must either be running as root, or as the same user as the owner of the file, for the lease to be granted. You have a limited time to yield the lease to let the other process continue opening the file, or the kernel will break the lease anyway.
If you have a number of files, use the Audit subsystem to filter all related syscalls. Your application then works similar to an audit daemon, and will need superuser privileges in practice. I'm not sure if the android kernels have the audit subsystem enabled or not; it is if CONFIG_AUDIT and CONFIG_AUDITSYSCALL are enabled in the kernel config.
Get a lease on the target file using fcntl(), and your process will receive a signal (SIGIO by default) when another process tries to open the file. The process must either be running as root, or as the same user as the owner of the file, for the lease to be granted. You have a limited time to yield the lease to let the other process continue opening the file, or the kernel will break the lease anyway.
If you have a number of files, use the Audit subsystem to filter all related syscalls. Your application then works similar to an audit daemon, and will need superuser privileges in practice. I'm not sure if the android kernels have the audit subsystem enabled or not; it is if CONFIG_AUDIT and CONFIG_AUDITSYSCALL are enabled in the kernel config.
-------------------------------------------------------------------------------------------------------------------------
Thanx for the answer!!
Using F_GETLEASE with fcntl(int fd, int cmd,...) needs file desriptor and we cannot get fd without opening the file...!!
My requirement is that the file should be truncated by my program before getting opened..
Is the application dynamically linked or are you providing the application and could provide a version using the mentioned wrapper? You could load a library beforehand by defining it in LD_PRELOAD and replace the call to fopen with another function, which can check the filename first and deny the access or call the real function. Yes, it’s not foolproof, but maybe an option.
If you have a recent enough kernel (2.6.37 or newer), you can use the fanotify interface instead. (Check for CONFIG_FANOTIFY in your kernel configuration.)
Here is a working example you can try. The fanotify interface was originally intended for use by virus scanners, but it should match your needs also. It is not just a notification like inotify (although both now use the same kernel code); it intercepts and either allows or disallows the access. In your case, you can just truncate the file before allowing a open-for-write to proceed.
If you have a recent enough kernel (2.6.37 or newer), you can use the fanotify interface instead. (Check for CONFIG_FANOTIFY in your kernel configuration.)
Here is a working example you can try. The fanotify interface was originally intended for use by virus scanners, but it should match your needs also. It is not just a notification like inotify (although both now use the same kernel code); it intercepts and either allows or disallows the access. In your case, you can just truncate the file before allowing a open-for-write to proceed.
Unfortunately I am working on android 2.1 with kernel 2.6.29...so fanotify won't work!!
The real issue here is that notification is not enough. You need to intercept all attempts to truncate or open the files for writing.
I'm assuming you want to let any process read the contents, but truncate the file whenever someone opens it for writing. If you wanted to truncate the files whenever anyone opens it at all, you can just unlink() the file right now, and create an empty file in its place. Any process still having the old file open can keep reading and writing to their file; the changes are just going to be discarded when the file is closed. If more than one process has the old file open, they will still see each others' changes, too.
In later kernels, fanotify would have worked well for this scenario.
You can set the LD_PRELOAD environment variable to point to a custom dynamic library, which overrides the file open functions that might have to truncate the file before returning to the actual program. Here is an example. There are a number of functions to override (at least fopen(), fopen64(), freopen(), freopen64(), open(), openat(), open64(), openat64(), __open_2(), __open64_2(), __openat_2(), __openat64_2()) from the C library, but they're most just variants of each other (with compatibility details). The library is trivial to create. If the target is one of the special files and the file open mode warrants it, the override function truncates the target file automatically.
You could also write a filesystem in user space (FUSE). The fusexmp example should be very close to what you'd need; just add the truncation logic. You can bind-mount files and directories on your filesystem to anywhere in the directory tree, so they really can exist anywhere in the directory tree. If your kernel config has CONFIG_FUSE_FS=y, then you can use FUSE. (Based on quick Googling, there seem to be other FUSEs written for Android too.)
If kernel modules are not out of question, it should not be too difficult to write a suitable Linux Security Module. After all, it only needs to intercept open() and openat() calls (since fopen() et cetera is implemented in the C library on top of those). If the mode is O_RDWR or O_WRONLY, and the filename matches (you can do a lookup or fstat() and check the device and inode numbers against a table), truncate the file (via ftruncate()).
Opening the files read-only, and getting a read lease using fcntl() (to catch file modification attempts like opening for read-write or write-only), and keeping the files open does work, but you cannot reliably truncate a file before the opener proceeds. I just tested this with a small C program. The problem is that the truncate(), ftruncate(), and open(O_TRUNC) will error with EAGAIN/EWOULDBLOCK until the lease is released, even if the same process owns the lease. When you release the lease, all openers can immediately proceed, and may be able to access the file contents before the file is truncated. Things like appending (open(O_APPEND)) may cause the old file to be replaced by zero bytes (NULs), with the appended data starting at the old size.
Mandatory file locks only block file reads and/or writes, not opening them. Even if you used a mandatory write lock, a writer would eventually write wherever they originally intended. If the file was truncated in the mean time, you just get a lot of zero bytes before that point. Also, you would need to add the mand mount option to /etc/fstab for each filesystem that may contain such files, and clear the group execute bit but set the set-group-ID bit (g-x,g+s) for each such file. Messy.
In Linux, the lease program can unlink() the file before the open() succeeds, and even replace it with a new file, but the open() will still continue with the old file, old length, and old contents. It just won't be attached to the same file name anymore. All changes will of course be lost, as the file will not be accessible by the old name anymore. (In Linux and Unix, you can unlink() an open file, but keep reading it and writing to it. Although it won't show up in file lists, the data is still stored on the disk normally, and all processes that already had it open at unlink() time can still see all changes to it. They are traditionally used for temporary files, because they will vanish when the program exists. They can be recovered by other processes belonging to the same user, by reading /proc/pid/fd/descriptor.)
Hope this helps.
Last edited by Nominal Animal; 07-21-2011 at 04:31 AM.
The real issue here is that notification is not enough. You need to intercept all attempts to truncate or open the files for writing.
I'm assuming you want to let any process read the contents, but truncate the file whenever someone opens it for writing. If you wanted to truncate the files whenever anyone opens it at all, you can just unlink() the file right now, and create an empty file in its place. Any process still having the old file open can keep reading and writing to their file; the changes are just going to be discarded when the file is closed. If more than one process has the old file open, they will still see each others' changes, too.
In later kernels, fanotify would have worked well for this scenario.
You can set the LD_PRELOAD environment variable to point to a custom dynamic library, which overrides the file open functions that might have to truncate the file before returning to the actual program. Here is an example. There are a number of functions to override (at least fopen(), fopen64(), freopen(), freopen64(), open(), openat(), open64(), openat64(), __open_2(), __open64_2(), __openat_2(), __openat64_2()) from the C library, but they're most just variants of each other (with compatibility details). The library is trivial to create. If the target is one of the special files and the file open mode warrants it, the override function truncates the target file automatically.
You could also write a filesystem in user space (FUSE). The fusexmp example should be very close to what you'd need; just add the truncation logic. You can bind-mount files and directories on your filesystem to anywhere in the directory tree, so they really can exist anywhere in the directory tree. If your kernel config has CONFIG_FUSE_FS=y, then you can use FUSE. (Based on quick Googling, there seem to be other FUSEs written for Android too.)
If kernel modules are not out of question, it should not be too difficult to write a suitable Linux Security Module. After all, it only needs to intercept open() and openat() calls (since fopen() et cetera is implemented in the C library on top of those). If the mode is O_RDWR or O_WRONLY, and the filename matches (you can do a lookup or fstat() and check the device and inode numbers against a table), truncate the file (via ftruncate()).
Opening the files read-only, and getting a read lease using fcntl() (to catch file modification attempts like opening for read-write or write-only), and keeping the files open does work, but you cannot reliably truncate a file before the opener proceeds. I just tested this with a small C program. The problem is that the truncate(), ftruncate(), and open(O_TRUNC) will error with EAGAIN/EWOULDBLOCK until the lease is released, even if the same process owns the lease. When you release the lease, all openers can immediately proceed, and may be able to access the file contents before the file is truncated. Things like appending (open(O_APPEND)) may cause the old file to be replaced by zero bytes (NULs), with the appended data starting at the old size.
Mandatory file locks only block file reads and/or writes, not opening them. Even if you used a mandatory write lock, a writer would eventually write wherever they originally intended. If the file was truncated in the mean time, you just get a lot of zero bytes before that point. Also, you would need to add the mand mount option to /etc/fstab for each filesystem that may contain such files, and clear the group execute bit but set the set-group-ID bit (g-x,g+s) for each such file. Messy.
In Linux, the lease program can unlink() the file before the open() succeeds, and even replace it with a new file, but the open() will still continue with the old file, old length, and old contents. It just won't be attached to the same file name anymore. All changes will of course be lost, as the file will not be accessible by the old name anymore. (In Linux and Unix, you can unlink() an open file, but keep reading it and writing to it. Although it won't show up in file lists, the data is still stored on the disk normally, and all processes that already had it open at unlink() time can still see all changes to it. They are traditionally used for temporary files, because they will vanish when the program exists. They can be recovered by other processes belonging to the same user, by reading /proc/pid/fd/descriptor.)
If the modifications will never change the file size -- for example, if you only modify some fixed-length string or value, like a time stamp --, you can achieve this with fcntl() leases, quite reliably.
(When the controlling program is run as a normal user, it can only do that for files owned by the same user. If it is run as root, it can do that for all files. The maximum number of files the controlling program can keep open is pretty big, sysconf(_SC_OPEN_MAX), usually 1024. So you should be able to keep leases on maybe a thousand files, I think.)
If you can describe the modifications, I could modify the GNU C program I wrote to investigate the issue, to show you exactly how it can be done.
Last edited by Nominal Animal; 07-21-2011 at 02:17 PM.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.