Re: [PATCH] mount.nfs: configurable minor version defaults

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, 17 Nov 2014, Steve Dickson wrote:

> Hey Ben,
>
> On 11/17/2014 02:43 PM, Benjamin Coddington wrote:
> > Update nfsmount.conf to allow minor version specification, and rearrange
> > the autonegotiation logic to agreed upon best behavior.  Depending upon the
> > combination of values specified within nfsmount.conf and given to mount.nfs,
> > the retry behavior of mount.nfs varies according to the general rule of
> > defaulting to the most specific setting, with some exceptions.  A general
> > guide to the expected behavior follows:
> Would you mind breaking this patch up into a more digestible patch
> series defined by functionality. 199 insertions and 150 deletions
> in one patch makes it a bit tough to digest it... at least for me. :-)

Yes, I'll do that.

> >
> > ┌────────────────┐
> > │ nfsmount.conf  ├──────────────┬───────────────────┐
> > │ Defaultvers=   │  arg option  │     attempts:     │
> > ├────────────────┼──────────────┼───────────────────┤
> > │     4.2        │  not set     │ v4.2 v4.1 v4.0 v3 │
> > │     4.2        │   v4         │ v4.2 v4.1 v4.0    │
> > │     4.1        │  not set     │      v4.1 v4.0 v3 │
> > │     4.1        │   v4         │      v4.1 v4.0    │
> > │     4          │  not set     │           v4.0 v3 │
> > │     4          │   v4         │           v4.0    │
> > │     3          │  not set     │                v3 │
> > │    any set     │   v4.2       │ v4.2              │
> > │    any set     │   v4.1       │      v4.1         │
> > │    any set     │   v4         │           v4.0    │
> > │    any set     │   v3         │                v3 │
> > │    not set     │  not set     │           v4.0 v3 │
> > └────────────────┴──────────────┴───────────────────┘
> Why is "not set"  and "not set" not the same as Defaultvers=4.2?

Oh, because I made a mistake in trying to handle another case.  That's
the whole point of this work!  I'll fix it.

> >
> > If built without --enable-mountconfig, then the behavior is the same as if
> > nfsmount.conf did not set Defaultvers.
> We probably should switch this around so mountconfig is always enabled and
> let people disable if they want.

I can do that, too.

> >
> > The last case in the above table is a special case of disagreement about how
> > to determine the upper bound for a minor number of the v4 protocol.  It
> > could be argued that mount.nfs should start at the highest available
> > protocol version, if that version could be discovered at mount time.  For
> > now, the upper bound on the value can be modified by setting the values
> > within nfs_default_version().
> For now lets just hard code the upper bound a 4.2. Minor version don't come
> along very often so the recompiling of mount.nfs will not be that
> big of deal...

Right, that'll get fixed when I fix the case above.

Ben

