How do I check if a file has a certain mode in Rust? - rust

I'd expect this to work:
use std::fs::OpenOptions;
use std::os::unix::fs::{OpenOptionsExt, PermissionsExt};
const MODE: u32 = 0o700;
fn main() {
let f = OpenOptions::new()
.write(true)
.create_new(true)
.mode(MODE)
.open("myfile")
.unwrap();
let f_mode = f.metadata().unwrap().permissions().mode();
assert_eq!(f_mode, MODE);
}
When run, I get:
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `33216`,
right: `448`', src/main.rs:14:5
If I check the output of ls:
$ ls -al myfile
-rwx------ 1 edd edd 0 Apr 26 14:50 myfile
Clearly there's some other information encoded in the mode field once it gets committed to the file-system.
Is there a good way to check if the file is -rwx------ besides using bitwise operators on underlying the octal representation (masking off the irrelevant parts)?

If you are going to use the low-level primitives of OS-specific permissions, you need to deal with those details:
#define S_IFMT 0170000 /* type of file */
#define S_IFIFO 0010000 /* named pipe (fifo) */
#define S_IFCHR 0020000 /* character special */
#define S_IFDIR 0040000 /* directory */
#define S_IFBLK 0060000 /* block special */
#define S_IFREG 0100000 /* regular */
#define S_IFLNK 0120000 /* symbolic link */
#define S_IFSOCK 0140000 /* socket */
#define S_IFWHT 0160000 /* whiteout */
#define S_ISUID 0004000 /* set user id on execution */
#define S_ISGID 0002000 /* set group id on execution */
#define S_ISVTX 0001000 /* save swapped text even after use */
#define S_IRUSR 0000400 /* read permission, owner */
#define S_IWUSR 0000200 /* write permission, owner */
#define S_IXUSR 0000100 /* execute/search permission, owner */
When you get the mode, you also get information on what kind of file it is. Here, you have S_IFREG | S_IRUSR | S_IWUSR | S_IXUSR.
Doing a bitwise AND is the simplest fix:
assert_eq!(f_mode & 0o777, MODE);
Of course, you can create your own accessor functions in an extension trait and implement them to have nice meaning, or there may be a crate which has already done so.

Related

How to force g++ to respect #define _POSIX_C_SOURCE 200809L

I need to use strerror_r to translate error numbers into human readable messages compiled with g++ on Linux Debian Bullseye. The man page notes:
int strerror_r(int errnum, char *buf, size_t buflen);
/* XSI-compliant */
char *strerror_r(int errnum, char *buf, size_t buflen);
/* GNU-specific */
strerror_r():
The XSI-compliant version is provided if:
(_POSIX_C_SOURCE >= 200112L) && ! _GNU_SOURCE
Otherwise, the GNU-specific version is provided.
We have two different types of the return value: int or char* depending on the version defined by _POSIX_C_SOURCE. I have this small test program:
~$ cat strerror_r.c
#include <string.h>
#include <stdio.h>
// #define _POSIX_C_SOURCE 200112L
// #undef _GNU_SOURCE
#define ERROR_BUFFER_LEN (size_t)256
int main(int argc, char **argv)
{
#if _POSIX_C_SOURCE < 200112L
char* ret;
#else
int ret;
#endif
char errorBuffer[ERROR_BUFFER_LEN];
int errno;
errno = 0;
ret = strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
fprintf(stderr, "Error message by pointer = '%s'\n", ret);
fprintf(stderr, "Content of errorBuffer = '%s'\n", errorBuffer);
return 0;
}
If I compile it with gcc everything is as expected:
$ gcc strerror_r.c && ./a.out; rm a.out
Error message by pointer = '(null)'
Content of errorBuffer = 'Success'
If I compile it with g++ I get this:
$ g++ strerror_r.c && ./a.out; rm a.out
strerror_r.c: In function ‘int main(int, char**)’:
strerror_r.c:24:21: error: invalid conversion from ‘char*’ to ‘int’ [-fpermissive]
24 | ret = strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
| ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| char*
rm: cannot remove 'a.out': No such file or directory
If I try to force the needed version by uncommenting
#define _POSIX_C_SOURCE 200112L
#undef _GNU_SOURCE
I get:
$ g++ strerror_r.c && ./a.out; rm a.out
strerror_r.c:7: warning: "_POSIX_C_SOURCE" redefined
7 | #define _POSIX_C_SOURCE 200112L
|
In file included from /usr/include/x86_64-linux-gnu/bits/libc-header-start.h:33,
from /usr/include/string.h:26,
from strerror_r.c:3:
/usr/include/features.h:281: note: this is the location of the previous definition
281 | # define _POSIX_C_SOURCE 200809L
|
strerror_r.c: In function ‘int main(int, char**)’:
strerror_r.c:24:21: error: invalid conversion from ‘char*’ to ‘int’ [-fpermissive]
24 | ret = strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
| ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| char*
rm: cannot remove 'a.out': No such file or directory
What I'm missing here? Why g++ does not compile the default thread save version of strerror_r? I need that version. How can I fix it?
Reference
Feature Test Macros
You need to specify the #define and #undef directives before you include any header files, so the first few lines should look like this:
#define _POSIX_C_SOURCE 200112L
#undef _GNU_SOURCE
#include <string.h>
#include <stdio.h>
That's because those header files or internal header files they include need those values defined to choose the proper variant. If you define them after including the headers, the headers don't see the right values and they don't include the version you want.
Often people specify these values on the command like with the -D and -U arguments so they are always specified before header files are included.

