O_PATH equivalent in MacOS? - c

Linux has the O_PATH flag to open() which allows one to get a fd to be used in fstat, fcntl and others without actually opening the file for reading (or having permissions to do so). However the O_PATH flag is Linux specific.
Is there an equivalent to the O_PATH flag to open() in MacOS? For example, how can I use fstat() on a file I don't have read permissions for?

macOS doesn't have an equivalent to O_PATH, so it's impossible to have a reference to a file without opening it. Regarding the one bit of functionality that you mentioned, you can call stat with a given file path as long as you have "execution" rights to its parent directory, regardless of whether you have any rights to that file.

Related

Character Device Driver Read/Write in Linux

I've written a character device driver that, I believe, should work.
I want to test the read/write functions. On another stack overflow question, apparently this can be done by writing to the device file in /dev and echoing the output.
I don't understand how this works:
When I load up my device driver by allocating a major number and calling mknod to create the file, is the file "open"? If it isn't open, then reading/writing shouldn't work like that from the command line, or so I thought?
What state is the device driver in when it has been initialized in /proc/devices and a file created in /dev?
Does this initialization need to happen before attempting to open the device file in a c program?
These answer are extremely difficult to find online, and many resources are outdated. Thanks
One good resource is Linux device driver. A shorter and simpler explanation can be found here.
When you create a file driver, you will implement some functions among file operations (fops):
open
close
read
write
seek
...
Not all function have to be implemented. If write is not implemented for instance, your device won't support writting.
When I load up my device driver by allocating a major number and calling mknod to create the file, is the file "open"?
When the /dev file is created, your module is only inited. A function like init_module is called
When the file is removed, your module is deinited. A function like module_cleanup is called.
What state is the device driver in when it has been initialized in /proc/devices and a file created in /dev?
In that case, the module is inited, the file are not open.
If it isn't open, then reading/writing shouldn't work like that from the command line, or so I thought?
When you read a file from command line, the file is open, read then closed, as a user, you don't have to care to open/close file explicitly.
Things are different if you are a C programmer, in that case, you explicitly have to open, read, close the files.
You can check that adding traces in your kernel code (using printk to print some info to kernel console, reading it with dmesg) or using strace that will trace the system calls.
Does this initialization need to happen before attempting to open the device file in a c program?
Let's resume:
The first function called will be module_init before it's called, the file doesnot exist in /dev
The last function called will be module_cleanup after it's called, the file doesnot exist in /dev
between init and cleanup, you can call the different open, close, read and write function.
Generally read/write are called between open and close.

Is dnotify obsolete in kernel 4.x?

Im reading the fcntl manual page and came across thw dnotify:
File and directory change notification (dnotify)
It is suggested that new application should use inotify instead, but I think they are not the same since inotify works with char * paths making it suffering from file renaming (or cathing MOVED_FROM/MOVED_TO events) but dnotify works with file descriptors which is different:
int fcntl(int fd, int cmd, ... /* arg */ );
So as far as I can tell dnotify is not a subset of inotify neither vice versa.
Is it discouraged to use dnotify in newer kernel even if I want to subscribe on events by file descriptor, not by a file path as inotify allows?
If you read the manual page for inotify_add_watch closer, then yes it takes a path but it returns a "watch descriptor"
for the filesystem object (inode) that corresponds to pathname
So the path is only used to locate the inode. Once that's done you have a reference to the inode and the name of the file can be changed without problems.

How to lock files correctly in ANSI C

I am writing an ANSI C cgi-bin server program.
Each instance of program can access to the same files simultaneously.
I do as follows:
handle = fopen(name,type);
fd = fileno(handle);
MyLockFile(fd) //I use fcntl
.....
The problem is that I open file with "fopen", not with "open".
Will "locking" work in such manner?
I can lock "fd", not "handle".
The reason is that I can't write workable "fd=open..." code.
My code below creates the executable file, write permissions were not set.
I don't know why
fd = open(name,O_CREAT|O_WRONLY|S_IREAD|S_IWRITE|S_IRGRP|
S_IWGRP|S_IROTH|S_IWOTH);
write(fd,data,strlen(data));
close(fd);
I can neither write nor append to this file.
Your open() function is wrong, when you specify O_CREAT, the permission bits needs to be the 3. argument, e.g.
open(name,O_CREAT|O_WRONLY,
S_IREAD|S_IWRITE|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);

Possible reasons of linux open call returning EINVAL

I am trying to make a system call in my source code as follows.
int file;
file = open(argv[index], O_RDONLY);
Where the command line arguement is a path to a binary file in my filesystem. But this call throws me an EINVAL error. I have checked the existence of file and the required permissions to access it.
Any suggestions on what circumstances the EINVAL error will be thrown out.
The official documentation suggests that this is because your implementation of open() does not support synchronized IO for the file you are trying to open.
Cause of failure:
There were two processes say (process-1 and process-2) that were executing in close sequel and was trying to open this binary file. Since my system (embedded device) will crash after this open call, the debugs splitted out weren't proper and it made me to suspect the process-1. But the actual culprit is process-2 who was opening the binary with O_RDWR flag. But my file system (network mount) was mounted as "read only file system".
Points to be taken care:
Refining the perror prints it should be the right cause of the problem as "Read Only File System". So my initial perror description must be a uncleared value of any of the previous erroneous call. One learning here is to use perror with care, so as avoid analysing misleading error message.
Possible circumstances the EINVAL error will be thrown out:
The open call will show an EINVAL if we use O_SYNC (or) related flags for the file which we are not supposed to use. I conclude this based on the documentation as previously mentioned by Rafe.
If you are sure that argv[index] actually contains the filename and that O_RDONLY hasn't been overridden somehow (O_RDONLY should equal 0), check your system log via the dmesg command and make sure that nothing funky has happened in-kernel.