>
> steved.
>
> >
> > Signed-off-by: Benjamin Coddington <bcodding@xxxxxxxxxx>
> > ---
> >  utils/mount/configfile.c |   36 +-----------
> >  utils/mount/network.c    |  122 +++++++++++++++++++--------------------
> >  utils/mount/network.h    |   15 +++++-
> >  utils/mount/nfsumount.c  |    4 +-
> >  utils/mount/parse_opt.c  |   26 ++++++++-
> >  utils/mount/parse_opt.h  |    2 +
> >  utils/mount/stropts.c    |  144 ++++++++++++++++++++++++++++++----------------
> >  7 files changed, 199 insertions(+), 150 deletions(-)
> >
> > diff --git a/utils/mount/configfile.c b/utils/mount/configfile.c
> > index 39d3741..0a4cc04 100644
> > --- a/utils/mount/configfile.c
> > +++ b/utils/mount/configfile.c
> > @@ -228,37 +228,8 @@ void free_all(void)
> >  		free(entry);
> >  	}
> >  }
> > -static char *versions[] = {"v2", "v3", "v4", "vers", "nfsvers", NULL};
> > -static int
> > -check_vers(char *mopt, char *field)
> > -{
> > -	int i, found=0;
> > -
> > -	/*
> > -	 * First check to see if the config setting is one
> > -	 * of the many version settings
> > -	 */
> > -	for (i=0; versions[i]; i++) {
> > -		if (strcasestr(field, versions[i]) != NULL) {
> > -			found++;
> > -			break;
> > -		}
> > -	}
> > -	if (!found)
> > -		return 0;
> > -	/*
> > -	 * It appears the version is being set, now see
> > -	 * if the version appears on the command
> > -	 */
> > -	for (i=0; versions[i]; i++)  {
> > -		if (strcasestr(mopt, versions[i]) != NULL)
> > -			return 1;
> > -	}
> > -
> > -	return 0;
> > -}
> >
> > -unsigned long config_default_vers;
> > +struct nfs_version config_default_vers;
> >  unsigned long config_default_proto;
> >  extern sa_family_t config_default_family;
> >
> > @@ -331,11 +302,6 @@ conf_parse_mntopts(char *section, char *arg, char *opts)
> >  		snprintf(buf, BUFSIZ, "%s=", node->field);
> >  		if (opts && strcasestr(opts, buf) != NULL)
> >  			continue;
> > -		/*
> > -		 * Protocol verions can be set in a number of ways
> > -		 */
> > -		if (opts && check_vers(opts, node->field))
> > -			continue;
> >
> >  		if (lookup_entry(node->field) != NULL)
> >  			continue;
> > diff --git a/utils/mount/network.c b/utils/mount/network.c
> > index 4f8c15c..b5ed850 100644
> > --- a/utils/mount/network.c
> > +++ b/utils/mount/network.c
> > @@ -92,9 +92,6 @@ static const char *nfs_version_opttbl[] = {
> >  	"v4",
> >  	"vers",
> >  	"nfsvers",
> > -	"v4.0",
> > -	"v4.1",
> > -	"v4.2",
> >  	NULL,
> >  };
> >
> > @@ -1242,71 +1239,69 @@ nfs_nfs_program(struct mount_options *options, unsigned long *program)
> >   * or FALSE if the option was specified with an invalid value.
> >   */
> >  int
> > -nfs_nfs_version(struct mount_options *options, unsigned long *version)
> > +nfs_nfs_version(struct mount_options *options, struct nfs_version *version)
> >  {
> > -	long tmp;
> > +	char *version_key, *version_val, *cptr;
> > +	int i, found = 0;
> >
> > -	switch (po_rightmost(options, nfs_version_opttbl)) {
> > -	case 0:	/* v2 */
> > -		*version = 2;
> > -		return 1;
> > -	case 1: /* v3 */
> > -		*version = 3;
> > -		return 1;
> > -	case 2: /* v4 */
> > -		*version = 4;
> > -		return 1;
> > -	case 3:	/* vers */
> > -		switch (po_get_numeric(options, "vers", &tmp)) {
> > -		case PO_FOUND:
> > -			if (tmp >= 2 && tmp <= 4) {
> > -				*version = tmp;
> > -				return 1;
> > -			}
> > -			nfs_error(_("%s: parsing error on 'vers=' option\n"),
> > -					progname);
> > -			return 0;
> > -		case PO_NOT_FOUND:
> > -			nfs_error(_("%s: parsing error on 'vers=' option\n"),
> > -					progname);
> > -			return 0;
> > -		case PO_BAD_VALUE:
> > -			nfs_error(_("%s: invalid value for 'vers=' option"),
> > -					progname);
> > -			return 0;
> > -		}
> > -	case 4: /* nfsvers */
> > -		switch (po_get_numeric(options, "nfsvers", &tmp)) {
> > -		case PO_FOUND:
> > -			if (tmp >= 2 && tmp <= 4) {
> > -				*version = tmp;
> > -				return 1;
> > -			}
> > -			nfs_error(_("%s: parsing error on 'nfsvers=' option\n"),
> > -					progname);
> > -			return 0;
> > -		case PO_NOT_FOUND:
> > -			nfs_error(_("%s: parsing error on 'nfsvers=' option\n"),
> > -					progname);
> > -			return 0;
> > -		case PO_BAD_VALUE:
> > -			nfs_error(_("%s: invalid value for 'nfsvers=' option"),
> > -					progname);
> > -			return 0;
> > +	version->v_mode = V_DEFAULT;
> > +
> > +	for (i = 0; nfs_version_opttbl[i]; i++) {
> > +		if (po_contains_prefix(options, nfs_version_opttbl[i],
> > +								&version_key) == PO_FOUND) {
> > +			found++;
> > +			break;
> >  		}
> > -	case 5: /* v4.0 */
> > -	case 6: /* v4.1 */
> > -	case 7: /* v4.2 */
> > -		*version = 4;
> > +	}
> > +
> > +	if (!found)
> >  		return 1;
> > +
> > +	if (i <= 2 ) {
> > +		/* v2, v3, v4 */
> > +		version_val = version_key + 1;
> > +		version->v_mode = V_SPECIFIC;
> > +	} else if (i > 2 ) {
> > +		/* vers=, nfsvers= */
> > +		version_val = po_get(options, version_key);
> >  	}
> >
> > -	/*
> > -	 * NFS version wasn't specified.  The pmap version value
> > -	 * will be filled in later by an rpcbind query in this case.
> > -	 */
> > -	*version = 0;
> > +	if (!version_val)
> > +		goto ret_error;
> > +
> > +	if (!(version->major = strtol(version_val, &cptr, 10)))
> > +		goto ret_error;
> > +
> > +	if (version->major < 4)
> > +		version->v_mode = V_SPECIFIC;
> > +
> > +	if (*cptr == '.') {
> > +		version_val = ++cptr;
> > +		if (!(version->minor = strtol(version_val, &cptr, 10)) && cptr == version_val)
> > +			goto ret_error;
> > +		version->v_mode = V_SPECIFIC;
> > +	} else if (version->major > 3 && *cptr == '\0')
> > +		version->v_mode = V_GENERAL;
> > +
> > +	if (*cptr != '\0')
> > +		goto ret_error;
> > +
> >  	return 1;
> > +
> > +ret_error:
> > +	if (i <= 2 ) {
> > +		nfs_error(_("%s: parsing error on 'v' option"),
> > +			progname);
> > +	} else if (i == 3 ) {
> > +		nfs_error(_("%s: parsing error on 'vers=' option"),
> > +			progname);
> > +	} else if (i == 4) {
> > +		nfs_error(_("%s: parsing error on 'nfsvers=' option"),
> > +			progname);
> > +	}
> > +	version->v_mode = V_PARSE_ERR;
> > +	errno = EINVAL;
> > +	return 0;
> >  }
> >
> >  /*
> > @@ -1625,10 +1620,13 @@ out_err:
> >  int nfs_options2pmap(struct mount_options *options,
> >  		     struct pmap *nfs_pmap, struct pmap *mnt_pmap)
> >  {
> > +	struct nfs_version version;
> > +
> >  	if (!nfs_nfs_program(options, &nfs_pmap->pm_prog))
> >  		return 0;
> > -	if (!nfs_nfs_version(options, &nfs_pmap->pm_vers))
> > +	if (!nfs_nfs_version(options, &version))
> >  		return 0;
> > +	nfs_pmap->pm_vers = version.major;
> >  	if (!nfs_nfs_protocol(options, &nfs_pmap->pm_prot))
> >  		return 0;
> >  	if (!nfs_nfs_port(options, &nfs_pmap->pm_port))
> > diff --git a/utils/mount/network.h b/utils/mount/network.h
> > index d7636d7..9cc5dec 100644
> > --- a/utils/mount/network.h
> > +++ b/utils/mount/network.h
> > @@ -57,9 +57,22 @@ int clnt_ping(struct sockaddr_in *, const unsigned long,
> >
> >  struct mount_options;
> >
> > +enum {
> > +	V_DEFAULT = 0,
> > +	V_GENERAL,
> > +	V_SPECIFIC,
> > +	V_PARSE_ERR,
> > +};
> > +
> > +struct nfs_version {
> > +	unsigned long major;
> > +	unsigned long minor;
> > +	int v_mode;
> > +};
> > +
> >  int nfs_nfs_proto_family(struct mount_options *options, sa_family_t *family);
> >  int nfs_mount_proto_family(struct mount_options *options, sa_family_t *family);
> > -int nfs_nfs_version(struct mount_options *options, unsigned long *version);
> > +int nfs_nfs_version(struct mount_options *options, struct nfs_version *version);
> >  int  nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol);
> >
> >  int nfs_options2pmap(struct mount_options *,
> > diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c
> > index 3538d88..de284f2 100644
> > --- a/utils/mount/nfsumount.c
> > +++ b/utils/mount/nfsumount.c
> > @@ -179,10 +179,10 @@ static int nfs_umount_is_vers4(const struct mntentchn *mc)
> >
> >  		options = po_split(pmc->m.mnt_opts);
> >  		if (options != NULL) {
> > -			unsigned long version;
> > +			struct nfs_version version;
> >  			int rc = nfs_nfs_version(options, &version);
> >  			po_destroy(options);
> > -			if (rc && version == 4)
> > +			if (rc && version.major == 4)
> >  				goto out_nfs4;
> >  		}
> >
> > diff --git a/utils/mount/parse_opt.c b/utils/mount/parse_opt.c
> > index 75a0daa..7ba61c4 100644
> > --- a/utils/mount/parse_opt.c
> > +++ b/utils/mount/parse_opt.c
> > @@ -391,7 +391,7 @@ po_return_t po_append(struct mount_options *options, char *str)
> >  }
> >
> >  /**
> > - * po_contains - check for presense of an option in a group
> > + * po_contains - check for presence of an option in a group
> >   * @options: pointer to mount options
> >   * @keyword: pointer to a C string containing option keyword for which to search
> >   *
> > @@ -410,6 +410,30 @@ po_found_t po_contains(struct mount_options *options, char *keyword)
> >  }
> >
> >  /**
> > + * po_contains_prefix - check for presence of an option matching a prefix
> > + * @options: pointer to mount options
> > + * @prefix: pointer to prefix to match against a keyword
> > + * @keyword: pointer to a C string containing the option keyword if found
> > + *
> > + * On success, *keyword contains the pointer of the matching option's keyword.
> > + */
> > +po_found_t po_contains_prefix(struct mount_options *options,
> > +								const char *prefix, char **keyword)
> > +{
> > +	struct mount_option *option;
> > +
> > +	if (options && prefix) {
> > +		for (option = options->head; option; option = option->next)
> > +			if (strncmp(option->keyword, prefix, strlen(prefix)) == 0) {
> > +				*keyword = option->keyword;
> > +				return PO_FOUND;
> > +			}
> > +	}
> > +
> > +	return PO_NOT_FOUND;
> > +}
> > +
> > +/**
> >   * po_get - return the value of the rightmost instance of an option
> >   * @options: pointer to mount options
> >   * @keyword: pointer to a C string containing option keyword for which to search
> > diff --git a/utils/mount/parse_opt.h b/utils/mount/parse_opt.h
> > index 5037207..0745e0f 100644
> > --- a/utils/mount/parse_opt.h
> > +++ b/utils/mount/parse_opt.h
> > @@ -45,6 +45,8 @@ po_return_t		po_join(struct mount_options *, char **);
> >
> >  po_return_t		po_append(struct mount_options *, char *);
> >  po_found_t		po_contains(struct mount_options *, char *);
> > +po_found_t		po_contains_prefix(struct mount_options *options,
> > +						const char *prefix, char **keyword);
> >  char *			po_get(struct mount_options *, char *);
> >  po_found_t		po_get_numeric(struct mount_options *,
> >  					char *, long *);
> > diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
> > index 2d72d5b..936f65c 100644
> > --- a/utils/mount/stropts.c
> > +++ b/utils/mount/stropts.c
> > @@ -88,30 +88,44 @@ struct nfsmount_info {
> >  	struct mount_options	*options;	/* parsed mount options */
> >  	char			**extra_opts;	/* string for /etc/mtab */
> >
> > -	unsigned long		version;	/* NFS version */
> > +	struct nfs_version	version;	/* NFS version */
> >  	int			flags,		/* MS_ flags */
> >  				fake,		/* actually do the mount? */
> >  				child;		/* forked bg child? */
> >  };
> >
> > -#ifdef MOUNT_CONFIG
> > -static void nfs_default_version(struct nfsmount_info *mi);
> >
> >  static void nfs_default_version(struct nfsmount_info *mi)
> >  {
> > -	extern unsigned long config_default_vers;
> > +#ifdef MOUNT_CONFIG
> > +	extern struct nfs_version config_default_vers;
> >  	/*
> >  	 * Use the default value set in the config file when
> >  	 * the version has not been explicitly set.
> >  	 */
> > -	if (mi->version == 0 && config_default_vers) {
> > -		if (config_default_vers < 4)
> > -			mi->version = config_default_vers;
> > +	if (config_default_vers.v_mode == V_PARSE_ERR) {
> > +		mi->version.v_mode = V_PARSE_ERR;
> > +		return;
> >  	}
> > -}
> > -#else
> > -inline void nfs_default_version(__attribute__ ((unused)) struct nfsmount_info *mi) {}
> > +
> > +	if (mi->version.v_mode == V_DEFAULT &&
> > +		config_default_vers.v_mode != V_DEFAULT) {
> > +		mi->version.major = config_default_vers.major;
> > +		mi->version.minor = config_default_vers.minor;
> > +		return;
> > +	}
> > +
> > +	if (mi->version.v_mode == V_GENERAL &&
> > +		config_default_vers.v_mode != V_DEFAULT) {
> > +		if (mi->version.major == config_default_vers.major)
> > +			mi->version.minor = config_default_vers.minor;
> > +		return;
> > +	}
> > +
> >  #endif /* MOUNT_CONFIG */
> > +	mi->version.major = 4;
> > +	mi->version.minor = 0;
> > +}
> >
> >  /*
> >   * Obtain a retry timeout value based on the value of the "retry=" option.
> > @@ -300,7 +314,7 @@ static int nfs_set_version(struct nfsmount_info *mi)
> >  		return 0;
> >
> >  	if (strncmp(mi->type, "nfs4", 4) == 0)
> > -		mi->version = 4;
> > +		mi->version.major = 4;
> >
> >  	/*
> >  	 * Before 2.6.32, the kernel NFS client didn't
> > @@ -308,28 +322,44 @@ static int nfs_set_version(struct nfsmount_info *mi)
> >  	 * 4 cannot be included when autonegotiating
> >  	 * while running on those kernels.
> >  	 */
> > -	if (mi->version == 0 &&
> > -	    linux_version_code() <= MAKE_VERSION(2, 6, 31))
> > -		mi->version = 3;
> > +	if (mi->version.v_mode == V_DEFAULT &&
> > +	    linux_version_code() <= MAKE_VERSION(2, 6, 31)) {
> > +		mi->version.major = 3;
> > +		mi->version.v_mode = V_SPECIFIC;
> > +	}
> >
> >  	/*
> >  	 * If we still don't know, check for version-specific
> >  	 * mount options.
> >  	 */
> > -	if (mi->version == 0) {
> > +	if (mi->version.v_mode == V_DEFAULT) {
> >  		if (po_contains(mi->options, "mounthost") ||
> >  		    po_contains(mi->options, "mountaddr") ||
> >  		    po_contains(mi->options, "mountvers") ||
> > -		    po_contains(mi->options, "mountproto"))
> > -			mi->version = 3;
> > +		    po_contains(mi->options, "mountproto")) {
> > +			mi->version.major = 3;
> > +			mi->version.v_mode = V_SPECIFIC;
> > +		}
> >  	}
> >
> >  	/*
> >  	 * If enabled, see if the default version was
> >  	 * set in the config file
> >  	 */
> > -	nfs_default_version(mi);
> > -
> > +	if (mi->version.v_mode != V_SPECIFIC) {
> > +		nfs_default_version(mi);
> > +		/*
> > +		 * If the version was not specifically set, it will
> > +		 * be set by autonegotiation later, so remove it now:
> > +		 */
> > +		po_remove_all(mi->options, "v4");
> > +		po_remove_all(mi->options, "vers");
> > +		po_remove_all(mi->options, "nfsvers");
> > +	}
> > +
> > +	if (mi->version.v_mode == V_PARSE_ERR)
> > +		return 0;
> > +
> >  	return 1;
> >  }
> >
> > @@ -684,6 +714,7 @@ static int nfs_do_mount_v4(struct nfsmount_info *mi,
> >  {
> >  	struct mount_options *options = po_dup(mi->options);
> >  	int result = 0;
> > +	char version_opt[16];
> >  	char *extra_opts = NULL;
> >
> >  	if (!options) {
> > @@ -691,20 +722,24 @@ static int nfs_do_mount_v4(struct nfsmount_info *mi,
> >  		return result;
> >  	}
> >
> > -	if (mi->version == 0) {
> > -		if (po_contains(options, "mounthost") ||
> > -			po_contains(options, "mountaddr") ||
> > -			po_contains(options, "mountvers") ||
> > -			po_contains(options, "mountproto")) {
> > -		/*
> > -		 * Since these mountd options are set assume version 3
> > -		 * is wanted so error out with EPROTONOSUPPORT so the
> > -		 * protocol negation starts with v3.
> > -		 */
> > -			errno = EPROTONOSUPPORT;
> > -			goto out_fail;
> > -		}
> > -		if (po_append(options, "vers=4") == PO_FAILED) {
> > +	if (po_contains(options, "mounthost") ||
> > +		po_contains(options, "mountaddr") ||
> > +		po_contains(options, "mountvers") ||
> > +		po_contains(options, "mountproto")) {
> > +	/*
> > +	 * Since these mountd options are set assume version 3
> > +	 * is wanted so error out with EPROTONOSUPPORT so the
> > +	 * protocol negation starts with v3.
> > +	 */
> > +		errno = EPROTONOSUPPORT;
> > +		goto out_fail;
> > +	}
> > +
> > +	if (mi->version.v_mode != V_SPECIFIC) {
> > +		snprintf(version_opt, sizeof(version_opt) - 1,
> > +			"vers=%lu.%lu", mi->version.major, mi->version.minor);
> > +
> > +		if (po_append(options, version_opt) == PO_FAILED) {
> >  			errno = EINVAL;
> >  			goto out_fail;
> >  		}
> > @@ -792,14 +827,25 @@ static int nfs_autonegotiate(struct nfsmount_info *mi)
> >  	int result;
> >
> >  	result = nfs_try_mount_v4(mi);
> > +check_result:
> >  	if (result)
> >  		return result;
> >
> > -check_errno:
> >  	switch (errno) {
> >  	case EPROTONOSUPPORT:
> >  		/* A clear indication that the server or our
> > -		 * client does not support NFS version 4. */
> > +		 * client does not support NFS version 4 and minor */
> > +		if (mi->version.v_mode == V_GENERAL &&
> > +			mi->version.minor == 0)
> > +				return result;
> > +		if (mi->version.v_mode != V_SPECIFIC) {
> > +			if (mi->version.minor > 0) {
> > +				mi->version.minor--;
> > +				result = nfs_try_mount_v4(mi);
> > +				goto check_result;
> > +			}
> > +		}
> > +
> >  		goto fall_back;
> >  	case ENOENT:
> >  		/* Legacy Linux servers don't export an NFS
> > @@ -818,7 +864,7 @@ check_errno:
> >  			/* v4 server seems to be registered now. */
> >  			result = nfs_try_mount_v4(mi);
> >  			if (result == 0 && errno != ECONNREFUSED)
> > -				goto check_errno;
> > +				goto check_result;
> >  		}
> >  		return result;
> >  	default:
> > @@ -839,19 +885,19 @@ static int nfs_try_mount(struct nfsmount_info *mi)
> >  {
> >  	int result = 0;
> >
> > -	switch (mi->version) {
> > -	case 0:
> > -		result = nfs_autonegotiate(mi);
> > -		break;
> > -	case 2:
> > -	case 3:
> > -		result = nfs_try_mount_v3v2(mi, FALSE);
> > -		break;
> > -	case 4:
> > -		result = nfs_try_mount_v4(mi);
> > -		break;
> > -	default:
> > -		errno = EIO;
> > +	switch (mi->version.major) {
> > +		case 2:
> > +		case 3:
> > +			result = nfs_try_mount_v3v2(mi, FALSE);
> > +			break;
> > +		case 4:
> > +			if (mi->version.v_mode != V_SPECIFIC)
> > +				result = nfs_autonegotiate(mi);
> > +			else
> > +				result = nfs_try_mount_v4(mi);
> > +			break;
> > +		default:
> > +			errno = EIO;
> >  	}
> >
> >  	return result;
> >
>

[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux