Next Previous Contents

6. Validity checks

When a Remote Procedure Call arrives with a file handle (or possibly two file handles) in it, the file handle needs to be converted to a dentry (the Linux internal representation of a filesystem object), and this dentry must be checked to see if the required access is permitted. This checking is performed by nfsfh.c::fh_verify.

Then the file handle arrives, nfsxdr.c::decode_fh copies it in to a struct svc_fh structure which has been zeroed by nfsffh.h::fh_init.

The svc_fh structure was described earlier in the section on Using the File Handle.

The process of verification proceeds as follows:

  1. The file handle is checked to make sure that fh_dev is that same as fh_xdev. If it isn't a warning is printed and a ESTALE error is returned. This is simply a consistency check. The code could equally well simply ignore the value in fh_xdev (as it ignores many other bytes in the file handle) and copy fh_dev into fh_xdev for other sections of code to use.
  2. The export point from the file handle (fh_xdev, fh_xino) is looked up in the export table (with export.c::exp_get) to find out how, and whether, this file tree is currently exported. If there is no export entry, then the file handle is rejected with ESTALE. There are enhancements being worked on (September 1999) to allow knfsd to call-back to a userlevel process (such as mountd) to ask that and appropriate entry be inserted into the table --- possible and entry denying access.
  3. If the export entry requires requests to come from a secure port (1-1024), and the request is from an insecure port, then file handle is rejected with EPERM and a warning is printed.
  4. Next the file handle is converted to a dentry by find_fh_dentry. If the export point has the SUBTREECHECK flag set, then find_fh_dentry must find a dentry which is properly located in the file hierarchy . If not, and the file handle does not refer to a directory, then it is allowed to return a "root" dentry that simply refers to the appropriate inode. If an appropriate dentry cannot be found, then the file handle is rejected, possibly with ESTALE, or ENOENT if a location could not be found in the tree. (Maybe it should always return ESTALE?)
  5. Next the generation number from the inode (referred to be the dentry) is compared with the generation number in the file handle. If they don't match then the file handle is rejected as stale. Arguably the generation number should be checked in find_fh_dentry as if the generation number doesn't match then it isn't the right dentry. This is more of aesthetic than practical significance.
  6. When fh_verify is called, the called may indicate that a particular type of object is required, possibly a directory, or a file or a symbolic link. If a type was specified then the next check is to make sure that the inode that was found has the right type. If the inode has the wrong type, then either ENOTDIR or EISDIR is returned depending on whether a diretory was asked for or not.
  7. The next check is the sub-tree check and can be disabled if the export point did not have the SUBTREECHECK flag set. The sub-tree check involves walking up the dcache tree from the dentry that was found until we find the dentry for the export point. If the root of the filesystem is found before finding the export point, then the dentry found is clearly not in the exported tree, and so the filehandle is rejected with ESTALE. While the the tree is being walked another check is made. If the filesystem is exported ROOTSQUASH then every directory in the path must give execute access to someone other than root/wheel ???
  8. The last step of fh_verify is to call vfs.c::nfsd_permission. This checks the access type that was requested in various ways as the following points outline. However it first checks if the dentry was mounted on. In this case (if it is compiled with CONFIG_NFSD_SUN) the filehandle is rejected with EPERM.
  9. The first access type check in nfsd_permission is to guard against writing inappropriately. If a write access (including setattr and truncate) is requested then: if the export or the filesystem is readonly, the EROFS is returned, and if the file is immutable, then EPERM is returned. These is another test involving nfsd_iscovered, however nfsd_iscovered is equivalent to false. See Below.
  10. If access requires truncation, but the file is append only, then EPERM is returned. It would seem that this test should be done in the VFS layer. However VFS enforces correct handling of IS_APPEND at file open time, and there is not equivalent of open with NFS. Interestingly, vfs.c::nfsd_open rejects all read/write access to IS_APPEND files.
  11. If all preceding tests succeed, then the owner of the file will always get access. This may seem a bit odd, but it is related to the fact that Unix does permission checking at open time, while NFS has to do it at access time.
  12. Finally, the vfs permission routine is called to do normal access checking. As a special case, read-only requests on a regular file are allowed to if read OR exec access is available. This allow executables to be loaded (NFS does not distinguish between loading a file to read it and loading a file to execute it).

knfsd has a number of other bits of permission checking code distributed in various places which are worth mentioning.

nfsd_iscovered

This function is called from a number of places in vfs.c, including once in nfsd_permission as mentioned above. In apparent contradiction to it's name, this routine seems to check if a given dentry "covers" (i.e. is mounted on) some other dentry. However it allows through the export point. As linux only allows the root of a filesystem to cover anything, this function could only return true for the root of a filesystem, but when given a dentry which is the root of a filesystem, the export point will be that same root, and so nfsd_iscovered will still return false.

I am not sure what the intent of this routine is.

fs_off_limits

The vfs.c:fs_off_limits function rejects any filesystem which is either an NFS filesystem or a PROC filesystem, as exporting these is a bad idea for different reasons.

It is called in nfsd_lookup to make sure that the parent dentry is not on an off-limits file system.

It would seem to make more sense to perform this test in fh_verify so that those file systems were equally rejected for all accessed. Further, a more general test would be to reject any filesystem without the FS_REQUIRES_DEV flag, as this coverred the two in question and any such filesystem does not have a reliably stable device number, and so (current) filehandles wouldn't be guaranteed to remain meaningful across reboots.

set-time

Some NFS clients (apparently) try to use the setattr request to update the access and modify times on a file to the current time. This should be allowed for any client which has write access to the file (whereas normally seting these times is restricted to the owner). nfsd_setattr makes a special case of allowing such a request through providing that the requested time is "close enough" to the current time on the server. "Close enough" is a configurable value set via the /proc/fs/nfs/time-diff-margin file.

This configuation should probably go somewhere in /proc/sys to meet current (apparent) standards, though where isn't clear.


Next Previous Contents