Where is the serial port flag CRTSXOFF on Linux?

I'm trying to port some Solaris serial port code to Linux, however the XOn / XOff symbol typically found in termios.h seems to be missing.
// ...
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
// ...
int config_port(int fd, int timeout)
{
struct termios options; /* Holds the port option flags */
int status; /* Holds return value of system calls */
int min_chars; /* Holds the minimum number of characters to read
* before returning. */
// ...
options.c_cflag &= ~( CRTSXOFF | CRTSCTS ); // <<-- HERE
Of course, Linux GCC doesn't know CRTSXOFF:
# gcc -c -g serial.c
serial.c: In function ‘config_port’:
serial.c:125:25: error: ‘CRTSXOFF’ undeclared (first use in this function)
125 | options.c_cflag &= ~( CRTSXOFF | CRTSCTS );
On Solaris, this symbol is certainly defined in termios.h:
# find /usr/include -iname \*.h -exec grep CRTSXOFF {} /dev/null \;
/usr/include/sys/termios.h:#define CRTSXOFF 010000000000
But on Linux the same command finds me nothing.
Is this symbol called something else on Linux?
Found it: under Linux CRTSXOFF is implemented as IXOFF.

Linux read() system call takes longer than my expectation ( serial port programming )

I am trying to read data sent from the tty/USB0 and print it out with byte format.
Question:
I expect to print out the data once the reading bytes reach 40 However, the time takes much longer than I expect. The read() system call hangs and I believe the data should already be larger than 40. The data will finally be printed out but it should not take so long. Did I make anything wrong in this programming ?
thanks
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#define BAUDRATE B9600
#define MODEMDEVICE "/dev/ttyUSB0"
#define FALSE 0
#define TRUE 1
main()
{
int fd,c, res;
struct termios oldtio,newtio;
unsigned char buf[40];
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
if (fd <0) {perror(MODEMDEVICE); exit(-1); }
tcgetattr(fd,&oldtio);
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR | ICRNL;
newtio.c_oflag = 1;
newtio.c_lflag = ICANON;
tcflush(fd, TCIOFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
int i;
while (1) {
res = read(fd,buf,40);
if(res==40){
printf("res reaches 40 \n");
}
printf("res: %d\n",res);
for(i=0;i<res;++i){
printf("%02x ", buf[i]);
}
return;
}
}
--------------------raw mode code------------------------
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#define BAUDRATE B9600
#define MODEMDEVICE "/dev/ttyUSB0"
#define _POSIX_SOURCE 1 /* POSIX compliant source */
#define FALSE 0
#define TRUE 1
volatile int STOP=FALSE;
main()
{
int fd,c, res;
struct termios oldtio,newtio;
unsigned char buf[255];
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
if (fd <0) {perror(MODEMDEVICE); exit(-1); }
tcgetattr(fd,&oldtio); /* save current port settings */
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
/* set input mode (non-canonical, no echo,...) */
newtio.c_lflag = 0;
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 40;
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
int i;
while (STOP==FALSE) {
res = read(fd,buf,255);
for( i=0;i<res;++i){
printf("%02x \n", buf[i]);
}
}
tcsetattr(fd,TCSANOW,&oldtio);
}
It now can print out the data once buffer capacity is full ( which is 40 ).
1 question:
When I modified the printf
printf("%02x ", buf[i]); ( remove "\n" )
It will not print out when the buffer is full until more bytes are received. Why this happens?
Thanks
You need to switch the terminal to raw mode to disable line buffering.
Citing this answer:
The terms raw and cooked only apply to terminal drivers. "Cooked" is
called canonical and "raw" is called non-canonical mode.
The terminal driver is, by default a line-based system: characters are
buffered internally until a carriage return (Enter or Return) before
it is passed to the program - this is called "cooked". This allows
certain characters to be processed (see stty(1)), such as Cntl-D,
Cntl-S, Ctrl-U Backspace); essentially rudimentary line-editing. The
terminal driver "cooks" the characters before serving them up.
The terminal can be placed into "raw" mode where the characters are
not processed by the terminal driver, but are sent straight through
(it can be set that INTR and QUIT characters are still processed).
This allows programs like emacs and vi to use the entire screen more
easily.
You can read more about this in the "Canonical mode" section of the
termios(3) manpage.
See e.g. this or this how to achieve that programmatically (did not check the code, but it should be easy to find it).
Alternatively you could use e.g. strace or ltrace to check what stty -F /dev/ttyUSB0 raw does (or read the manual page where it is described).
EDIT>
Regarding printf without a newline -- fflush(stdout); immediately after it should help (another line-buffering is taking place).
You might consider reading this and maybe this.

How do I read data from bar 0, from userspace, on a pci-e card in linux?

On windows there is this program called pcitree that allows you to set and read memory without writing a device driver. Is there a linux alternative to pcitree that will allow me read memory on block 0 of my pcie card?
A simple use case would be that I use driver code to write a 32bit integer on the first memory address in block zero of my pci-e card. I then use pcitree alternative to read the value at the first memory address of block zero and see my integer.
Thank you
I found some code online that does what I want here github.com/billfarrow/pcimem.
As I understand it this link offers code that maps kernel memory to user memory via the system call "mmap"
This was mostly stolen from the readme of the program, and the man pages of mmap.
mmap takes
a start address
a size
memory protection flags
file descriptor that that is linked to bar0 of your pci-card.
and an offset
mmap returns a userspace pointer to the memory defined by the start address and size parameters.
This code shows an example of mmaps usage.
//The file handle can be found by typing "lspci -v "
// and looking for your device.
fd = open("/sys/devices/pci0001\:00/0001\:00\:07.0/resource0", O_RDWR | O_SYNC);
//mmap returns a userspace address
//0, 4096 tells it to pull one page
ptr = mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
printf("PCI BAR0 0x0000 = 0x%4x\n", *((unsigned short *) ptr);
I use the way to get PCI BAR0 register described above but get the segmentation fault back. I use gdb to debug the error from my code as follows and it shows the return value of mmap() is (void *) 0xffffffffffffffff
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
#define PRINT_ERROR \
do { \
fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \
__LINE__, __FILE__, errno, strerror(errno)); exit(1); \
} while(0)
#define MAP_SIZE 4096UL
#define MAP_MASK (MAP_SIZE - 1)
int main(int argc, char **argv) {
int fd;
void *ptr;
//The file handle can be found by typing lscpi -v
//and looking for your device.
fd = open("/sys/bus/pci/devices/0000\:00\:05.0/resource0", O_RDWR | O_SYNC);
//mmap returns a userspace address
//0, 4096 tells it to pull one page
ptr = mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
printf("PCI BAR0 0x0000 = 0x%4x\n", *((unsigned short *) ptr));
if(munmap(ptr, 4096) == -1) PRINT_ERROR;
close(fd);
return 0;
}
On a system with functioning /dev/mem in the kernel it is possible to read a bar for a device using:
sudo dd if=/dev/mem skip=13701120 count=1 bs=256 | hexdump
Look at the dd man page. In the above example 13701120 * 256 is the start physical address at which 256 bytes will be read.

linux file attributes

Use open syscall to write and create a file ,there's no attributes with the file. fedora16 gcc-4.6.3
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
char * str= "helloworld";
int fd = open("test.db",O_WRONLY|O_CREAT|O_TRUNC|O_APPEND);
write(fd,str,10);
close(fd);
return 0;
}
ll test.db
----------. 1 jiamo jiamo 14 Apr 17 11:34 test.db
While it don't create file with the default file attributes such like touch test.db
umask : 0002
if drop the O_TRUNC
int fd = open("test1.db",O_WRONLY|O_CREAT|O_APPEND)
the file attributs is :
----rwx---. 1 jiamo jiamo 14 Apr 17 12:29 test1.db
Add the required permissions to the open() syscall:
int fd = open("test.db",O_WRONLY|O_CREAT|O_TRUNC|O_APPEND, 0666);
From the documentation:
mode must be specified when O_CREAT is in the flags, and is ignored otherwise.
The argument mode specifies the permissions to use in case a new file is created.
You need to pass the mode to open. Then it will set the permissions too. open is a variable argument function and you can pass more arguments to it
int open(const char *path, int oflag, ... );
Do something like
open(LOCKFILE, O_WRONLY | O_CREAT | O_EXCL,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
Check the various permission bits here

Resources