filelock allows opening of files - c

i executed the following c code on my ubuntu machine...i have read about fcntl()'s use to lock a file and even reading will not be allowed if F_WRLCK opton is set...so i started this program and before relinquishing the lock by pressing enter i tried to open the file in two ways- by directly double clicking on file1.cpp and by running a different c program in a new terminal...both the time the file was opened...so how did fcntl() allow opening of these files when F_WRLCK is set...
int main(int argc, char *argv[])
{
struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0 };
int fd;
fl.l_pid = getpid();
if ((fd = open("/home/file1.cpp", O_WRONLY)) == -1)
{
perror("open");
exit(1);
}
if (fcntl(fd, F_SETLKW, &fl) == -1)
{
perror("fcntl");
exit(1);
}
printf("Press <RETURN> to release lock: ");
getchar();
fl.l_type = F_UNLCK; /* set to unlock same region */
if (fcntl(fd, F_SETLK, &fl) == -1)
{
perror("fcntl");
exit(1);
}
printf("Unlocked.\n");
close(fd);
return 0;
}

fcntl locks are purely advisory locks. Their only effect is to cause fcntl F_SETLK calls to block when the lock cannot be obtained. They have absolutely no effect on IO operations. It's up to your program to obtain the locks it needs before performing IO when synchronization is necessary.
This is completely analogous to using mutexes to protect objects in memory. The mutex will not prevent you from reading or writing memory addresses; it's just a protocol you use to make sure your program only reads and writes and proper times.

Related

File content and lock

I found this code on the web which is able to lock a file and it works.
/*
** lockdemo.c -- shows off your system's file locking. Rated R.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
/* l_type l_whence l_start l_len l_pid */
struct flock fl = { F_WRLCK, SEEK_SET, 0, 0, 0 };
int fd;
fl.l_pid = getpid();
if (argc > 1)
fl.l_type = F_RDLCK;
if ((fd = open("lockdemo.c", O_RDWR)) == -1) {
perror("open");
exit(1);
}
printf("Press <RETURN> to try to get lock: ");
getchar();
printf("Trying to get lock...");
if (fcntl(fd, F_SETLKW, &fl) == -1) {
perror("fcntl");
exit(1);
}
printf("got lock\n");
printf("Press <RETURN> to release lock: ");
getchar();
fl.l_type = F_UNLCK; /* set to unlock same region */
if (fcntl(fd, F_SETLK, &fl) == -1) {
perror("fcntl");
exit(1);
}
printf("Unlocked.\n");
close(fd);
}
For my use case it's not enough.
While the file is locked, I am able to append data to lockdemo.c and I'd like to prevent that.
How can I do?
Regards
You can't.
Unix file locking is strictly "advisory," meaning that it only interacts with itself. Any process can open, read, write, truncate, etc. a locked file and as long as it doesn't call one of the locking functions it won't even notice that the lock exists.
This was an intentional design decision, made many years ago -- people can and do argue about whether it was the right design decision, but it's not going to be changed now.
If you tell us more about your larger problem, we might be able to suggest alternative approaches.

Having some troubles with file locks under Linux

My English is poor so you may get confused from my description below.
In Linux, multiple processes were requesting a file lock (flock or fcntl lock), then the previous exclusive file lock was released. I think which process can gain the lock is random (not specified).
But every time I try, it always seems like in FIFO (like the following photo). (And I have already tried many times).
I want to figure out is something wrong with my code or anything else?
#include <sys/file.h>
#include <fcntl.h>
#include <string.h>
#include "tlpi_hdr.h"
char *currTime(const char *format);
int main(int argc, char *argv[])
{
int fd;
struct flock fl;
fd = open("./file", O_RDWR); /* Open file to be locked */
if (fd == -1)
errExit("open");
fl.l_len = 0;
fl.l_start = 0;
fl.l_whence = SEEK_SET;
fl.l_type = F_WRLCK;
if (fcntl(fd, F_SETLKW, &fl) == -1)
{
if (errno == EAGAIN || errno == EACCES)
printf("already locked");
else if (errno == EDEADLK)
printf("dead lock");
else
errExit("fcntl");
}
else
printf("PID %ld: have got write lock at %s\n", (long)getpid(), currTime("%T"));
sleep(atoi(argv[1]));
exit(EXIT_SUCCESS); // close fd and this cause unlock flock's lock
}

