others
/* $Id$ */
							/* Copyright (c) 2007-2015 Pierre Pronchery <khorben@defora.org> */
							/* This file is part of DeforaOS Unix others */
							/* This program is free software: you can redistribute it and/or modify
							 * it under the terms of the GNU General Public License as published by
							 * the Free Software Foundation, version 3 of the License.
							 *
							 * This program is distributed in the hope that it will be useful,
							 * but WITHOUT ANY WARRANTY; without even the implied warranty of
							 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
							 * GNU General Public License for more details.
							 *
							 * You should have received a copy of the GNU General Public License
							 * along with this program.  If not, see <http://www.gnu.org/licenses/>. */
							#include <sys/stat.h>
							#include <sys/statvfs.h>
							#include <sys/param.h>
							#include <sys/mount.h>
							#ifdef MOUNT_ADOSFS
							# define MT_ADOSFS	MOUNT_ADOSFS
							# include <adosfs/adosfs.h>
							#endif
							#ifdef MOUNT_CD9660
							# define MT_ISO9660	MOUNT_CD9660
							# include <isofs/cd9660/cd9660_mount.h>
							#endif
							#ifdef MOUNT_EXT2FS
							# define MT_EXT2FS	MOUNT_EXT2FS
							# include <ufs/ufs/ufsmount.h>
							#endif
							#ifdef MOUNT_FFS
							# define MT_FFS		MOUNT_FFS
							# include <ufs/ufs/ufsmount.h>
							#endif
							#ifdef MOUNT_HFS
							# define MT_HFS		MOUNT_HFS
							# include <fs/hfs/hfs.h>
							#endif
							#ifdef MOUNT_MSDOS
							# define MT_FAT		MOUNT_MSDOS
							# include <msdosfs/msdosfsmount.h>
							#endif
							#ifdef MOUNT_NFS
							# define MT_NFS		MOUNT_NFS
							# include <nfs/nfsmount.h>
							#endif
							#ifdef MOUNT_NTFS
							# define MT_NTFS	MOUNT_NTFS
							# include <ntfs/ntfsmount.h>
							#endif
							#ifdef MOUNT_NULLFS
							# define MT_NULLFS	MOUNT_NULLFS
							# include <miscfs/nullfs/null.h>
							#endif
							#ifdef MOUNT_PROCFS
							# define MT_PROCFS	MOUNT_PROCFS
							# include <miscfs/procfs/procfs.h>
							# define PROCFS_ARGS_VERSION	PROCFS_ARGSVERSION
							#endif
							#ifdef MOUNT_TMPFS
							# define MT_TMPFS	MOUNT_TMPFS
							# include <fs/tmpfs/tmpfs_args.h>
							#endif
							#ifdef MOUNT_UNION
							# define MT_UNIONFS	MOUNT_UNION
							# include <miscfs/union/union.h>
							#endif
							#include <unistd.h>
							#include <stdlib.h>
							#include <stdio.h>
							#include <string.h>
							#include <errno.h>
							#ifndef PROGNAME
							# define PROGNAME	"mount"
							#endif
							#ifndef FSTAB
							# define FSTAB		"/etc/fstab"
							#endif
							/* portability */
							#ifndef MT_ISO9660
							# define MT_ISO9660	"iso9660"
							struct iso_args
							{
								char const * fspec;
							};
							#endif
							#ifndef MT_PROCFS
							# define MT_PROCFS	"proc"
							struct procfs_args
							{
								int version;
								int args;
							};
							# define PROCFS_ARGS_VERSION	0
							#endif
							/* mount */
							/* private */
							/* types */
							typedef struct _Prefs
							{
								int flags;
								char const * options;
								char const * type;
							} Prefs;
							#define PREFS_a 0x1
							#define PREFS_f 0x2
							#define PREFS_u	0x4
							/* prototypes */
							#ifdef MT_ADOSFS
							static int _mount_callback_adosfs(char const * type, int flags,
									char const * special, char const * node);
							#endif
							#ifdef MT_EXT2FS
							static int _mount_callback_ext2fs(char const * type, int flags,
									char const * special, char const * node);
							#endif
							#ifdef MT_FAT
							static int _mount_callback_fat(char const * type, int flags,
									char const * special, char const * node);
							#endif
							#ifdef MT_FFS
							static int _mount_callback_ffs(char const * type, int flags,
									char const * special, char const * node);
							#endif
							static int _mount_callback_generic(char const * type, int flags,
									char const * special, char const * node);
							#ifdef MT_HFS
							static int _mount_callback_hfs(char const * type, int flags,
									char const * special, char const * node);
							#endif
							#ifdef MT_ISO9660
							static int _mount_callback_iso9660(char const * type, int flags,
									char const * special, char const * node);
							#endif
							#ifdef MT_MFS
							static int _mount_callback_mfs(char const * type, int flags,
									char const * special, char const * node);
							#endif
							#ifdef MT_NFS
							static int _mount_callback_nfs(char const * type, int flags,
									char const * special, char const * node);
							#endif
							#ifdef MT_NTFS
							static int _mount_callback_ntfs(char const * type, int flags,
									char const * special, char const * node);
							#endif
							#ifdef MT_NULLFS
							static int _mount_callback_nullfs(char const * type, int flags,
									char const * special, char const * node);
							#endif
							#ifdef MT_PROCFS
							static int _mount_callback_procfs(char const * type, int flags,
									char const * special, char const * node);
							#endif
							#ifdef MT_TMPFS
							static int _mount_callback_tmpfs(char const * type, int flags,
									char const * special, char const * node);
							#endif
							#ifdef MT_UNIONFS
							static int _mount_callback_unionfs(char const * type, int flags,
									char const * special, char const * node);
							#endif
							/* constants */
							/* options */
							static const struct
							{
								size_t len;
								char const * name;
								int flags;
							} _mount_options[] =
							{
							#ifdef MNT_ASYNC
								{ 5,	"async",	MNT_ASYNC	},
							# ifndef MNT_SYNCHRONOUS
								{ 4,	"sync",		-MNT_ASYNC	},
							# endif
							#endif
							#ifdef MNT_LOCAL
								{ 5,	"local",	MNT_LOCAL	},
							#endif
							#ifdef MNT_LOG
								{ 3,	"log",		MNT_LOG		},
							#endif
							#ifdef MNT_NOATIME
								{ 5,	"atime",	-MNT_NOATIME	},
								{ 7,	"noatime",	MNT_NOATIME	},
							#endif
							#ifdef MS_NOATIME
								{ 5,	"atime",	-MS_NOATIME	},
								{ 7,	"noatime",	MS_NOATIME	},
							#endif
							#ifdef MNT_NOCOREDUMP
								{ 8,	"coredump",	-MNT_NOCOREDUMP	},
								{ 10,	"nocoredump",	MNT_NOCOREDUMP	},
							#endif
							#ifdef MNT_NODEV
								{ 3,	"dev",		-MNT_NODEV	},
								{ 5,	"nodev",	MNT_NODEV	},
							#endif
							#ifdef MS_NODEV
								{ 3,	"dev",		-MS_NODEV	},
								{ 5,	"nodev",	MS_NODEV	},
							#endif
							#ifdef MNT_NODEVMTIME
								{ 8,	"devmtime",	-MNT_NODEVMTIME	},
								{ 10,	"nodevmtime",	MNT_NODEVMTIME	},
							#endif
							#ifdef MNT_NOEXEC
								{ 4,	"exec",		-MNT_NOEXEC	},
								{ 6,	"noexec",	MNT_NOEXEC	},
							#endif
							#ifdef MS_NOEXEC
								{ 4,	"exec",		-MS_NOEXEC	},
								{ 6,	"noexec",	MS_NOEXEC	},
							#endif
							#ifdef MNT_NOSUID
								{ 4,	"suid",		-MNT_NOSUID	},
								{ 6,	"nosuid",	MNT_NOSUID	},
							#endif
							#ifdef MS_NOSUID
								{ 4,	"suid",		-MS_NOSUID	},
								{ 6,	"nosuid",	MS_NOSUID	},
							#endif
							#ifdef MNT_RDONLY
								{ 2,	"ro",		MNT_RDONLY	},
								{ 2,	"rw",		-MNT_RDONLY	},
							#endif
							#ifdef MS_RDONLY
								{ 2,	"ro",		MS_RDONLY	},
								{ 2,	"rw",		-MS_RDONLY	},
							#endif
							#ifdef MNT_ROOTFS
								{ 4,	"root",		MNT_ROOTFS	},
							#endif
							#ifdef MNT_SOFTDEP
								{ 7,	"softdep",	MNT_SOFTDEP	},
							#endif
							#ifdef MNT_SYNCHRONOUS
							# ifndef MNT_ASYNC
								{ 5,	"async",	-MNT_SYNCHRONOUS},
							# endif
								{ 4,	"sync",		MNT_SYNCHRONOUS	},
							#endif
							#ifdef MS_SYNCHRONOUS
							# ifndef MS_ASYNC
								{ 5,	"async",	-MS_SYNCHRONOUS},
							# endif
								{ 4,	"sync",		MS_SYNCHRONOUS	},
							#endif
							#ifdef MNT_UNION
								{ 5,	"union",	MNT_UNION	},
								{ 7,	"nounion",	-MNT_UNION	},
							#endif
								{ 0,	NULL,		0		}
							};
							/* filesystems supported */
							static const struct
							{
								char * name;
								char * type;
								int (*callback)(char const * type, int flags, char const * special,
										char const * node);
							} _mount_supported[] =
							{
							#ifdef MT_ADOSFS
								{ "adosfs",	MT_ADOSFS,	_mount_callback_adosfs	},
							#endif
							#ifdef MT_EXT2FS
								{ "ext2fs",	MT_EXT2FS,	_mount_callback_ext2fs	},
							#endif
							#ifdef MT_EXT3FS
								{ "ext3fs",	MT_EXT3FS,	_mount_callback_ext2fs	}, /* XXX */
							#endif
							#ifdef MT_FAT
								{ "fat",	MT_FAT,		_mount_callback_fat	},
							#endif
							#ifdef MT_FFS
								{ "ffs",	MT_FFS,		_mount_callback_ffs	},
							#endif
							#ifdef MT_HFS
								{ "hfs",	MT_HFS,		_mount_callback_hfs	},
							#endif
							#ifdef MT_ISO9660
								{ "iso9660",	MT_ISO9660,	_mount_callback_iso9660	},
							#endif
							#ifdef MT_MFS
								{ "mfs",	MT_MFS,		_mount_callback_mfs	},
							#endif
							#ifdef MT_NFS
								{ "nfs",	MT_NFS,		_mount_callback_nfs	},
							#endif
							#ifdef MT_NTFS
								{ "ntfs",	MT_NTFS,	_mount_callback_ntfs	},
							#endif
							#ifdef MT_NULLFS
								{ "nullfs",	MT_NULLFS,	_mount_callback_nullfs	},
							#endif
							#ifdef MT_PROCFS
								{ "procfs",	MT_PROCFS,	_mount_callback_procfs	},
							#endif
							#ifdef MT_TMPFS
								{ "tmpfs",	MT_TMPFS,	_mount_callback_tmpfs	},
							#endif
							#ifdef MT_UNIONFS
								{ "unionfs",	MT_UNIONFS,	_mount_callback_unionfs	},
							#endif
								{ NULL,		NULL,		_mount_callback_generic	}
							};
							/* prototypes */
							static int _mount(Prefs * prefs, char const * special, char const * node);
							static int _mount_error(char const * message, int ret);
							static int _mount_usage(void);
							/* functions */
							/* mount */
							static int _mount_all(Prefs * prefs, char const * node);
							static int _mount_print(void);
							static int _mount_do(Prefs * prefs, char const * special, char const * node);
							static int _mount_do_mount(char const * type, int flags, char const * special,
									char const * node, void * data, size_t datalen);
							static void _mount_do_options(Prefs * prefs, int * flags);
							static int _mount(Prefs * prefs, char const * special, char const * node)
							{
								if(special == NULL && node == NULL)
									return (prefs != NULL && (prefs->flags & PREFS_a) == PREFS_a)
										? _mount_all(prefs, NULL) : _mount_print();
								return _mount_do(prefs, special, node);
							}
							static int _mount_all(Prefs * prefs, char const * node)
							{
								int ret = 0;
								Prefs p;
								const char fstab[] = FSTAB;
								FILE * fp;
								char buf[128];
								size_t len;
								int res;
								char fsspecial[32];
								char fsnode[32];
								char fstype[32];
								char fsoptions[32];
								unsigned int freq;
								unsigned int passno;
								if(prefs != NULL)
									memcpy(&p, prefs, sizeof(p));
								else
									memset(&p, 0, sizeof(p));
								p.type = fstype;
								p.options = fsoptions;
								if((fp = fopen(fstab, "r")) == NULL)
									return -_mount_error(fstab, 1);
								while(fgets(buf, sizeof(buf), fp) != NULL)
								{
									if((len = strlen(buf)) == 0)
										continue; /* empty line */
									if(buf[len - 1] != '\n')
									{
										errno = E2BIG; /* XXX */
										break; /* line is too long */
									}
									if(buf[0] == '#')
										continue; /* comment */
									freq = 0;
									passno = 0;
									fsoptions[0] = '\0';
									res = sscanf(buf, "%31s %31s %31s %31s %u %u\n", fsspecial,
											fsnode, fstype, fsoptions, &freq, &passno);
									if(res < 3)
									{
										errno = EINVAL;
										break; /* not enough arguments */
									}
									if(node != NULL && strcmp(node, fsnode) != 0)
										continue;
									if(prefs != NULL && prefs->type != NULL
											&& strcmp(prefs->type, fstype) != 0)
										continue;
									fsspecial[sizeof(fsspecial) - 1] = '\0';
									fsnode[sizeof(fsnode) - 1] = '\0';
									fstype[sizeof(fstype) - 1] = '\0';
									fsoptions[sizeof(fsoptions) - 1] = '\0';
									ret |= _mount_do(&p, fsspecial, fsnode);
								}
								if(!feof(fp))
									ret |= -_mount_error(fstab, 1);
								if(fclose(fp) != 0)
									ret |= -_mount_error(fstab, 1);
								return ret;
							}
							#ifdef ST_WAIT
							static void _mount_print_flags(unsigned long flags);
							static int _mount_print(void)
							{
								int cnt;
								size_t s;
								struct statvfs * f;
								int i;
								if((cnt = getvfsstat(NULL, 0, ST_WAIT)) < 0)
									return _mount_error("getvfsstat", 1);
								s = sizeof(*f) * cnt;
								if((f = malloc(s)) == NULL)
									return _mount_error("malloc", 1);
								/* XXX race condition (the result of getvfsstat() may be different) */
								if((cnt = getvfsstat(f, s, ST_WAIT)) < 0)
								{
									free(f);
									return _mount_error("getvfsstat", 1);
								}
								for(i = 0; i < cnt; i++)
								{
									printf("%s%s%s%s%s%s", f[i].f_mntfromname, " on ",
											f[i].f_mntonname, " type ", f[i].f_fstypename,
											" (");
									_mount_print_flags(f[i].f_flag);
									printf("%s\n", ")");
								}
								free(f);
								return 0;
							}
							static void _mount_print_flags(unsigned long flags)
							{
								size_t i;
								char const * sep = "";
								for(i = 0; i < sizeof(_mount_options) / sizeof(*_mount_options); i++)
								{
									if(_mount_options[i].flags <= 0)
										continue;
									if((flags & _mount_options[i].flags)
											== (unsigned)_mount_options[i].flags)
									{
										printf("%s%s", sep, _mount_options[i].name);
										sep = ",";
										flags -= _mount_options[i].flags;
									}
								}
								if(flags != 0)
									printf("%s%lx", sep, flags);
							}
							#else /* workaround when getvfsstat() is missing */
							static int _mount_print(void)
							{
								int ret = 0;
								FILE * fp;
								const char mtab[] = "/etc/mtab";
								const char mounts[] = "/proc/mounts";
								char const * file = mtab;
								size_t res;
								char buf[256];
								if((fp = fopen(file, "r")) == NULL)
								{
									_mount_error(file, 1);
									file = mounts;
									if((fp = fopen(file, "r")) == NULL)
										return -_mount_error(file, 1);
								}
								while((res = fread(buf, 1, sizeof(buf), fp)) > 0)
									fwrite(buf, 1, res, stdout);
								if(!feof(fp))
									ret = -_mount_error(file, 1);
								if(fclose(fp) != 0)
									ret = -_mount_error(file, 1);
								return ret;
							}
							#endif
							static int _mount_do(Prefs * prefs, char const * special, char const * node)
							{
								int flags = 0;
								size_t i;
							#ifdef MF_FORCE
								if(prefs->flags & PREFS_f)
									flags |= MF_FORCE;
							#endif
							#ifdef MNT_FORCE
								if(prefs->flags & PREFS_f)
									flags |= MNT_FORCE;
							#endif
							#ifdef MF_REMOUNT
								if(prefs->flags & PREFS_u)
									flags |= MF_REMOUNT;
							#endif
							#ifdef MNT_UPDATE
								if(prefs->flags & PREFS_u)
									flags |= MNT_UPDATE;
							#endif
							#ifdef MS_REMOUNT
								if(prefs->flags & PREFS_u)
									flags |= MS_REMOUNT;
							#endif
								_mount_do_options(prefs, &flags);
								if(prefs->type == NULL)
									return _mount_callback_generic(NULL, flags, special, node);
								for(i = 0; _mount_supported[i].name != NULL; i++)
									if(strcmp(_mount_supported[i].name, prefs->type) == 0)
										return _mount_supported[i].callback(
												_mount_supported[i].type, flags,
												special, node);
								fprintf(stderr, "%s: %s: Filesystem not supported\n", PROGNAME,
										prefs->type);
								return -1;
							}
							static int _mount_do_mount(char const * type, int flags, char const * special,
									char const * node, void * data, size_t datalen)
							{
								struct stat st;
							#if defined(__NetBSD__) /* NetBSD */
							#if !defined(__NetBSD_Version__) || __NetBSD_Version__ >= 499000000
								if(mount(type, node, flags, data, datalen) == 0)
							# else
								if(mount(type, node, flags, data) == 0)
							# endif
							#elif defined(__APPLE__) || defined(__FreeBSD__) /* Darwin, FreeBSD */
								if(mount(type, node, flags, data) == 0)
							#else
								struct { char const * fspec; } * d = data;
								if(mount(special, node, type, flags, d->fspec) == 0)
							#endif
									return 0;
								switch(errno)
								{
									case ENOENT:
										if(stat(node, &st) == 0)
											return -_mount_error(special, 1);
										return -_mount_error(node, 1);
									case ENODEV:
									case ENXIO:
										return -_mount_error(special, 1);
									default:
										return -_mount_error(node, 1);
								}
							}
							static void _mount_do_options(Prefs * prefs, int * flags)
							{
								char const * o;
								size_t i;
								size_t j;
								if((o = prefs->options) == NULL)
									return;
								for(i = 0;; i++)
								{
									if(o[i] != ',' && o[i] != '\0')
										continue;
									for(j = 0; j < sizeof(_mount_options) / sizeof(*_mount_options);
											j++)
										if(i > 0 && _mount_options[j].len == i
												&& strncmp(_mount_options[j].name, o, i)
												== 0)
										{
											if(_mount_options[j].flags >= 0)
												*flags |= _mount_options[j].flags;
											else
												*flags &= ~(_mount_options[j].flags);
											break;
										}
									o += i;
									i = 0;
									if(o[i] == '\0')
										break;
									o++;
								}
							}
							#ifdef MT_ADOSFS
							static int _mount_callback_adosfs(char const * type, int flags,
									char const * special, char const * node)
							{
								int ret;
								struct adosfs_args adosfs;
								void * data = &adosfs;
								struct stat st;
								memset(&adosfs, 0, sizeof(adosfs));
								if((adosfs.fspec = strdup(special)) == NULL)
									return -_mount_error(node, 1);
								if(stat(node, &st) == 0)
								{
									adosfs.uid = st.st_uid;
									adosfs.gid = st.st_gid;
									adosfs.mask = ~st.st_mode & 0777;
								}
								/* FIXME actually parse options */
								adosfs.mask = 0755;
								type = MT_ADOSFS;
								ret = _mount_do_mount(type, flags, special, node, data, sizeof(adosfs));
								free(adosfs.fspec);
								return ret;
							}
							#endif
							#ifdef MT_EXT2FS
							static int _mount_callback_ext2fs(char const * type, int flags,
									char const * special, char const * node)
							{
								int ret;
								struct ufs_args ffs;
								void * data = &ffs;
								memset(&ffs, 0, sizeof(ffs));
								if((ffs.fspec = strdup(special)) == NULL)
									return -_mount_error(node, 1);
								type = MT_EXT2FS;
								ret = _mount_do_mount(type, flags, special, node, data, sizeof(ffs));
								free(ffs.fspec);
								return ret;
							}
							#endif
							#ifdef MT_FAT
							static int _mount_callback_fat(char const * type, int flags,
									char const * special, char const * node)
							{
								int ret;
								struct msdosfs_args msdosfs;
								void * data = &msdosfs;
								struct stat st;
								memset(&msdosfs, 0, sizeof(msdosfs));
								if((msdosfs.fspec = strdup(special)) == NULL)
									return -_mount_error(node, 1);
								if(stat(node, &st) == 0)
								{
									msdosfs.uid = st.st_uid;
									msdosfs.gid = st.st_gid;
									msdosfs.mask = ~st.st_mode & 0666;
									msdosfs.dirmask = ~st.st_mode & 0777;
								}
								/* FIXME actually parse options */
								msdosfs.version = MSDOSFSMNT_VERSION;
								type = MT_FAT;
								ret = _mount_do_mount(type, flags, special, node, data,
										sizeof(msdosfs));
								free(msdosfs.fspec);
								return ret;
							}
							#endif
							#ifdef MT_FFS
							static int _mount_callback_ffs(char const * type, int flags,
									char const * special, char const * node)
							{
								int ret;
								struct ufs_args ffs;
								void * data = &ffs;
								memset(&ffs, 0, sizeof(ffs));
								if((ffs.fspec = strdup(special)) == NULL)
									return -_mount_error(node, 1);
								type = MT_FFS;
								ret = _mount_do_mount(type, flags, special, node, data, sizeof(ffs));
								free(ffs.fspec);
								return ret;
							}
							#endif
							static int _mount_callback_generic(char const * type, int flags,
									char const * special, char const * node)
							{
								struct
								{
									char const * fspec;
								} data;
								if(node == NULL)
								{
									errno = EINVAL;
									return -_mount_error("mount", 1);
								}
								if(special == NULL)
									return -_mount_all(NULL, node);
								data.fspec = special;
								return _mount_do_mount(type, flags, special, node, &data,
										sizeof(special));
							}
							#ifdef MT_HFS
							static int _mount_callback_hfs(char const * type, int flags,
									char const * special, char const * node)
							{
								int ret;
								struct hfs_args hfs;
								void * data = &hfs;
								memset(&hfs, 0, sizeof(hfs));
								if((hfs.fspec = strdup(special)) == NULL)
									return -_mount_error(node, 1);
								/* XXX does not support options at the moment */
								type = MT_HFS;
								ret = _mount_do_mount(type, flags, special, node, data, sizeof(hfs));
								free(hfs.fspec);
								return ret;
							}
							#endif
							#ifdef MT_ISO9660
							static int _mount_callback_iso9660(char const * type, int flags,
									char const * special, char const * node)
							{
								struct iso_args iso9660;
								void * data = &iso9660;
								memset(&iso9660, 0, sizeof(iso9660));
								iso9660.fspec = special;
								/* FIXME actually parse options */
								type = MT_ISO9660;
								return _mount_do_mount(type, flags, special, node, data,
										sizeof(iso9660));
							}
							#endif
							#ifdef MT_MFS
							static int _mount_callback_mfs(char const * type, int flags,
									char const * special, char const * node)
							{
								int ret;
								struct mfs_args mfs;
								void * data = &mfs;
								memset(&mfs, 0, sizeof(mfs));
								if((mfs.fspec = strdup(special)) == NULL)
									return -_mount_error(node, 1);
								/* FIXME actually parse options */
								mfs.size = (2 << 24);
								type = MT_MFS;
								ret = _mount_do_mount(type, flags, special, node, data, sizeof(mfs));
								free(mfs.fspec);
								return ret;
							}
							#endif
							#ifdef MT_NFS
							static int _mount_callback_nfs(char const * type, int flags,
									char const * special, char const * node)
							{
								int ret;
								struct nfs_args nfs;
								void * data = &nfs;
								char * p;
								char * q;
								memset(&nfs, 0, sizeof(nfs));
								if(special == NULL || strchr(special, ':') == NULL)
								{
									errno = EINVAL;
									return -_mount_error(node, 1);
								}
								if((p = strdup(special)) == NULL)
									return -_mount_error(node, 1);
								q = strchr(p, ':');
								*(q++) = '\0';
								/* FIXME untested */
								nfs.version = NFS_ARGSVERSION;
								nfs.hostname = p;
								nfs.fh = (unsigned char *)q;
								/* FIXME implement the rest */
								type = MT_NFS;
								ret = _mount_do_mount(type, flags, special, node, data, sizeof(nfs));
								free(q);
								return ret;
							}
							#endif
							#ifdef MT_NTFS
							static int _mount_callback_ntfs(char const * type, int flags,
									char const * special, char const * node)
							{
								int ret;
								struct ntfs_args ntfs;
								void * data = &ntfs;
								struct stat st;
								memset(&ntfs, 0, sizeof(ntfs));
								if((ntfs.fspec = strdup(special)) == NULL)
									return -_mount_error(node, 1);
								if(stat(node, &st) == 0)
								{
									ntfs.uid = st.st_uid;
									ntfs.gid = st.st_gid;
									ntfs.mode = ~st.st_mode & 0777;
								}
								/* FIXME actually parse options */
								type = MT_NTFS;
								ret = _mount_do_mount(type, flags, special, node, data, sizeof(ntfs));
								free(ntfs.fspec);
								return ret;
							}
							#endif
							#ifdef MT_NULLFS
							static int _mount_callback_nullfs(char const * type, int flags,
									char const * special, char const * node)
							{
								int ret;
								struct null_args nullfs;
								void * data = &nullfs;
								memset(&nullfs, 0, sizeof(nullfs));
								if((nullfs.la.target = strdup(special)) == NULL)
									return -_mount_error(node, 1);
								type = MT_NULLFS;
								ret = _mount_do_mount(type, flags, special, node, data, sizeof(nullfs));
								free(nullfs.la.target);
								return ret;
							}
							#endif
							#ifdef MT_PROCFS
							static int _mount_callback_procfs(char const * type, int flags,
									char const * special, char const * node)
							{
								struct procfs_args procfs;
								void * data = &procfs;
								memset(&procfs, 0, sizeof(procfs));
								procfs.version = PROCFS_ARGS_VERSION;
								type = MT_PROCFS;
								return _mount_do_mount(type, flags, special, node, data,
										sizeof(procfs));
							}
							#endif
							#ifdef MT_TMPFS
							static int _mount_callback_tmpfs(char const * type, int flags,
									char const * special, char const * node)
							{
								struct tmpfs_args tmpfs;
								void * data = &tmpfs;
								struct stat st;
								memset(&tmpfs, 0, sizeof(tmpfs));
								tmpfs.ta_version = TMPFS_ARGS_VERSION;
								if(stat(node, &st) == 0)
								{
									tmpfs.ta_root_uid = st.st_uid;
									tmpfs.ta_root_gid = st.st_gid;
									tmpfs.ta_root_mode = st.st_mode;
								}
								/* FIXME actually parse options */
								tmpfs.ta_nodes_max = 1024;
								tmpfs.ta_root_mode = 0755;
								type = MT_TMPFS;
								return _mount_do_mount(type, flags, special, node, data, sizeof(tmpfs));
							}
							#endif
							#ifdef MT_UNIONFS
							static int _mount_callback_unionfs(char const * type, int flags,
									char const * special, char const * node)
							{
								int ret;
								struct union_args unionfs;
								void * data = &unionfs;
								memset(&unionfs, 0, sizeof(unionfs));
								if((unionfs.target = strdup(special)) == NULL)
									return -_mount_error(node, 1);
								/* FIXME actually parse options */
								type = MT_UNIONFS;
								ret = _mount_do_mount(type, flags, special, node, data,
										sizeof(unionfs));
								free(unionfs.target);
								return ret;
							}
							#endif
							/* mount_error */
							static int _mount_error(char const * message, int ret)
							{
								fputs(PROGNAME ": ", stderr);
								perror(message);
								return ret;
							}
							/* mount_usage */
							static int _mount_usage(void)
							{
								size_t i;
								char const * sep = "";
								fputs("Usage: " PROGNAME " [-a][-t type]\n"
							"       " PROGNAME " [-f] special | node\n"
							"       " PROGNAME " [-f][-u][-o options] special node\n", stderr);
								fputs("\nFilesystems supported:\n", stderr);
								for(i = 0; _mount_supported[i].name != NULL; i++)
								{
									fprintf(stderr, "%s%s", sep, _mount_supported[i].name);
									sep = ", ";
								}
								fputs("\n\nOptions supported:\n", stderr);
								sep = "";
								for(i = 0; _mount_options[i].name != NULL; i++)
									if(_mount_options[i].flags >= 0)
									{
										fprintf(stderr, "%s%s", sep, _mount_options[i].name);
										sep = ", ";
									}
								fputs("\n\nEach filesystem may not support every option.\n", stderr);
								return 1;
							}
							/* public */
							/* functions */
							/* main */
							int main(int argc, char * argv[])
							{
								Prefs prefs;
								int o;
								char const * special = NULL;
								char const * node = NULL;
								memset(&prefs, 0, sizeof(prefs));
								while((o = getopt(argc, argv, "afo:t:u")) != -1)
									switch(o)
									{
										case 'a':
											prefs.flags |= PREFS_a;
											break;
										case 'f':
											prefs.flags |= PREFS_f;
											break;
										case 'o':
											prefs.options = optarg;
											break;
										case 't':
											prefs.type = optarg;
											break;
										case 'u':
											prefs.flags |= PREFS_u;
											break;
										default:
											return _mount_usage();
									}
								if(optind + 2 == argc)
									special = argv[optind++];
								if(optind + 1 == argc)
									node = argv[optind];
								else if(optind != argc)
									return _mount_usage();
								return (_mount(&prefs, special, node) == 0) ? 0 : 2;
							}
							