Duplicate file descriptor with its own file offset

How can one create a new file descriptor from an existing file descriptor such that the new descriptor does not share the same internal file structure/entry in the file table? Specifically attributes such as file offset (and preferably permissions, sharing and modes) should not be shared between the new and old file descriptors.
Under both Windows and Linux, dup() will duplicate the file descriptor, but both descriptors still point to the same file structure in the process' file table. Any seeking on either descriptor will adjust the position for the other descriptors as well.
Note
I've since received answers for both Windows and Linux and adjusted the question a little too often, which has made it difficult for people to answer. I'll adjust my votes and accept the cleanest answer which covers both Windows and Linux. Apologies to all, I'm still new to the SO paradigm. Thanks for the great answers!
So basically, what you really want is to be given a file descriptor, and basically open the same file over again, to get a separate position, sharing, mode, etc. And you want to do this on Windows (where the "file descriptor" is basically a foreign object, not something used directly by the OS or the run-time library at all.
Amazingly enough, there is a way to do that, at least with MS VC++. All but two steps of it use only the Win32 API so porting to other compilers/libraries should be fairly reasonable (I think most supply versions of those two functions). Those are for converting a Unix-style file descriptor to a native Win32 file handle, and converting a native Win32 file handle back to a Unix-style file descriptor.
Convert file-descriptor to native file handle with _get_osfhandle()
Get a name for the file with GetFileInformationByHandleEx(FILE_NAME_INFO)1
Use CreateFile to open a new handle to that file
Create a file descriptor for that handle with _open_osfhandle()
Et voilĂ , we have a new file descriptor referring to the same file, but with its own permissions, position, etc.
Toward the end of your question, you make it sound like you also want the "permissions", but that doesn't seem to make any real sense -- the permissions attach to the file itself, not to how the file is opened, so opening or reopening the file has no effect on the file's permissions. If you really want to know the, you can get it with GetFileInformationByHandle, but be aware that file permissions in Windows are quite a bit different from the (traditional) file permissions in Unix. Unix has owner/group/world permissions on all files, and most systems also have ACLs (though there's more variation in how they work). Windows either has no permissions at all (e.g., files on FAT or FAT32) or else uses ACLs (e.g., files on NTFS), but nothing that's really equivalent to the traditional owner/group/world permissions most people are accustomed to on Unix.
Perhaps you're using "permissions" to refer to whether the file was open for reading, writing, or both. Getting that is considerably uglier than any of the preceding. The problem is that most of it is in the library, not Win32, so there's probably no way to do it that will be even close to portable between compilers. With MS VC++ 9.0 SP1 (not guaranteed for any other compiler) you can do this:
#include <stdio.h>
int get_perms(int fd) {
int i;
FILE * base = __iob_func();
for (i=0; i<_IOB_ENTRIES; i++)
if (base[i]._file == fd)
return base[i]._flag; // we've found our file
return 0; // file wasn't found.
}
Since this involved some spelunking, I wrote a quick test to verify that it might actually work:
#ifdef TEST
#include <io.h>
void show_perms(int perms, char const *caption) {
printf("File opened for %s\n", caption);
printf("Read permission = %d\n", (perms & _IOREAD)!=0);
printf("Write permission = %d\n", (perms & _IOWRT)!=0);
}
int main(int argc, char **argv) {
FILE *file1, *file2;
int perms1, perms2;
file1=fopen(argv[1], "w");
perms1 = get_perms(_fileno(file1));
fclose(file1);
file2=fopen(argv[1], "r");
perms2 = get_perms(_fileno(file2));
fclose(file2);
show_perms(perms1, "writing");
show_perms(perms2, "reading");
return 0;
}
#endif
And the results seem to indicate success:
File opened for writing
Read permission = 0
Write permission = 1
File opened for reading
Read permission = 1
Write permission = 0
You can then test that returned flag against _IOREAD, _IOWRT, and _IORW, which are defined in stdio.h. Despite my previous warnings, I should probably point out that I suspect (though I certainly can't guarantee) that this part of the library is fairly stable, so the real chances of major changes are probably fairly minimal.
In the other direction, however, there's basically no chance at all that it'll work with any other library. It could (but certainly isn't guaranteed to) work with the other compilers that use the MS library, such as Intel, MinGW or Comeau using MS VC++ as its back-end. Of those, I'd say the most likely to work would be Comeau, and the least likely MinGW (but that's only a guess; there's a good chance it won't work with any of them).
Requires the redistributable Win32 FileID API Library
So, I recommend reading up on this a little more. The dup() and related functions serve to create a duplicate value in the file descriptor table pointing to the same entry in the open file table. This is intended to have the same offset. If you call open(), you will create a new entry the open file table.
It doesn't make any sense to create a duplicate of a file descriptor and that new file descriptor have a different offset in the open file table (this seems to contradict what the word "duplicate" means).
I'm not sure what your question is actually. I mean, it isn't the same thing as a duplicate. You could read:
/proc/self/fd/[descriptor]
and get the string that was used to open that file descriptor; bear in mind this may provide some pitfalls, some of which you actually noted in your observation of calling open() again.
Maybe you can explain a little more and I can try to update to help.
Why don't you just open the file a second time with open() or CreateFile() on windows? This gives you all freedom of different access rights and separate offset.
This of course has the drawback that you you can not open the file exclusively, but it solves your problem very simply.

Resources