fcntl how to know which process hold lock file?

I'm new with fcntl locking and following this example to create a sample lock in linux using c code: http://www.informit.com/articles/article.aspx?p=23618&seqNum=4
I wonder how can we can print out which process hold the lock file and which process is waiting for lock. I consider using l_pid to figure out the process id which is holding the lock but i'm not sure the right way to do it.
What is the best way to print out which process is holding the lock?
As the man 2 fcntl page describes, you can use the F_GETLK to obtain the process ID that has the conflicting lock (if the conflicting lock is a process-associated one). So, for example,
/* Return 0 if descriptor locked exclusively, positive PID if
a known process holds a conflicting lock, or -1 if the
descriptor cannot be locked (and errno has the reason).
*/
static pid_t lock_exclusively(const int fd)
{
struct flock lock;
int err = 0;
if (fd == -1) {
errno = EINVAL;
return -1;
}
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
if (!fcntl(fd, F_SETLK, &lock))
return 0;
/* Remember the cause of the failure */
err = errno;
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
lock.l_pid = 0;
if (fcntl(fd, F_GETLK, &lock) == 0 && lock.l_pid > 0)
return lock.l_pid;
errno = err;
return -1;
}
Do note that fd must be open for reading and writing. I recommend using open(path, O_RDWR | O_NOCTTY) or open(path, O_WRONLY | O_NOCTTY). Closing any file descriptor to the same file will release the lock.
Some may say that re-setting the lock memebers before the second fcntl() call is unnecessary, but I'd rather err on the side of caution here.
As to how to report it, I would simply use
int fd;
pid_t p;
fd = open(path, O_RDWR | O_NOCTTY);
if (fd == -1) {
fprintf(stderr, "%s: Cannot open file: %s.\n",
path, strerror(errno));
exit(EXIT_FAILURE);
}
p = lock_exclusively(fd);
if (p < 0) {
fprintf(stderr, "%s: Cannot lock file: %s.\n",
path, strerror(errno));
exit(EXIT_FAILURE);
} else
if (p > 0) {
fprintf(stderr, "%s: File is already locked by process %ld.\n",
path, (long)p);
exit(EXIT_FAILURE);
}
/* fd is now open and exclusive-locked. */
The user can always run e.g. ps -o cmd= -p PID to see what command that is (or you can try reading /proc/PID/cmdline in Linux).
From the example code:
printf ("locking\n");
/* Initialize the flock structure. */
memset (&lock, 0, sizeof(lock));
lock.l_type = F_WRLCK;
/* Place a write lock on the file. */
fcntl (fd, F_SETLKW, &lock);
printf ("locked; hit Enter to unlock... ");
You need to change the fcntl (fd, F_SETLKW, &lock); to:
if (fcntl (fd, F_SETLK, &lock) == -1) {
printf ("File is locked by pid %i\n", lock.l_pid);
return 0;
}
The F_SETLKW command blocks if it cannot get the lock. F_SETLK will return if it cannot get the lock. Really the code should also check errno == EACCESS or errno == EAGAIN after getting the -1 return value.

Wrong write to file output order of synchronized processes?

