#ident "$Id: mount_smbfs.c,v 1.4 1998/11/03 21:05:17 hpa Exp $"
/* ----------------------------------------------------------------------- *
 *   
 * mount_smbfs.c
 *
 *  Module for Linux automountd to mount an SMB/CIFS filesystem
 *
 *  Mount point input format expected is: //server/service[/root_path...]
 *
 *   Copyright 1997-1998 Transmeta Corporation - All Rights Reserved
 *
 *   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, Inc., 675 Mass Ave, Cambridge MA 02139,
 *   USA; either version 2 of the License, or (at your option) any later
 *   version; incorporated herein by reference.
 *
 * ----------------------------------------------------------------------- */

#include <stdio.h>
#include <malloc.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <syslog.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>

#define MODULE_MOUNT
#include "automount.h"

#define MODPREFIX "mount(smbfs): "
int mount_version = AUTOFS_MOUNT_VERSION; /* Required by protocol */

struct smb_mount_opt {
  char *optname;		/* mount-style option name */
  char *optflag;		/* command-line flag for smbmount */
  int hasarg;			/* takes argument? */
  int satisfies_pwd;		/* true if this means we don't need a -n */
};

static struct smb_mount_opt mount_opt_list[] = {
  { "nocaps",    "-C", 0, 0 },
  { "guest",     "-n", 0, 1 },
  { "passwd",    "-P", 1, 1 },
  { "srvname",   "-s", 1, 0 },
  { "mysmbname", "-c", 1, 0 },
  { "login",     "-U", 1, 0 },
  { "uid",       "-u", 1, 0 },
  { "gid",       "-g", 1, 0 },
  { "filemod",   "-f", 1, 0 },
  { "dirmod",    "-d", 1, 0 },
  { "port",      "-p", 1, 0 },
  { "maxxmit",   "-m", 1, 0 },
  { NULL, NULL, 0, 0 }
};

int mount_init(void **context)
{
  return 0;
}

static int smb_parse_options(char *optstr, const char **argv,
			     char *qbuf, int *qbuflen)
{
  char *opt;
  int ln;
  int argc;
  int has_pwd;
  int qbufchr, qln;
  struct smb_mount_opt *mount_opt;

  has_pwd = 0;
  qbufchr = 0;
  argc = 0;

  if ( optstr ) {
    for ( opt = strtok(optstr, ",") ; opt ; opt = strtok(NULL, ",") ) {
      for ( mount_opt = mount_opt_list ; mount_opt->optname ; mount_opt++ ) {
	if ( mount_opt->hasarg ) {
	  ln = strlen(mount_opt->optname);
	  if ( !strncmp(opt, mount_opt->optname, ln) && opt[ln] == '=' ) {
	    qln = strlen(opt)-ln;
	    if ( argv ) {
	      *(argv++) = mount_opt->optflag;
	      memcpy(qbuf, opt+ln+1, qln);
	      *(argv++) = qbuf;
	      qbuf += qln;
	    }
	    qbufchr += qln;
	    has_pwd = has_pwd || mount_opt->satisfies_pwd;
	    argc += 2;
	    break;
	  }
	} else {
	  if ( !strcmp(opt, mount_opt->optname) ) {
	    if ( argv )
	      *(argv++) = mount_opt->optflag;
	    has_pwd = has_pwd || mount_opt->satisfies_pwd;
	    argc++;
	    break;
	  }
	}
      }
      /* Ignore unknown options */
    }
  }

  if ( !has_pwd ) {
    syslog(LOG_DEBUG, MODPREFIX "no password option, adding -n");
    if ( argv )
      *(argv++) = "-n";
    argc++;
  }
  
  if ( argv )
    *argv = NULL;

  if ( qbuflen )
    *qbuflen = qbufchr;

  return argc;
}

int mount_mount(const char *root, const char *name, int name_len,
		const char *what, const char *fstype, const char *options,
		void *context)
{
  char *fullpath, *optcopy;
  int err;
  char *qbuf;
  int argc, optsize, qbuflen;
  const char **argv;

  fullpath = alloca(strlen(root)+name_len+2);
  if (options)
    optcopy  = alloca(optsize = strlen(options)+1);
  else {
    optsize = 0;
    optcopy = NULL;
  }
  if ( !fullpath || (!optcopy && optsize)) {
    syslog(LOG_ERR, MODPREFIX "alloca: %m");
    return 1;
  }
  sprintf(fullpath, "%s/%s", root, name);

  if (optsize)
    memcpy(optcopy, options, optsize);

  argc = smb_parse_options(optcopy, NULL, NULL, &qbuflen) + 4;
  argv = alloca(sizeof(char *) * argc);
  qbuf = alloca(qbuflen);
  if ( !argv || (qbuflen && !qbuf) ) {
    syslog(LOG_ERR, MODPREFIX "alloca: %m");
    return 1;
  }
  argv[0] = PATH_SMBMOUNT;
  argv[1] = what;
  argv[2] = fullpath;
  if (optsize)
    memcpy(optcopy, options, optsize);
  smb_parse_options(optcopy, argv+3, qbuf, NULL);

  syslog(LOG_DEBUG, MODPREFIX "calling mkdir %s", fullpath);
  if ( mkdir(fullpath, 0555) && errno != EEXIST ) {
    syslog(LOG_NOTICE, MODPREFIX "mkdir %s failed: %m", name);
    return 1;
  }

  err = spawnv(LOG_NOTICE, PATH_SMBMOUNT, argv);
 
  if ( err ) {
    rmdir(fullpath);
    syslog(LOG_NOTICE, MODPREFIX "failed to mount %s on %s", what, fullpath);
    return 1;
  } else {
    syslog(LOG_DEBUG, MODPREFIX "mounted %s on %s", what, fullpath);
    return 0;
  }
}

int mount_done(void *context)
{
  return 0;
}
