Writing Filesystems - Mount Options
From Genunix
| This article has been identified as a draft. It is currently undergoing a community review. Please add your comments to the discussion page.
Do not quote any text on this page! It is still a draft! |
How the Solaris filesystem framework can parse mount options for you
The behaviour of a filesystem instance (one mountpoint) can usually be modified when calling the mount(1m) command by passing in "mount options". Several of these are generic, most obviously the "readonly" option, mount -o ro ...., but they can be used for highly filesystem-specific behaviour selection, see for example the huge multitude of mount options nfs supports.
The generic mount options that every filesystem in Solaris "supports" - since the framework actually parses them and validates their effect before calling into filesystem-specific code - are listed in usr/src/uts/common/fs/vfs.c in the OpenSolaris sourcecode. A larger table of mount options used in Solaris by some (but not necessarily all) filesystems is in <sys/mntent.h>, and it makes sense to check this list for a possible functional match before you declare your own proprietary option name strings.
Now that you've decided what mount options you need beyond those parsed and applied by the framework by default, do you really want to implement mount option parsing ? You can - just leave the last element in the vfsdef_t structure for your filesystem at NULL. But it's much easier to communicate your needs to the framework, and let that parse the options for you.
How is that done ? You use an array of mntopt_t data structures:
/*
* Structure defining a mount option for a filesystem.
* option names are found in mntent.h
*/
typedef struct mntopt {
char *mo_name; /* option name */
char **mo_cancel; /* list of options cancelled by this one */
char *mo_arg; /* argument string for this option */
int mo_flags; /* flags for this mount option */
void *mo_data; /* filesystem specific data */
} mntopt_t;
What are these structure members for, how are they used:
- mo_name
- This is the mount option string. You'd just use foobaropt if you want to tell the framework your filesystem accepts a request mount -o foobaropt ....
- mo_cancel
- Mount option cancellation supports mutually exclusive pairs of mount options, like e.g. ufs' logging/nologging, that control enabling/disabling of a specific piece of functionality. If this mount option does not have such side effects, just make the field NULL.
- mo_arg
- Mount option arguments allow options like mount -o behaviour=randomize_response (a completely fictional example). mo_arg specifies the default value assigned to such an option, which the framework will pass even if -o behaviour=... is not supplied.
- mo_flags
- Flags control the behaviour of mount option parsing. The following are available:
- MO_SET is the "always on" (default) option. MO_DEFAULT is exactly the same.
- MO_IGNORE tells the framework to ignore a specific mount option. This allows, for example, to override the framework's generic behaviour - if you manage to write a filesystem that cannot be mounted readonly, use MO_IGNORE to tell the framework you don't want -o ro parsed.
- MO_HASVALUE indicates the use of mount option arguments for this mount option.
- MO_NODISPLAY allows "internal" options that don't show up in mnttab, i.e. that the user cannot see when dumping the list of mounted filesystems via the mount command without parameters.
- MO_EMPTY can be used to create placeholder lines in your sourcecode.
- mo_data
- This is currently unused and must always be set to NULL.
So, let's create a set of mount options for lowcarbfs. We want the following behaviour:
- A pair of mount options hidden and nohidden controls whether the filesystem will show files that have the FAT attribute "hidden" set. These two are obviously mutually exclusive, and not in the list in <sys/mntent.h>.
- Support the more-or-less generic noatime option. This and its mutually exclusive, default (as per POSIX) partner atime, are in <sys/mntent.h>.
- A pair of mount options foldcase / nofoldcase, and another one lfn / nolfn, again both mutually exclusive, that control filename parsing, whether to convert all names to lowercase, and whether to support Windows95-style long filenames. nofoldcase and lfn should be the default, and since these are only known to FAT fs support they're not found in <sys/mntent.h>.
- An option codepage=... should allow passing character set conversion requests for MSDOS filenames into our driver. This one takes an argument.
- Another option with argument, timezone=..., which is needed because MSDOS records file timestamps on FAT in localtime, and the timezone of the receiving system may not be identical to the one on the recording machine.
- And finally, another pair of mutually-exclusive options clamptime / noclamptime to specify whether FAT timestamps (which do not overlap with the UNIX time32_t range) are clamped to the common subset, 1980..2038, or result in EOVERFLOW errors.
This translates to the following sourcecode.
First, a header file fat_fs.h will get the definitions for our proprietary mount option names:
#include <sys/mntent.h> /* * Proprietary mount options for this filesystem type */ #define MNTOPT_FAT_HIDDEN "hidden" #define MNTOPT_FAT_NOHIDDEN "nohidden" #define MNTOPT_FAT_FOLDCASE "foldcase" #define MNTOPT_FAT_NOFOLDCASE "nofoldcase" #define MNTOPT_FAT_CLAMPTIME "clamptime" #define MNTOPT_FAT_NOCLAMPTIME "noclamptime" #define MNTOPT_FAT_ATIME MNTOPT_ATIME #define MNTOPT_FAT_NOATIME MNTOPT_NOATIME #define MNTOPT_FAT_LFN "lfn" #define MNTOPT_FAT_NOLFN "nolfn" #define MNTOPT_FAT_CODEPAGE "codepage" #define MNTOPT_FAT_TIMEZONE "timezone" /* * Allowed values for the "codepage=..." mount option */ #define FAT_CODEPAGE_ASCII "ascii" #define FAT_CODEPAGE_UNICODE "unicode" /* * Allowed values for the "timezone=..." mount option */ #define FAT_TIMEZONE_UTC "utc"
Second, our fatfs_vfsops.c sourcefile needs the following tables:
#include <sys/fs/fat_fs.h>
/*
* mount options table
*/
static char *nohidden_cancel[] = { MNTOPT_FAT_HIDDEN, NULL };
static char *hidden_cancel[] = { MNTOPT_FAT_NOHIDDEN, NULL };
static char *nofoldcase_cancel[] = { MNTOPT_FAT_FOLDCASE, NULL };
static char *foldcase_cancel[] = { MNTOPT_FAT_NOFOLDCASE, NULL };
static char *clamptime_cancel[] = { MNTOPT_FAT_NOCLAMPTIME, NULL };
static char *noclamptime_cancel[] = { MNTOPT_FAT_CLAMPTIME, NULL };
static char *atime_cancel[] = { MNTOPT_FAT_NOATIME, NULL };
static char *noatime_cancel[] = { MNTOPT_FAT_ATIME, NULL };
static char *nolfn_cancel[] = { MNTOPT_FAT_LFN, NULL };
static char *lfn_cancel[] = { MNTOPT_FAT_NOLFN, NULL };
/*
* Default FAT mount options:
* hidden (show hidden files)
* clamptime (clamp timestamps to suppress EOVERFLOW on stat)
* noatime (don't update access timestamps)
* nofoldcase (show filename case as stored on disk)
* lfn (show long filenames)
*/
static mntopt_t mntopts[] = {
/*
* option name cancel option default arg flags opt data
*/
{ MNTOPT_FAT_NOHIDDEN, nohidden_cancel, NULL, 0, NULL },
{ MNTOPT_FAT_HIDDEN, hidden_cancel, NULL, MO_DEFAULT, NULL },
{ MNTOPT_FAT_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT, NULL },
{ MNTOPT_FAT_FOLDCASE, foldcase_cancel, NULL, 0, NULL },
{ MNTOPT_FAT_CLAMPTIME, clamptime_cancel, NULL, MO_DEFAULT, NULL },
{ MNTOPT_FAT_NOCLAMPTIME, noclamptime_cancel, NULL, NULL, NULL },
{ MNTOPT_FAT_NOATIME, noatime_cancel, NULL, MO_DEFAULT, NULL },
{ MNTOPT_FAT_ATIME, atime_cancel, NULL, 0, NULL },
{ MNTOPT_FAT_NOLFN, nolfn_cancel, NULL, 0, NULL },
{ MNTOPT_FAT_LFN, lfn_cancel, NULL, MO_DEFAULT, NULL },
{ MNTOPT_FAT_TIMEZONE, NULL, FAT_CODEPAGE_ASCII, MO_HASVALUE, NULL },
{ MNTOPT_FAT_CODEPAGE, NULL, FAT_TIMEZONE_UTC, MO_HASVALUE, NULL },
};
One this is set up, the only thing that remains is to tell the framework that our lowcarbfs filesystem supports the abovementioned mount options. Which we do by hooking that table into our filesystem's vfsdef_t:
static mntopts_t fat_mntopts = {
sizeof (mntopts) / sizeof (mntopt_t),
mntopts
};
static vfsdef_t vfw = {
VFSDEF_VERSION,
"fat",
fatfsinit,
VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS,
&fat_mntopts
};
Note the presence of the VSW_HASPROTO, VSW_CANREMOUNT and VSW_STATS flags. These tell the framework that we're supplying mount option prototypes via table, that we support the mount -o remount,... command to allow on-the-fly changing of mount options without requiring unmounting the filesystem, and that we will be providing hooks for the fsstat(1) functionality. See <sys/vfs.h> for the complete list of VSW_* flags.
That's it ? Yes - that's it, so easy. How to perform the remaining parsing at mount time will be shown in a later section of this series.
