What is the `0` equivalent oflag in <fcntl.h>? - c

To open a semaphore, I've seen the following expression:
sem_t semaphore = sem_open("/sempath", 0);
The man page says that that integer at the end is the "oflag" and that I should read more about the oflags in fcntl.h but I can't figure out what oflag the number 0 get's mapped to.
What does the 0 mean in the code above? Is it O_RDWR?
More generally: What numbers do the flags in fcntl.h get mapped to and how can I find them?

What does the 0 mean in the code above? Is it O_RDWR?
0 means no flags. The flags argument passed to sem_open and similar routines, such as open is the logical OR of single bits or bit-fields that are defined by the various symbols documented for these routines. When there are no flags, no bits are ORed into the argument, so its value should be the identity element of the OR operation, which is zero.
What numbers do the flags in fcntl.h get mapped to and how can I find them?
You can find them in fcntl.h or the files it includes. Or, more easily, you can write a program that prints them:
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <fcntl.h>
int main(void)
{
printf("O_CREAT is 0x%" PRIxMAX ".\n", (uintmax_t) O_CREAT);
printf("O_EXCL is 0x%" PRIxMAX ".\n", (uintmax_t) O_EXCL);
}
However, you should not rely on these symbols having particular values, especially across different POSIX implementations.

Related

Error in the systemcalls C11 for do file i/o

Hi I'm on Linux WSL Debian and I've the following code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char *argv[]) {
int fd = open("file.dat", O_RDONLY | O_WRONLY);
write(fd, "ciao", strlen("ciao"));
close(fd);
}
Why don't works?
Many bugs:
Must include unistd.h for the declaration of write.
O_RDONLY and O_WRONLY (and O_RDWR) are mutually exclusive. If you want to open a file for read and write you have to use O_RDWR. Since you are not actually reading from the file, you should use O_WRONLY alone.
It is very rare to want to use O_WRONLY without O_CREAT and one (exactly one; these three are also mutually exclusive) of O_APPEND, O_EXCL, and O_TRUNC. Assuming that you do want O_CREAT, you must also supply a third argument, which should be the octal constant 0666 unless you have a specific reason to use some other value.
write(fd, "ciao", strlen("ciao")) should be write(fd, "ciao\n", strlen("ciao\n")) unless you have a specific reason to be creating a text file with an incomplete last line.
Because you are writing to a file, you need to check for errors on all three of the open, write, and close calls. (The fact that close can fail is a bug in its specification, but one that we are permanently stuck with. It's safe to ignore errors in close for files that were opened for reading, but not writing.)
Also some style corrections:
The code as shown only needs unistd.h, fcntl.h, and string.h; it should also be including stdio.h, because it should also be making calls to perror. None of the other headers should be included (unless this is cut down from a much larger program).
Declare main as int main(void) unless you are actually going to use argc and argv.
Don't use the C99 license to fall off the end of main; the last line of main should be return 0;.
For historical reasons, in C, the opening curly brace of a function definition should always be placed on a line by itself, even if all other opening curly braces are "cuddled" with their parent control flow construct.
You need the header <unistd.h> to get the declarations of write() and close(). The only other header you need is <fcntl.h> for open().
I've also kept <stdio.h> so I can use perror() if open() fails.
Since you're only writing to the file, you don't need O_RDONLY in the open modes. If you want to read and write, you should use O_RDWR instead. These three flags are mutually exclusive.
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
int fd=open("file.dat",O_WRONLY);
if (fd < 0) {
perror("open failed");
return 1;
}
write(fd,"ciao",strlen("ciao"));
close(fd);
}
Note that this will not create the file if it doesn't already exist. If you want that, you need to add the O_CREAT flag and another argument with the permissions that should be given to the file if it's created.

Is ftruncate() always precise?

