C File Locking behavior on windows and Linux - c

I was looking into below examples for understanding files locking on windows and linux. The program 1 is working on both windows and linux with gcc. But the second one is only working on Linux. Especially problem in winodws GCC is coming in the structure flock declaration. I dont know if I am missing any thing here. Also Even after I close and unlink the file in 1st example for the next run the file is not unlocked.
Program 1: Working on Windows with GCC
Source: http://www.c.happycodings.com/Gnu-Linux/code9.html
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
if((fd = open("locked.file", O_RDWR|O_CREAT|O_EXCL, 0444)) == -1)
{
printf("[%d]: Error - file already locked ...\n", getpid());
}
else
{
printf("[%d]: Now I am the only one with access :-)\n", getpid());
close(fd);
unlink("locked.file");
}
Program 2: Working on Linux with GCC
Source: http://beej.us/guide/bgipc/output/html/multipage/flocking.html
#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);
return 0;
}
Can you please help with this and if possible provide guidelines for portable code in these scenarios?

It will likely be difficult to get protabiltiy with this kind of operation using the C Runtime LIbrary. You really need to use OS specific code for this kind of thing.
But, you may be able to get this to work by inspecting and understanding the underlying C Runtime Library implimentations. The source code to both the GCC run times and the Microsofot run times come with the tools. Just go look and see how they are implimented.
Note that, on Windows, you can use the CRT file I/O APIs with Windows handles. Just go look at the source.

I would look into XPDEV specifically the file wrapper methods... they implement reasonable cross-platform locking.

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
}

sem_unlink(): permission denied on macOS

I am working on a school assignment using semaphores, every time I restart my mechine everything works as expected but after running my code, the second try gives undefined results. I've come to a conclusion that my machine don't give me permission to unlink any created semaphores, and checking the errno showed that my conclusion was correct. It gives errno EACCES, how can I fix this problem? I am working on the last visions of Xcode (12.2).
The code I am running:
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <fcntl.h> /* For O_* constants */
#include <errno.h>
const char *semName1 = "my_sema1";
const char *semName2 = "my_sema2";
int main(int argc, char **argv)
{
pid_t pid;
sem_t *sem_id1 = sem_open(semName1, O_CREAT, O_RDWR, 1);
sem_t *sem_id2 = sem_open(semName2, O_CREAT, O_RDWR, 0);
int i, status;
pid = fork();
if (pid) {
for (i = 0; i < 100; i++) {
sem_wait(sem_id1);
putchar('A'); fflush(stdout);
sem_post(sem_id2);
}
sem_close(sem_id1);
sem_close(sem_id2);
wait(&status);
int error = sem_unlink(semName1);
int hej2 = sem_unlink(semName2);
printf("%d \n",error);
if (errno == EACCES){
printf("%d \n",error);
}
} else {
for (i = 0; i < 100; i++) {
sem_wait(sem_id2);
putchar('B'); fflush(stdout);
sem_post(sem_id1);
}
sem_close(sem_id1);
sem_close(sem_id2);
}
}
Appreciate your help
Quick Answer: Run the program as root
Long Answer:
Given this code snippet:
#include <errno.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
sem_t *sem_id1 = sem_open("sem_id1", O_CREAT, O_RDWR, 0);
if (sem_id1 == SEM_FAILED)
{
perror("sem_open");
exit(1);
}
printf("sem_open: Successful\n");
int rc = sem_unlink("sem_id1");
if (rc != 0)
{
perror("sem_unlink");
exit(1);
}
printf("sem_unlink: Successful\n");
return 0;
}
The first time it's run, it will let you create the semaphore, but fail to unlink it:
./main
sem_open: Successful
sem_unlink: Permission denied
The second time it will fail even to create the semaphore:
./main
sem_open: Permission denied
The issue is clearly with permissions, and running the program as root fixes it:
sudo ./main
sem_open: Successful
sem_unlink: Successful
Why does it need root privileges?
Quoting from the man page:
POSIX semaphores come in two forms: named semaphores and unnamed semaphores.
...
Persistence
POSIX named semaphores have kernel persistence: if not removed by sem_unlink(3), a semaphore will exist until the system is shut down.
These are named semaphores.
The fn sem_open and sem_unlink are possibly affecting processes that don't belong to you: imagine if you could run a program that overwrites or removes another process' semaphore without root permissions!