I have the following problem.
I have two processes that are being synchronized with semaphores and the idea is this:
process 1 writes something to the txt file
process 2 writes something to the txt file
process 1 writes something to the test file
I have included this sample code that demonstrates the problem:
// semaphore names
#define NAME1 "/s1"
#define NAME2 "/s2"
int main()
{
/* semaphores for process synchronization */
sem_t *sm1;
sm1 = sem_open( NAME1, O_CREAT, 0666, 0);
sem_t *sm2;
sm2 = sem_open(NAME2, O_CREAT, 0666, 0);
/* processes*/
int proc1;
int proc2;
/* file lock struct */
struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0 };
/* create a text file */
FILE *fp;
int fd;
fp = fopen("output.txt", "w"); // create and close the file
fclose(fp);
if((fd = open("output.txt", O_RDWR)) == -1) { // open the file again to get file descriptor
perror("open");
exit(1);
}
fp = fdopen(fd, "w");
/* first process */
if ((proc1 = fork()) < 0) {
perror("fork");
exit(2);
}
else if(proc1 == 0) {
fl.l_type = F_WRLCK; // set the lock type and pid of the forked process
fl.l_pid = getpid();
if (fcntl(fd, F_SETLKW, &fl) == -1) { // lock the file before writing to it
perror("fcntl");
exit(1);
}
fprintf(fp, "proc1 - action1\n"); // write to the file
fl.l_type = F_UNLCK;
if (fcntl(fd, F_SETLK, &fl) == -1) { // unlock the file so other processes can write to it
perror("fcntl");
exit(1);
}
fprintf(stdout, "proc1 - action1\n");
sem_post(sm1); // let the second process run
sem_wait(sm2); // wait till the second process is done
// write one more thing the same way to the text file after the second process is done
fl.l_type = F_WRLCK;
fl.l_pid = getpid();
if (fcntl(fd, F_SETLKW, &fl) == -1) {
perror("fcntl");
exit(1);
}
fprintf(fp, "proc1 - action2\n");
fl.l_type = F_UNLCK;
if (fcntl(fd, F_SETLK, &fl) == -1) {
perror("fcntl");
exit(1);
}
fprintf(stdout, "proc1 - action2\n");
exit(0);
}
/* second process */
if ((proc2 = fork()) < 0) {
perror("fork");
exit(2);
}
else if(proc2 == 0) {
sem_wait(sm1); // waits for proc1 to perform it's first action
// write something to the text file and let proc1 write it's second action
fl.l_type = F_WRLCK;
fl.l_pid = getpid();
if (fcntl(fd, F_SETLKW, &fl) == -1) {
perror("fcntl");
exit(1);
}
fprintf(fp, "proc2 - action1\n");
fl.l_type = F_UNLCK;
if (fcntl(fd, F_SETLK, &fl) == -1) {
perror("fcntl");
exit(1);
}
fprintf(stdout, "proc2 - action1\n");
sem_post(sm2);
exit(0);
}
// wait for both processes to finish
waitpid(proc1, NULL, 0);
waitpid(proc2, NULL, 0);
sem_close(sm1);
sem_unlink(NAME1);
sem_close(sm2);
sem_unlink(NAME2);
return 0;
}
I have included the fprintf with stdout lines so that you can see that the output in the terminal is correct:
-proc1 - action1
-proc2 - action1
-proc1 - action2
Just like they are being synchronized. However, the output in the output.txt file is this:
-proc2 - action1
-proc1 - action1
-proc1 - action2
Why is this happening?
Also, before the process writes to the file, I always lock it so that no other process can access it and then unlock it again.
I'm not sure if I'm doing this right so I'd appreciate any advice I can get!
Thanks a lot!
You have buffered IO by default - so the buffer isn't actually flushed to the file until you close it if it is smaller output than the buffer size (otherwise you get a buffer's worth at a time when it is full... often 8kb or so). Note also that each process gets its own, separate buffer.
To fix, flush your output after printing, and before writing, fseek() to SEEK_END to ensure you are at the end of the file.
fprintf(fp, "proc1 - action1\n"); // write to the file
fflush(fp);
BTW, i didn't check for the validity of your semaphore code, just noticed you were missing this crucial piece of information. In general, however, if you are using file locking, the semaphore is redundant...
fp = fopen("output.txt", "w"); will truncate the file and is not synchronized, so process #2 may truncate the file even if process #1 has already written something to it. Same for fp = fdopen(fd, "w");
Also, you are heading for trouble if you keep the file open while the other process writes to it. At least you should SEEK to the (new) end of the file after the second process finished its writing or you may overwrite what process #2 did. Better close the file before the other process runs and reopen afterwards. This will also make sure any buffered data is flushed.

