4

I need to pass some custom flags to the open() call of my device driver.

I found this example in LDD3:

int dev_open(struct inode *inode, struct file *filp)
{
    if ((filp->f_flags & O_ACCMODE) == O_WRONLY) {
        ...
    }
}

My question is: is it possibile to define other flags (like O_ACCMODE and O_WRONLY) without conflicts with any others?

Sam Protsenko
  • 14,045
  • 4
  • 59
  • 75
Michele
  • 131
  • 2
  • 11

1 Answers1

3

Yes, it's possible. Take a look at include/uapi/asm-generic/fcntl.h. Pay attention to next comment:

/*
 * When introducing new O_* bits, please check its uniqueness in fcntl_init().
 */

Now look into fcntl_init() function (defined at fs/fcntl.c):

/*
 * Please add new bits here to ensure allocation uniqueness.
 * Exceptions: O_NONBLOCK is a two bit define on parisc; O_NDELAY
 * is defined as O_NONBLOCK on some platforms and not on others.
 */
BUILD_BUG_ON(20 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32(
    O_RDONLY        | O_WRONLY      | O_RDWR        |
    O_CREAT         | O_EXCL        | O_NOCTTY      |
    O_TRUNC         | O_APPEND      | /* O_NONBLOCK | */
    __O_SYNC        | O_DSYNC       | FASYNC        |
    O_DIRECT        | O_LARGEFILE   | O_DIRECTORY   |
    O_NOFOLLOW      | O_NOATIME     | O_CLOEXEC     |
    __FMODE_EXEC    | O_PATH        | __O_TMPFILE
    ));

So first you need to find unique value for your new definition, so it can be bitwise-or'd with flags listed in fcntl_init(). Next you need to add your new definition to include/uapi/asm-generic/fcntl.h. And finally add your new define to fcntl_init(), so it will be checked at compile time.

In the end it boils down to finding the value that doesn't conflict with existing definitions. E.g. as I can see all 10, 100, 1000, 10000, 100000, 1000000 and 10000000 are used. So for your new flags you can use 100000000, 200000000, 400000000 and 800000000 values.

UPDATE: As SailorCaire correctly mentioned, you also need to increment first number in BUILD_BUG_ON() macro. For example, if it originally was BUILD_BUG_ON(20 - 1, and you are to add one element to this list, you should make it BUILD_BUG_ON(21 - 1.

UPDATE 2: Another valuable addition from SailorCaire:

By the way, you'll need to do make install_headers, copy the new headers, and it looks like you'll need to recompile glibc so it becomes aware of the API change.

Community
  • 1
  • 1
Sam Protsenko
  • 14,045
  • 4
  • 59
  • 75
  • The only thing, is that despite doing these things, I'm still getting compile errors at the BUILD_BUG_ON.. – SailorCire May 04 '16 at 16:13
  • @SailorCire What kind of compile errors do you have? Can you provide GCC output? – Sam Protsenko May 04 '16 at 16:16
  • This will get ugly, but: `include/linux/compiler.h:484:20: error: call to ‘__compiletime_assert_758’ declared with attribute error: BUILD_BUG_ON failed: 20 - 1 != HWEIGHT32( O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O _EXCL | O_NOCTTY | O_TRUNC | O_APPEND | __O_SYNC | O_DSYNC | FASYNC | O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | O_NOATIME | O_CLOEXEC | __FMODE_EXEC | O_PATH | __O_TMPFILE | __FMOD E_NONOTIFY | O_CLASSIFY ) prefix ## suffix(); \` I relooked at #define statements and noticed a pattern. I'm recompiling to see if that fixes that. – SailorCire May 04 '16 at 16:17
  • 1
    @SailorCire Why do you have `20 - 1` in the error, where it should probably be `22 - 1`? Also, what is the number that you assigned for `O_CLASSIFY`? BTW, you can use [this](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=75069f2b5bfb5164beafaf3da597279c25b5535a) commit as a template for your task. – Sam Protsenko May 04 '16 at 16:22
  • I was using [this](https://lwn.net/Articles/643730/) as a basis and `O_CLASSIFY` is set to: `040000000`. I think you're right about the cause of the error. Sometimes I just need an extra set of eyes. I don't know why I decremented the value.. – SailorCire May 04 '16 at 16:30
  • 1
    Yeah, that solved it...interesting that I made a point and didn't actually follow it. >_ – SailorCire May 04 '16 at 16:44
  • 1
    By the way, you'll need to do make install_headers, copy the new headers, and it looks like you'll need to recompile glibc so it becomes aware of the API change. – SailorCire May 04 '16 at 18:19
  • Wow, good point! Totally missed it. Let me add it to the answer. – Sam Protsenko May 04 '16 at 21:59
  • If you know of a way to have glibc be aware of the changes, without recompiling (and potentially breaking your system), then I'd love to know. – SailorCire May 04 '16 at 22:01
  • I'm not completely sure that glibc needs to be recompiled. As I see it, new constant only needs to be provided to `open()` syscall. Of course, if you don't want to mess with `open()` syscall, but rather use `fopen()` from standard library, it should be recompiled. So from my point of view it strongly depends on how you are planning to use newly added constant (possible use-cases). – Sam Protsenko May 04 '16 at 22:10
  • if you did `syscall(SYS_open,...)`, then you'd be fine; however, if you do `open(2)` from `fcntl.h` then it doesn't know what the new values are. – SailorCire May 04 '16 at 22:12
  • Well, I'm pretty sure glibc doesn't check all flags in `open()` implementation, more like just passing them to actual syscall (btw, it's really hard to find which `open()` stub is used for x86_64 arch in glibc). Or I'm missing something? If you still need to override `open()` syscall -- you can do that using `--wrap` GCC option (look [here](http://stackoverflow.com/questions/3662856/how-to-reimplement-or-wrap-a-syscall-function-in-linux)), or maybe using `LD_PRELOAD` environment variable. – Sam Protsenko May 04 '16 at 22:55
  • I've submitted a patch to address this. The data is pulled from ``. One could recompile their kernel and then modify ; however, your package manager will blast it away when there is a new version of glibc. I'd suggest installing a new version in `/usr/local/` to avoid this. – SailorCire May 09 '16 at 20:20
  • @SailorCire Interesting. Can you please share a link to your patch here? – Sam Protsenko May 09 '16 at 22:10