Issue with CTRL+D on Windows Subsystem for Linux

I was working on an assignment using Windows Subsystem for Linux. Below is the C Code that was used to write a mini-shell for this assignment.
I ran into an interesting issue using WSL. On line 35, you can see that I call the read function to read in the buffer and it does a check for null. When pressing Ctrl+D, while using WSL, it will go into the if statement and prints the print message on line 36 infinitely and does not stop until I used Ctrl+C to exit. When running this program on a Linux machine, it behaves appropriately and prints once, and brings us to the top of the loop.
Any ideas as to what this bug could be?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
#include <sys/types
#include <unistd.h>
#include <error.h>
char prompt[] = "$ ";
static int
Fork()
{
pid_t pid;
if ((pid = fork()) < 0)
error(EXIT_FAILURE, errno, "fork error");
return(pid);
}
int
main(void)
{
long MAX = sysconf(_SC_LINE_MAX);
char buf[MAX];
pid_t pid;
int status, n;
do {
write(STDOUT_FILENO, prompt, strlen(prompt));
fflush(NULL);
memset(buf, 0, MAX);
if((n = read(STDIN_FILENO, buf, MAX)) == 0) {
printf("use exit to exit shell\n");
continue;
}
buf[strlen(buf) - 1] = '\0'; // chomp '\n'
if (strncmp(buf, "exit", MAX) == 0) { // match
break;
}
pid = Fork();
if (pid == 0) { // child
execlp(buf, buf, (char *)NULL);
error(EXIT_FAILURE, errno, "exec failure");
}
// parent
if ((pid = waitpid(pid, &status, 0)) < 0)
error(EXIT_FAILURE, errno, "waitpid error");
} while(1);
exit(EXIT_SUCCESS);
}
The program is in C but there are no options available to insert C Code snippets.
Documentation on read() (Linux manpages v 3.54) does not specify that end of file (ctrl/D) causes read to return anything besides of 0. On the contrary, it says that return value zero indicates end of file. So you're relying upon undefined behavior.
Somehow on your Linux ctrl/D causes error, thus read() returns -1. Your program in this case exits the loop. Or, ctrl/D is read literally, then read() returns 1.
Different OSs use different keystrokes for EOF.

Named pipes without child process

I used a FIFO for a simple read/write programme where the input from user is written to standard output by the writer function. The question is however, am I able to run this program without creating a child process (with the fork() operation). From what I see from examples about FIFOs, most read/write programmes with a named pipe/FIFO are done with 2 files - one for reading and one for writing. Could I do these all in a file?
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
/* read from user */
void reader(char *namedpipe) {
char c;
int fd;
while (1) {
/* Read from keyboard */
c = getchar();
fd = open(namedpipe, O_WRONLY);
write(fd, &c, 1);
fflush(stdout);
}
}
/* writes to screen */
void writer(char *namedpipe) {
char c;
int fd;
while (1) {
fd = open(namedpipe, O_RDONLY);
read(fd, &c, 1);
putchar(c);
}
}
int main(int argc, char *argv[]) {
int child,res;
if (access("my_fifo", F_OK) == -1) {
res = mkfifo("my_fifo", 0777);
if (res < 0) {
return errno;
}
}
child = fork();
if (child == -1)
return errno;
if (child == 0) {
reader("my_fifo");
}
else {
writer("my_fifo");
}
return 0;
}
You'll need to put a lock on the file, or else you could attempt to be reading when someone else is writing. You'll also want to flush the write buffer, or your changes to the fifo might actually not be recorded until the kernel write buffer fills and then writes to the file (in linux, write doesn't guarantee a write happens at that exact moment. i see you're flushing stdout, but you should also fsync on the file descriptor. This will cause the file to lock during any write operation so that no one else can write. In order to lock the file for reading, you might have to use a semaphore.

Resources