I am trying to ftruncate a shared memory object to a specific length. For example, I want to set its length to 1 byte using the following snippet:
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
int main() {
struct stat fd_stat;
int fd;
fd = shm_open("NAME", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
fstat(fd, &fd_stat);
printf("Size before: %lld\n", fd_stat.st_size);
ftruncate(fd, 1);
fstat(fd, &fd_stat);
printf("Size after: %lld\n", fd_stat.st_size);
}
Which in Ubuntu 20.04 prints:
Size before: 0
Size after: 1
That's the output I'd expect.
However, in macOS X Big Sur I get:
Size before: 0
Size after: 4096
As you see, it seems to be expanding the size to the size of a page.
The ftruncate Linux man page reads:
The truncate() and ftruncate() functions cause the regular file named by path or referenced by fd to be truncated to a size of precisely length bytes.
Nonetheless, the POSIX specification is not as specific (pun intended):
If fildes refers to a regular file, the ftruncate() function shall cause the size of the file to be truncated to length. [...] If the file previously was smaller than this size, ftruncate() shall increase the size of the file.
Does that mean that ftruncate always sets the length to exactly the specified number of bytes? If indeed it does, it would entail that macOS X Big Sur is not fully POSIX-compliant (even though it is certified to be so). If not, how can I guarantee that it truncates fd to the size I want?
In short, you cannot get a guarantee that your shared memory object will be of precisely the size you ask ftruncate to be. That's because, as #user3386109 said, "The portion of the POSIX spec that you quoted starts with "If fildes refers to a regular file"".
If you want to constrain yourself to an arbitrary length, you can always use an auxiliary variable to keep track of the size you assume it to be (even if the actual size might actually differ, which may not be that important after all). Your code would look like:
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
int main() {
struct stat fd_stat;
int fd;
off_t fd_size;
fd = shm_open("NAME", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
fstat(fd, &fd_stat);
printf("Size before: %lld\n", fd_stat.st_size);
fd_size = 1;
ftruncate(fd, fd_size);
fstat(fd, &fd_stat);
printf("Actual size: %lld\n", fd_stat.st_size);
printf("Perceived size: %lld\n", fd_size);
}
On top of that, if you want to share the size among different processes, you can turn fd_size into a mmaped shared memory object to keep track of the size you assume it to be across them all.

What does SYNOPSIS part in perror man page mean?

SYNOPSIS section in perror's man page is:
#include <stdio.h>
void perror(const char *s);
#include <errno.h>
const char * const sys_errlist[];
int sys_nerr;
int errno; /* Not really declared this way; see errno(3) */
according to man page specification, SYNOPSIS section indicates that
For functions, it shows any required data declarations or #include directives, followed by the function declaration.
The following code:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *ls_args[2] = {"nonsense", NULL};
execv(ls_args[0], ls_args); // no return
perror("execve failed");
return 2;
}
outputs error message execve failed: No such file or directory, with corresponding errno being 2
since errno is a global variable (actually a macro) defined in errno.h, and errno.h header is not included, how does this code trigger errno modification?
What does #include <errno.h> ... int errno; mean in the SYNOPSIS section? seems like perror() can be called without this portion of code, thanks!
According to the C standard, the macro errno is declared in errno.h and you must include errno.h explicitly if you want to write a portable program which uses errno. The manpage synopsis is telling you that. (It is not saying that you need to include errno.h in order to use perror. Sometimes the Synopsis section tells you about other related library facilities.)
Nothing in the standard specifies what the definition of the errno macro is, or exactly where in the implementation you can find any objects referenced by the expansion of that definition. The implementation of perror obviously needs to be able to access whatever object errno refers to, but since it doesn't need to be portable, it is completely unspecified how that works.
In particular, recent C standards require that the object errno refers to be thread-local, so that every thread has its own errno object. (If this were not the case, the mechanism would be essentially unusable in multithreaded code.) The precise implementation of thread-local storage is also not specified by the standard, and on a particular implementation it might be mapped onto some facility provided by the underlying operating system.

Accidently creating gigantic file with write

this is the code I am using:
#include <stdio.h>
#include <stdlib.h>
void main(){
test = creat("TEST",0751);
close(test);
test = open("TEST",2);
write(test, "123456789101112131415",21);
lseek(test,-2,2);
read(test,swap_array,2);
write(test,swap_array,2);
lseek(test,-6, 1);
write(test,"xx",2);
}
This creates an 8gb file containing instead of inserting "xx" in between the numbers as I intend. What is wrong with the code as I have it?
You have failed to include the appropriate header files. You should include, at a minimum:
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
Additionally, this will give you access to the symbolic constants required to make your code maintainable.
Here is a working version of your program:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
char swap_array[2];
int test = creat("TEST",0751);
close(test);
test = open("TEST",O_RDWR);
write(test, "123456789101112131415",21);
lseek(test,-2,SEEK_END);
read(test,swap_array,2);
write(test,swap_array,2);
lseek(test,-6, SEEK_CUR);
write(test,"xx",2);
return 0;
}
You probably aren't including the headers, so there's probably no prototype for lseek() in scope, so the 2 in the middle argument is an int, but lseek() expects (a long) an off_t, and you're on a 64-bit machine where sizeof(int) != sizeof(long) so the two 2's are misinterpreted by the system call (maybe treating them as the offset and for whatever reason what's left on the stack is interpreted as SEEK_SET or otherwise jumps to a large offset).
Basically, you're probably passing inaccurately typed information to the system call because you haven't included the correct headers. Classically (before POSIX interfered), the middle 2 would be 2L because the l in lseek() stood for 'long' — prior to that, there was a seek() which took a plain int (in the days of 16-bit int types) — to ensure that a long is passed as lseek()
requires. These days, lseek() requires an off_t; using the prototype is crucial to ensure that what you write is interpreted correctly as an off_t.
There's a lot of UB lurking in that description, but the fact that you're not using SEEK_SET etc raises warning flags. Also, why is the file executable? That doesn't look like executable code you're writing.
This variant of the code is more careful (and doesn't create an 8 GiB file on Mac OS X 10.11.6 with GCC 6.2.0).
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#define MODE_0751 (S_IRWXU | S_IRGRP | S_IXGRP | S_IXOTH)
int main(void)
{
// All error checking omitted
char swap_array[2];
int test = creat("TEST", MODE_0751);
close(test);
test = open("TEST", O_RDWR);
write(test, "123456789101112131415", 21);
lseek(test, -2L, SEEK_END);
read(test, swap_array, 2);
write(test, swap_array, 2);
lseek(test, -6L, SEEK_CUR);
write(test, "xx", 2);
close(test);
return 0;
}
I use the L's out of (very) old habit, but they're optional. The various 2's could be replaced by sizeof(swap_array) (and sizeof("xx")-1). I learned C long enough ago that the octal permissions were the only way of doing business; the S_IRWXU and related names were years in the future. I find a 4-digit or 5-digit octal number more readable than a string of S_Iwxyz names.
Make sure you compile your code with options set so that prototypes are required before you can use functions. For example, I use:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes \
> -Wstrict-prototypes -Wold-style-definition mx19.c -o mx19
$

C warning: implicit declaration of function ‘fchmod’

I have a function, createFile that uses fchmod:
int createFile(char *pFileName) {
int ret;
if ((ret = open(pFileName, O_RDWR | O_CREAT | O_TRUNC)) < 0)
errorAndQuit(2);
fchmod(ret, S_IRUSR | S_IWUSR);
return ret;
}
At the top of my file, I have the following includes:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
When compiling: the compiler spits out:
warning: implicit declaration of function ‘fchmod’
I'm including all of the correct files, yet getting this warning. The program runs fine, even with the warning.
By a happy coincidence, your question is directly answered by the feature_test_macros(7) manpage:
Specification of feature test macro requirements in manual pages
When a function requires that a feature test macro is
defined, the manual page SYNOPSIS typically includes a note
of the following form (this example from the chmod(2) manual
page):
#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);
Feature Test Macro Requirements for glibc (see
feature_test_macros(7)):
fchmod(): _BSD_SOURCE || _XOPEN_SOURCE >= 500
The || means that in order to obtain the declaration of
fchmod(2) from <sys/stat.h>, either of the following macro
definitions must be made before including any header files:
#define _BSD_SOURCE
#define _XOPEN_SOURCE 500 /* or any value > 500 */
Alternatively, equivalent definitions can be included in the
compilation command:
cc -D_BSD_SOURCE
cc -D_XOPEN_SOURCE=500 # Or any value > 500
You didn't specify what compiler or platform you're using, but on my recent Linux installation, fchmod() is defined in but guarded by a couple of #ifdefs (__USD_BSD and __USE_XOPEN_EXTENDED).
You aren't supposed to set those directly, but rather via the _FOO_SOURCE macros in . Try defining _XOPEN_SOURCE_EXTENDED or just _GNU_SOURCE and recompiling (and note that these macros enable nonstandard functionality and use of the functionality they enable may limit the portability of your code).
I have faced this error while building uml.
Just add this line in the file where this error is thrown:
#include "sys/stat.h"
I believe it will take care about adding the macros defined in the above answers.

Resources