clone(2) with CLONE_FILES leak fcntl locks?

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#define __USE_GNU
#include <sched.h>
void init_lock(struct flock *f)
{
f->l_type = F_WRLCK; /* write lock set */
f->l_whence = SEEK_SET;
f->l_start = 0;
f->l_len = 0;
f->l_pid = getpid();
}
int lock(int fd, struct flock *f)
{
init_lock(f);
if(fcntl(fd, F_SETLKW, f) == -1) {
fprintf(stderr,"fcntl() failed: %s\n", strerror(errno));
return -1;
}
return 0;
}
int unlock(int fd, struct flock *f)
{
f->l_type = F_UNLCK;
if(fcntl(fd, F_SETLK, f) == -1) {
fprintf(stderr, "fcntl() failed: %s\n", strerror(errno));
return -1;
}
return 0;
}
int file_op(void *arg)
{
char buff[256];
int fd = (int) arg, n;
struct flock my_lock;
printf("Trying to get lock\n");
if(lock(fd, &my_lock) == -1) { /* lock acquired by a thread */
return -1;
}
printf("Got lock: %d\n", getpid()); /* I am printing thread id after lock() */
printf("Enter string to write in file : ");
scanf("%s", buff);
if((n=write(fd, &buff, strlen(buff))) == -1) {
fprintf(stderr, "write() failed: %s\n", strerror(errno));
}
if(unlock(fd, &my_lock) == -1) {
return -1;
}
printf("Lock Released: %d\n", getpid());
return 0;
}
int main()
{
char *stack;
int fd, i=0, cid, stacksize;
if((fd = open("sample.txt", O_CREAT | O_WRONLY | O_APPEND, 0644)) == -1) {
printf("Error in file opening\n");
exit(1);
}
stacksize = 3*1024*1024;
for(i=0; i<5; i++) {
stack = malloc(stacksize);
if((cid = clone(&file_op, stack + stacksize, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD, (void *) fd)) == -1) {
fprintf(stderr,"clone() failed: %s\n", strerror(errno));
break;
}
}
sleep(30);
close(fd);
return 0;
}
I want that every clone() will wait for lock.
But Output of this code (something like this):
Trying to get lock
Trying to get lock
Trying to get lock
Got lock: Got lock: 10287
Got lock: Got lock: 10287
Enter string to write in file : Trying to get lock
Enter string to wriGot lock: 10287
Got lock: 10287
Got lock: 10287
Enter string to write in file : Trying to get lock
Got lock: 10287
Got lock: Enter string to write in file :
But when i am removing CLONE_FILES field set from clone(2), it goes all well. Other clone threads will wait for a lock().
Output of that:
Trying to get lock
Got lock: 10311
Trying to get lock
Trying to get lock
Trying to get lock
Trying to get lock
Any other alternatives (with CLONE_FILES)? And Why this kind of behavior?
Beginner in this field.
The locking provided by flock is per process, not per thread.
From http://linux.die.net/man/2/flock (emphasis mine):
A call to flock() may block if an incompatible lock is held by another process.
Subsequent flock() calls on an already locked file will convert an existing lock to the new lock mode.
Locks created by flock() are associated with an open file table entry.
Although threads are not explicitly mentioned multiple threads share a file table entry whereas multiple processes do not. Passing CLONE_FILES to clone causes your 'processes' to share file tables.
A solution might be to call dup to make more file descriptors. From the documentation:
If a process uses open(2) (or similar) to obtain more than one descriptor for the same
file, these descriptors are treated independently by flock(). An attempt to lock the file
using one of these file descriptors may be denied by a lock that the calling process has
already placed via another descriptor.

Resources