1

I'm making some extensions to the kernel module nandsim, and I'm having trouble finding the correct way to test if a file exists before opening it. I've read this question, which covers how the basic open/read/write operations go, but I'm having trouble figuring out if and how the normal open(2) flags apply here.

I'm well aware that file reading and writing in kernel modules is bad practice; this code already exists in the kernel and is already reading and writing files. I am simply trying to make a few adjustments to what is already in place. At present, when the module is loaded and instructed to use a cache file (specified as a string path when invoking modprobe), it uses filp_open() to open the file or create it if it does not exist:

/* in nandsim.c */
...
module_param(cache_file,   charp, 0400);
...
MODULE_PARM_DESC(cache_file,    "File to use to cache nand pages instead of memory");
...
struct file *cfile;
cfile = filp_open(cache_file, O_CREAT | O_RDWR | O_LARGEFILE, 0600);

You might ask, "what do you really want to do here?" I want to include a header for the cache file, such that it can be reused if the system needs to be reset. By including information about the nand page geometry and page count at the beginning of this file, I can more readily simulate a number of error conditions that otherwise would be impossible within the nandsim framework. If I can bring down the nandsim module during file operations, or modify the backing file to model a real-world fault mode, I can recreate the net effect of these error conditions. This would allow me to bring the simulated device back online using nandsim, and assess how well a fault-tolerant file system is doing its job.

My thought process was to modify it as follows, such that it would fail trying to force creation of a file which already exists:

struct file *cfile;
cfile = filp_open(cache_file, O_CREAT | O_EXCL | O_RDWR | O_LARGEFILE, 0600);
if(IS_ERR(cfile)){
    printk(KERN_INFO "File didn't exist: %ld", PTR_ERR(cfile));
    /* Do header setup for first-time run of NAND simulation */
}
else{
    /* Read header and validate against system parameters.  Recover operations */
}

What I'm seeing is an error, but it is not the one I would have expected. It is reporting errno 14, EFAULT (bad address) instead of errno 17 EEXIST (File exists). I don't want to run with this because I would like this to be as idiomatic and correct as possible.

Is there some other way I should be doing this?

Do I need to somehow specify that the file path is in user address space? If so, why is that not the case in the code as it was?

EDIT: I was able to get a reliable error by trying to open with only O_RDWR and O_LARGEFILE, which resulted in ENOENT. It is still not clear why my original approach was incorrect, nor what the best way to accomplish my goal is. That said, if someone more experienced could comment on this, I can add it as a solution.

Onofog
  • 443
  • 3
  • 15
  • 1
    Where are you getting the `cache_file` variable from? Is it a module parameter like in the original file or did you change that? If so, please state it and add the relevant code. – Marco Bonelli May 01 '20 at 09:04
  • This is the original cache_file obtained from the module parameters. I've updated the code snippet to include how it is populated. – Onofog May 02 '20 at 20:53

1 Answers1

0

Indeed, filp_open expects a file path which is in kernel address space. Proof is the use of getname_kernel. You can mimic this for your use case with something like this:

struct filename *name = getname(cache_file);
struct file *cfile = ERR_CAST(name);

if (!IS_ERR(name)) {
        cfile = file_open_name(name, O_CREAT | O_EXCL | O_RDWR | O_LARGEFILE, 0600);
        if (IS_ERR(cfile))
                return PTR_ERR(cfile);

        putname(name);
}

Note that getname expects a user-space address and is the equivalent of getname_kernel.

Justin Iurman
  • 18,954
  • 3
  • 35
  • 54
  • The path used by OP already is in kernel space, it's a module parameter ([see here](https://github.com/torvalds/linux/blob/025a06c1104cd8995646b761d117816b5f28c873/drivers/mtd/nand/raw/nandsim.c#L129)). – Marco Bonelli May 01 '20 at 08:57
  • Well, I was confused by OP's statement `Do I need to somehow specify that the file path is in user address space?` – Justin Iurman May 01 '20 at 09:01
  • 1
    Well, they mention they are applying some modifications to that (already existing) kernel module. You're right, that's confusing, OP should specify where they are getting the path from, if it's still a module parameter or not, I've commented above. – Marco Bonelli May 01 '20 at 09:04
  • By looking at OP's edit, it looks like it was working by only passing some flags, which means `cache_file` should be the original (and kernel-space) one. So, the problem is probably coming from somewhere else, let's wait for OP's response. – Justin Iurman May 01 '20 at 09:07
  • Yeah that's what I was assuming. – Marco Bonelli May 01 '20 at 09:07
  • I asked about the user address space, because after invoking nandsim with modprobe, I can see and manipulate the cache file in the working directory. Has the kernel module already taken care of the address space mapping for module parameters then? – Onofog May 02 '20 at 21:01
  • 1
    @Onofog yeah, module parameters are of course copied to kernel space when invoking [`init_module`](http://man7.org/linux/man-pages/man2/init_module.2.html) (which is what `modprobe` does). The problem is not there. `file_open_name` is returning `-EFAULT` for some other strange reason... – Marco Bonelli May 02 '20 at 21:06