NASM x86_64: File opening (SYS_OPEN) error list? - linux

I'm coding a linux x64 assembly program that read a file and I want to handle errors like File Not Found or permission errors.
Where can I find a list of SYS_OPEN error codes?
Approaches to find codes (kinda fun)
My code to open a file:
SYS_OPEN equ 2
O_RDONLY equ 0
section .data
filename db "file.txt", 0
section .text
global _start
_start:
mov rax, SYS_OPEN
mov rdi, filename
mov rsi, O_RDONLY
mov rdx, 0644o
syscall
[...]
When the file is successfully opened the RAX register points to the file descriptor (positive integer), if fails RAX point to an error (negative integer). I managed to raise a permission error by removing all permissions for all users:
chmod 0000 file.txt
This causes an error with code -13. By deleting the file, I managed to get error -2. Where can I find a list of SYS_OPEN error codes?
PS: Maybe my googling skills are rusty

Linux system call return values from -4095 to -1 are -errno codes. (The actual highest error number that Linux has actually defined is currently about 133, EHWPOISON, but that's the official range.)
strace ./myprog can decode them for you so you don't need to actually write error checking in your toy programs when playing around with system calls.
For example:
$ strace touch /tmp/xyjklj/bar
... (dynamic linker / process startup stuff)
openat(AT_FDCWD, "/tmp/xyjklj/bar", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666) = -1 ENOENT (No such file or directory)
utimensat(AT_FDCWD, "/tmp/xyjklj/bar", NULL, 0) = -1 ENOENT (No such file or directory)
... (more system calls as touch(1) finds a locale-specific set of error messages and prints
(The -1 is what the libc wrapper function actually returns; the errno code is what strace decoded from the asm syscall return value, which the glibc wrapper will store in errno. When using raw system calls in asm, you don't have to waste instructions doing that. But strace will still say "-1", not the numeric error code)
Documentation of most ways SYS_open can fail
Each system call man page documents which error codes that particular system call can fail with, and in which cases that can happen. (Those list aren't fully exhaustive, for example not covering weird things a specific filesystem like NFS could return, like EMULTIHOP (see comments).)
For your case, see the ERRORs section of the open(2) man page. e.g. there are several entries for ENOENT, covering all the cases which can lead to that return value.
ENOENT - O_CREAT is not set and the named file does not exist.
ENOENT - A directory component in pathname does not exist or is a
dangling symbolic link.
ENOENT - pathname refers to a nonexistent directory, O_TMPFILE and
one of O_WRONLY or O_RDWR were specified in flags, but
this kernel version does not provide the O_TMPFILE
functionality.
(Spoiler alert, 2 is ENOENT, so -2 is -ENOENT.)
There are of course lots of other fun ways that pathname and file access stuff (and open(2) in particular) can error, including:
EACCES (-13) - The requested access to the file is not allowed, or search
permission is denied for one of the directories in the
path prefix of pathname, or the file did not exist yet and
write access to the parent directory is not allowed. (See
also path_resolution(7).)
EFAULT - pathname points outside your accessible address space.
ENAMETOOLONG -
pathname was too long.
EBUSY - O_EXCL was specified in flags and pathname refers to a
block device that is in use by the system (e.g., it is
mounted).
[this would require root, otherwise you'd get EACCESS]
ETXTBSY - pathname refers to an executable image which is currently
being executed and write access was requested.
EWOULDBLOCK -
The O_NONBLOCK flag was specified, and an incompatible
lease was held on the file (see fcntl(2)).
ENODEV - pathname refers to a device special file and no
corresponding device exists. (This is a Linux kernel bug;
in this situation ENXIO must be returned.)
ELOOP - Too many symbolic links were encountered in resolving
pathname.
EISDIR - pathname refers to a directory and the access requested
involved writing (that is, O_WRONLY or O_RDWR is set).
ENOTDIR -
A component used as a directory in pathname is not, in
fact, a directory, or O_DIRECTORY was specified and
pathname was not a directory.
EPERM - The O_NOATIME flag was specified, but the effective user
ID of the caller did not match the owner of the file and
the caller was not privileged.
As well as various limits like number of open files (ENFILE, EMFILE), or ENOSPC disk space full. The above is not a complete list, I just took one each the ways to get many (but not all) of the error codes.
As per funnydman's answer, you can look up the number -> symbolic meaning of error values in man pages. Or look in /usr/include/asm-generic/errno-base.h (The full path may differ on some systems, and you'd only include this file indirectly, via #include <errno.h>)

You can interpret this as values of errno, here is the table (to list all of the codes use errno -l), also take a look at the docs. A part of the table:
number
hex
symbol
description
2
0x02
ENOENT
No such file or directory
13
0x0d
EACCES
Permission denied
There is described a reason of such decision: https://stackoverflow.com/a/6008711/9926721

Related

What does flags 02000002 mean in /proc/pid/fdinfo/fd_num?

There is a python process running on Linux version 3.2.0 and hangs on epoll_wait(6, ...) for hours, then I use cat /proc/24924/fdinfo/6 command to find more information about the file descriptor.
$ cat /proc/24924/fdinfo/6
pos: 0
flags: 02000002
I have searched linux man-pages and know that the octal number of flags displays the file access mode and file status flags, so what does 02000002 exactly means?
AFAIK octal value of 02000002 consists of two flags: O_CLOEXEC and O_RDWR.
O_CLOEXEC is the flag which means that this file descriptor will be closed in case of invoking exec function. O_RDWR means opeening for reading and writing. More description you can find in man 3 open.

Fortran OPEN-call differs on NFSv3 vs NFSv4

I'm trying to understand the difference between why you can do an OPEN-call in fortran on NFSv3 in read-write mode on a file that you only have read-permissions on, while if you do the same thing on NFSv4 the OPEN-call will fail.
Let me explain, below is a simple fortran-program that opens given file (argument to the program) in read-write mode,
PROGRAM test_open
IMPLICIT NONE
! Parameters
INTEGER, PARAMETER :: lunin = 10
CHARACTER(LEN=100) :: fname
! Local
INTEGER :: i,ierr,siteid,nstation
REAL :: lat, lon, asl
CHARACTER(len=15) :: name
!----------------------------------------------------------------
!
! Open input file
!
CALL getarg(1,fname)
OPEN(lunin,file=fname,STATUS='OLD',IOSTAT=ierr)
IF ( ierr /= 0 ) THEN
WRITE(6,*)'Could not open ',TRIM(fname),ierr
STOP
ENDIF
WRITE(6,*)'Opened OK'
CLOSE(lunin)
END PROGRAM test_open
Save the above in test_open.f90 and compile with,
gfortran -o fortran test_open.f90
Now, execute the following on a mountpoint with NFSv3,
strace -eopen ./fortran file-with-only-read-permissions
And you should see the following lines (along with a lot of other output),
> open("file-with-only-read-permissions", O_RDWR) = -1 EACCES (Permission denied)
> open("file-with-only-read-permissions", O_RDONLY) = 3
So, we can clearly see that we get an "EACCES (Permission denied)" while trying to open in 'O_RDWR' (open read-write), but right after we see another open O_RDONLY (open read-only) and that succeeds.
Run the same program on a file on a NFSv4 share, and we get the following,
strace -eopen ./fortran file-with-only-read-permissions-on-nfsv4-share
> open("file-with-only-read-permissions-on-nfsv4-share", O_RDWR) = -1 EPERM (Operation not permitted)
So, here we get an "EPERM (Operation not permitted)" while trying to open the file in 'O_RDWR' (open read-write) and nothing more (ie application fails).
Doing the same tests in C with a small test-program it will fail to open the file in both scenarios (that is, it will not try to open the file in 'read-only-mode' after getting the "EACCES" on NFSv3).
So to the questions,
I assume the above behaviour is due to the implementation of the OPEN-call in fortran, and that if fortran gets an "EACCES (Permission denied)" while trying to open a file, it will automatically try to open the file in read-only (O_RDONLY). Is this assumption correct ?
I also assume that fortran doesn't have this "fallback-method" when getting an "EPERM (Operation not permitted)" while trying to open a file. Is this assumption correct, or am I missing something ?
C doesn't seem to implement a "fallback-method" in either a "EACCES" nor "EPERM". This seems correct to me, since this doesn't leave any room for confusion. If you try to open a file in a way that you do not have the permissions to do, the program should fail - my opinion.
I am aware of that there is a distinct difference between "Permission denied" and "Operation not permitted". And I guess that when mounting NFSv4 over kerberos there is a reason for getting "Permission denied" instead of "Operation not permitted", however some clarification regarding this area would be great.
Of course, adding the appropriate flags to the open-call (ACTION=READ) solves the problem. I'm just curios about my assumptions and if they are correct.
To answer your question, in order:
You are correct that gfortran will try to reopen a file in read-only mode when EACCES (or EROFS) is encountered.
You are also correct that EPERM is not handled this way, it is not mentioned in the libgfortran source tree at all.
As you say, this is a matter of opinion. Gfortran made the decision to do this a long time ago, and it seems to suit the users just fine.
I do not understand why NFS v4 returns EPERM in such a case. This seems at odds at least with the documentation in the open(2) Linux manpage that I have access to, where it is only mentioned when O_NOATIME has been specified (which libgfortran does not do). At least, this behavior does not seem to be portable.

Where is the system call table in linux kernel?

I'm reading Linux Kernel Development by Robert Love and one of the exercises he does is to create a system call (page 106). The problem is that I am unable to find the system call table file in v3.9 for the x86_32 architecture. I know that he's using the version 2.6.xx but I don't know if that version will work with the distribution that I'm using as it is pretty old so I would rather prefer v3.9.
More information:
The exercise of which I am speaking is the following:
Add an entry to the end of the system call table.This needs to be done for each architecture that supports the system call (which, for most calls, is all the architectures).The position of the syscall in the table, starting at zero, is its system call number. For example, the tenth entry in the list is assigned syscall number nine.
Solved using the following approach:
The system call table is located in arch/x86/syscalls/syscall_32.tbl for the x86 architecture. Thanks to Sudip Mukherjee for his help.
Another approach is the following:
http://lists.kernelnewbies.org/pipermail/kernelnewbies/2013-July/008598.html
Thanks to Srinivas Ganji for his help too.
From linux kernel 4.2, the system call table has been moved from arch/x86/syscalls/syscall_64.tbl to arch/x86/entry/syscalls/syscall_64.tbl
Here is the corresponding commit:
commit 1f57d5d85ba7f1f467173ff33f51d01a91f9aaf1
Author: Ingo Molnar <mingo#kernel.org>
Date: Wed Jun 3 18:36:41 2015 +0200
x86/asm/entry: Move the arch/x86/syscalls/ definitions to arch/x86/entry/syscalls/
The build time generated syscall definitions are entry code related, move
them into the arch/x86/entry/ directory.
Create a testing folder in src root: src/linux-3.4/testing/, then put inside this folder:
- a file that contains syscall code: strcpy.c
#include <linux/linkage.h>
#include <linux/kernel.h>
asmlinkage long sys_strcpy(char *dest, char *src)
{
int i=0;
while(src[i]!='\0') {
dest[i]=src[i++];
}
dest[i]='\0';
printk(" Done it ");
return 0;
}
and the Makefile that contains just the following line:
obj-y:=strcpy.o
Add an entry to the syscall table and the prototype of the function:
- edit the file src/linux-3.4/arch/x86/syscalls/syscall_32.tbl by adding this line to the entry 223 that is free
223 i386 strcpy sys_strcpy
Edit the file src/linux-3.4/include/linux/syscalls.h by adding the prototype of the function
asmlinkage long sys_strcpy(char *dest, char *src);
Edit the main Makefile in the src root (src/linux-3.4/Makefile) by adding the testing folder created before, as follow:
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ testing/
For systems where audit is enabled, a table of the syscalls may be easily retrieved with:
ausyscall --dump
For example:
$ ausyscall --dump
Using x86_64 syscall table:
0 read
1 write
2 open
3 close
4 stat
5 fstat
6 lstat
7 poll
8 lseek
9 mmap
10 mprotect
...SNIP...
A similar question on SO where the OP seems to have solved it:
New syscall not found (linux kernel 3.0.0) where should I start looking?
The file seems to be arch/x86/kernel/syscall_table_32.c.

embedded linux: readdir() sometimes failing with EFAULT

I've had some readdir() issues occur in an embedded app, so I added this self-contained test at a convenient place in the app code:
FILE *f;
DIR *d;
f = fopen ("/mnt/mydir/myfile", "r");
printf ("fopen %p\r\n", f);
if (f) fclose(f);
d = opendir ("/mnt/mydir");
printf ("opendir ret %p\r\n", f);
if (d)
{
struct dirent *entry;
do
{
errno = 0;
entry = readdir (d);
printf ("readdir ret %p %s, errno %d %s\r\n", entry, entry ? entry->d_name : "", errno, strerror(errno));
} while (entry);
closedir (d);
}
/mnt/mydir is an NFS mount (although I'm not sure if that's relevant). The fopen() call to open a file in that dir always succeeds, and the opendir() on the dir also always succeeds. However, sometimes (most) the readdir() fails with errno=EFAULT.
I don't believe anywhere else in the app is doing anything with that dir. The test is exactly as written, all variables are local stack scope.
If I run it as a standalone program, it always succeeds.
Can anyone offer any suggestions as to what could cause EFAULT here? I'm pretty sure my DIR pointer variable is not being corrupted, although the DIR structure itself could be I guess. I haven't seen any evidence elsewhere of heap corruption.
From man 2 readdir page:
EFAULT Argument points outside the calling process's address space.
This means that your structure is corrupted
I think I found the problem. The uClibc implementation of opendir/readdir does a stat() on the directory, then later does a stack alloca() of size statbuf.st_blksize. My NFS directory was mounted with rsize=512KB, causing readdir() to try and allocate 512KB on the stack to hold the dents. My embedded setup does not have that much room between stacks, so at some point was hitting something below in memory and causing EFAULT.
If I change my NFS mount options to rsize=4096, it works fine.

shmget() returns ENOENT with IPC_CREAT

I'm using shmget() to allocate a shared memory segment that I then use with pthread_mutex_init() to create a mutex shared between processes. Generally, this works as expected. However, occasionally shmget() will return ENOENT. Reading the man page, this should only occur if the shmflg doesn't include IPC_CREAT, however I am including that. Here's a snip-it of my code:
shmid_ = shmget( MYLOCK_KEY_ID, sizeof(pthread_mutex_t), IPC_CREAT | IPC_EXCL | 0666 );
if ( errno == ENOENT ) {
// This should never occur since IPC_CREAT was specified
std::cerr
<< "shmget() returned ENOENT (it thinks IPC_CREAT wasn't specified).\n"
<< "This seems to be a bug in shmget()?" << std::endl;
exit(1);
}
I'm totally lost as to what could be going on. I've tried this on several systems (Linux kernels 2.6.32 and 3.3.5) but both exhibit the same behavior. Currently, when I obtain this failure mode, I just repeat the process and it usually works. But that seems kind of kludgey and I don't know if this is a bug in shmget() or if I'm just doing something wrong.
Any ideas?
Your if statement is not checking the returned value - the man pages say to check shmid_ for -1 and then check errno.
RETURN VALUE
A valid segment identifier, shmid, is returned on success, -1 on error.
What you are doing is just checking errno - it could be ENOENT after some other call to some other function that failed.

Resources