wmi-1.3.16 from opsview.com
This commit is contained in:
@@ -0,0 +1,601 @@
|
||||
/*
|
||||
CIFSDD - dd for SMB.
|
||||
Main program, argument handling and block copying.
|
||||
|
||||
Copyright (C) James Peach 2005-2006
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/filesys.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "lib/cmdline/popt_common.h"
|
||||
|
||||
#include "cifsdd.h"
|
||||
|
||||
const char * const PROGNAME = "cifsdd";
|
||||
|
||||
#define SYNTAX_EXIT_CODE 1 /* Invokation syntax or logic error. */
|
||||
#define EOM_EXIT_CODE 9 /* Out of memory error. */
|
||||
#define FILESYS_EXIT_CODE 10 /* File manipulation error. */
|
||||
#define IOERROR_EXIT_CODE 11 /* Error during IO phase. */
|
||||
|
||||
struct dd_stats_record dd_stats;
|
||||
|
||||
static int dd_sigint;
|
||||
static int dd_sigusr1;
|
||||
|
||||
static void dd_handle_signal(int sig)
|
||||
{
|
||||
switch (sig)
|
||||
{
|
||||
case SIGINT:
|
||||
++dd_sigint;
|
||||
break;
|
||||
case SIGUSR1:
|
||||
++dd_sigusr1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Argument handling. */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static const char * argtype_str(enum argtype arg_type)
|
||||
{
|
||||
static const struct {
|
||||
enum argtype arg_type;
|
||||
const char * arg_name;
|
||||
} names [] =
|
||||
{
|
||||
{ ARG_NUMERIC, "COUNT" },
|
||||
{ ARG_SIZE, "SIZE" },
|
||||
{ ARG_PATHNAME, "FILE" },
|
||||
{ ARG_BOOL, "BOOLEAN" },
|
||||
};
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(names); ++i) {
|
||||
if (arg_type == names[i].arg_type) {
|
||||
return(names[i].arg_name);
|
||||
}
|
||||
}
|
||||
|
||||
return("<unknown>");
|
||||
}
|
||||
|
||||
static struct argdef args[] =
|
||||
{
|
||||
{ "bs", ARG_SIZE, "force ibs and obs to SIZE bytes" },
|
||||
{ "ibs", ARG_SIZE, "read SIZE bytes at a time" },
|
||||
{ "obs", ARG_SIZE, "write SIZE bytes at a time" },
|
||||
|
||||
{ "count", ARG_NUMERIC, "copy COUNT input blocks" },
|
||||
{ "seek",ARG_NUMERIC, "skip COUNT blocks at start of output" },
|
||||
{ "skip",ARG_NUMERIC, "skip COUNT blocks at start of input" },
|
||||
|
||||
{ "if", ARG_PATHNAME, "read input from FILE" },
|
||||
{ "of", ARG_PATHNAME, "write output to FILE" },
|
||||
|
||||
{ "direct", ARG_BOOL, "use direct I/O if non-zero" },
|
||||
{ "sync", ARG_BOOL, "use synchronous writes if non-zero" },
|
||||
{ "oplock", ARG_BOOL, "take oplocks on the input and output files" },
|
||||
|
||||
/* FIXME: We should support using iflags and oflags for setting oplock and I/O
|
||||
* options. This would make us compatible with GNU dd.
|
||||
*/
|
||||
};
|
||||
|
||||
struct argdef * find_named_arg(const char * arg)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(args); ++i) {
|
||||
if (strwicmp(arg, args[i].arg_name) == 0) {
|
||||
return(&args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
int set_arg_argv(const char * argv)
|
||||
{
|
||||
struct argdef * arg;
|
||||
|
||||
char * name;
|
||||
char * val;
|
||||
|
||||
if ((name = strdup(argv)) == NULL) {
|
||||
return(0);
|
||||
}
|
||||
|
||||
if ((val = strchr(name, '=')) == NULL) {
|
||||
fprintf(stderr, "%s: malformed argument \"%s\"\n",
|
||||
PROGNAME, argv);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*val = '\0';
|
||||
val++;
|
||||
|
||||
if ((arg = find_named_arg(name)) == NULL) {
|
||||
fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
|
||||
PROGNAME, name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Found a matching name; convert the variable argument. */
|
||||
switch (arg->arg_type) {
|
||||
case ARG_NUMERIC:
|
||||
if (!conv_str_u64(val, &arg->arg_val.nval)) {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case ARG_SIZE:
|
||||
if (!conv_str_size(val, &arg->arg_val.nval)) {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case ARG_BOOL:
|
||||
if (!conv_str_bool(val, &arg->arg_val.bval)) {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case ARG_PATHNAME:
|
||||
if (!(arg->arg_val.pval = strdup(val))) {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: argument \"%s\" is of "
|
||||
"unknown type\n", PROGNAME, name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
free(name);
|
||||
return(1);
|
||||
|
||||
fail:
|
||||
free(name);
|
||||
return(0);
|
||||
}
|
||||
|
||||
void set_arg_val(const char * name, ...)
|
||||
{
|
||||
va_list ap;
|
||||
struct argdef * arg;
|
||||
|
||||
va_start(ap, name);
|
||||
if ((arg = find_named_arg(name)) == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Found a matching name; convert the variable argument. */
|
||||
switch (arg->arg_type) {
|
||||
case ARG_NUMERIC:
|
||||
case ARG_SIZE:
|
||||
arg->arg_val.nval = va_arg(ap, uint64_t);
|
||||
break;
|
||||
case ARG_BOOL:
|
||||
arg->arg_val.bval = va_arg(ap, int);
|
||||
break;
|
||||
case ARG_PATHNAME:
|
||||
arg->arg_val.pval = va_arg(ap, char *);
|
||||
if (arg->arg_val.pval) {
|
||||
arg->arg_val.pval = strdup(arg->arg_val.pval);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: argument \"%s\" is of "
|
||||
"unknown type\n", PROGNAME, name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
return;
|
||||
|
||||
fail:
|
||||
fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
|
||||
PROGNAME, name);
|
||||
va_end(ap);
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL check_arg_bool(const char * name)
|
||||
{
|
||||
struct argdef * arg;
|
||||
|
||||
if ((arg = find_named_arg(name)) &&
|
||||
(arg->arg_type == ARG_BOOL)) {
|
||||
return(arg->arg_val.bval);
|
||||
}
|
||||
|
||||
DEBUG(0, ("invalid argument name: %s", name));
|
||||
SMB_ASSERT(0);
|
||||
return(False);
|
||||
}
|
||||
|
||||
uint64_t check_arg_numeric(const char * name)
|
||||
{
|
||||
struct argdef * arg;
|
||||
|
||||
if ((arg = find_named_arg(name)) &&
|
||||
(arg->arg_type == ARG_NUMERIC || arg->arg_type == ARG_SIZE)) {
|
||||
return(arg->arg_val.nval);
|
||||
}
|
||||
|
||||
DEBUG(0, ("invalid argument name: %s", name));
|
||||
SMB_ASSERT(0);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
const char * check_arg_pathname(const char * name)
|
||||
{
|
||||
struct argdef * arg;
|
||||
|
||||
if ((arg = find_named_arg(name)) &&
|
||||
(arg->arg_type == ARG_PATHNAME)) {
|
||||
return(arg->arg_val.pval);
|
||||
}
|
||||
|
||||
DEBUG(0, ("invalid argument name: %s", name));
|
||||
SMB_ASSERT(0);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
static void dump_args(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
DEBUG(10, ("dumping argument values:\n"));
|
||||
for (i = 0; i < ARRAY_SIZE(args); ++i) {
|
||||
switch (args[i].arg_type) {
|
||||
case ARG_NUMERIC:
|
||||
case ARG_SIZE:
|
||||
DEBUG(10, ("\t%s=%llu\n", args[i].arg_name,
|
||||
(unsigned long long)args[i].arg_val.nval));
|
||||
break;
|
||||
case ARG_BOOL:
|
||||
DEBUG(10, ("\t%s=%s\n", args[i].arg_name,
|
||||
args[i].arg_val.bval ? "yes" : "no"));
|
||||
break;
|
||||
case ARG_PATHNAME:
|
||||
DEBUG(10, ("\t%s=%s\n", args[i].arg_name,
|
||||
args[i].arg_val.pval ?
|
||||
args[i].arg_val.pval :
|
||||
"(NULL)"));
|
||||
break;
|
||||
default:
|
||||
SMB_ASSERT(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cifsdd_help_message(poptContext pctx,
|
||||
enum poptCallbackReason preason,
|
||||
struct poptOption * poption,
|
||||
const char * parg,
|
||||
void * pdata)
|
||||
{
|
||||
static const char notes[] =
|
||||
"FILE can be a local filename or a UNC path of the form //server/share/path.\n";
|
||||
|
||||
char prefix[24];
|
||||
int i;
|
||||
|
||||
if (poption->shortName != '?') {
|
||||
poptPrintUsage(pctx, stdout, 0);
|
||||
fprintf(stdout, " [dd options]\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
poptPrintHelp(pctx, stdout, 0);
|
||||
fprintf(stdout, "\nCIFS dd options:\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(args); ++i) {
|
||||
if (args[i].arg_name == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
snprintf(prefix, sizeof(prefix), "%s=%-*s",
|
||||
args[i].arg_name,
|
||||
(int)(sizeof(prefix) - strlen(args[i].arg_name) - 2),
|
||||
argtype_str(args[i].arg_type));
|
||||
prefix[sizeof(prefix) - 1] = '\0';
|
||||
fprintf(stdout, " %s%s\n", prefix, args[i].arg_help);
|
||||
}
|
||||
|
||||
fprintf(stdout, "\n%s\n", notes);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Main block copying routine. */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static void print_transfer_stats(void)
|
||||
{
|
||||
if (DEBUGLEVEL > 0) {
|
||||
printf("%llu+%llu records in (%llu bytes)\n"
|
||||
"%llu+%llu records out (%llu bytes)\n",
|
||||
(unsigned long long)dd_stats.in.fblocks,
|
||||
(unsigned long long)dd_stats.in.pblocks,
|
||||
(unsigned long long)dd_stats.in.bytes,
|
||||
(unsigned long long)dd_stats.out.fblocks,
|
||||
(unsigned long long)dd_stats.out.pblocks,
|
||||
(unsigned long long)dd_stats.out.bytes);
|
||||
} else {
|
||||
printf("%llu+%llu records in\n%llu+%llu records out\n",
|
||||
(unsigned long long)dd_stats.in.fblocks,
|
||||
(unsigned long long)dd_stats.in.pblocks,
|
||||
(unsigned long long)dd_stats.out.fblocks,
|
||||
(unsigned long long)dd_stats.out.pblocks);
|
||||
}
|
||||
}
|
||||
|
||||
static struct dd_iohandle * open_file(const char * which)
|
||||
{
|
||||
int options = 0;
|
||||
const char * path = NULL;
|
||||
struct dd_iohandle * handle = NULL;
|
||||
|
||||
if (check_arg_bool("direct")) {
|
||||
options |= DD_DIRECT_IO;
|
||||
}
|
||||
|
||||
if (check_arg_bool("sync")) {
|
||||
options |= DD_SYNC_IO;
|
||||
}
|
||||
|
||||
if (check_arg_bool("oplock")) {
|
||||
options |= DD_OPLOCK;
|
||||
}
|
||||
|
||||
if (strcmp(which, "if") == 0) {
|
||||
path = check_arg_pathname("if");
|
||||
handle = dd_open_path(path, check_arg_numeric("ibs"),
|
||||
options);
|
||||
} else if (strcmp(which, "of") == 0) {
|
||||
options |= DD_WRITE;
|
||||
path = check_arg_pathname("of");
|
||||
handle = dd_open_path(path, check_arg_numeric("obs"),
|
||||
options);
|
||||
} else {
|
||||
SMB_ASSERT(0);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
if (!handle) {
|
||||
fprintf(stderr, "%s: failed to open %s\n", PROGNAME, path);
|
||||
}
|
||||
|
||||
return(handle);
|
||||
}
|
||||
|
||||
static void set_max_xmit(uint64_t iomax)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)iomax);
|
||||
lp_set_cmdline("max xmit", buf);
|
||||
}
|
||||
|
||||
static int copy_files(void)
|
||||
{
|
||||
uint8_t * iobuf; /* IO buffer. */
|
||||
uint64_t iomax; /* Size of the IO buffer. */
|
||||
uint64_t data_size; /* Amount of data in the IO buffer. */
|
||||
|
||||
uint64_t ibs;
|
||||
uint64_t obs;
|
||||
uint64_t count;
|
||||
|
||||
struct dd_iohandle * ifile;
|
||||
struct dd_iohandle * ofile;
|
||||
|
||||
ibs = check_arg_numeric("ibs");
|
||||
obs = check_arg_numeric("obs");
|
||||
count = check_arg_numeric("count");
|
||||
|
||||
/* Allocate IO buffer. We need more than the max IO size because we
|
||||
* could accumulate a remainder if ibs and obs don't match.
|
||||
*/
|
||||
iomax = 2 * MAX(ibs, obs);
|
||||
if ((iobuf = malloc(iomax)) == NULL) {
|
||||
fprintf(stderr,
|
||||
"%s: failed to allocate IO buffer of %llu bytes\n",
|
||||
PROGNAME, (unsigned long long)iomax);
|
||||
return(EOM_EXIT_CODE);
|
||||
}
|
||||
|
||||
set_max_xmit(MAX(ibs, obs));
|
||||
|
||||
DEBUG(4, ("IO buffer size is %llu, max xmit is %d\n",
|
||||
(unsigned long long)iomax, lp_max_xmit()));
|
||||
|
||||
if (!(ifile = open_file("if"))) {
|
||||
return(FILESYS_EXIT_CODE);
|
||||
}
|
||||
|
||||
if (!(ofile = open_file("of"))) {
|
||||
return(FILESYS_EXIT_CODE);
|
||||
}
|
||||
|
||||
/* Seek the files to their respective starting points. */
|
||||
ifile->io_seek(ifile, check_arg_numeric("skip") * ibs);
|
||||
ofile->io_seek(ofile, check_arg_numeric("seek") * obs);
|
||||
|
||||
DEBUG(4, ("max xmit was negotiated to be %d\n", lp_max_xmit()));
|
||||
|
||||
for (data_size = 0;;) {
|
||||
|
||||
/* Handle signals. We are somewhat compatible with GNU dd.
|
||||
* SIGINT makes us stop, but still print transfer statistics.
|
||||
* SIGUSR1 makes us print transfer statistics but we continue
|
||||
* copying.
|
||||
*/
|
||||
if (dd_sigint) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (dd_sigusr1) {
|
||||
print_transfer_stats();
|
||||
dd_sigusr1 = 0;
|
||||
}
|
||||
|
||||
if (ifile->io_flags & DD_END_OF_FILE) {
|
||||
DEBUG(4, ("flushing %llu bytes at EOF\n",
|
||||
(unsigned long long)data_size));
|
||||
while (data_size > 0) {
|
||||
if (!dd_flush_block(ofile, iobuf,
|
||||
&data_size, obs)) {
|
||||
return(IOERROR_EXIT_CODE);
|
||||
}
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Try and read enough blocks of ibs bytes to be able write
|
||||
* out one of obs bytes.
|
||||
*/
|
||||
if (!dd_fill_block(ifile, iobuf, &data_size, obs, ibs)) {
|
||||
return(IOERROR_EXIT_CODE);
|
||||
}
|
||||
|
||||
if (data_size == 0) {
|
||||
/* Done. */
|
||||
SMB_ASSERT(ifile->io_flags & DD_END_OF_FILE);
|
||||
}
|
||||
|
||||
/* Stop reading when we hit the block count. */
|
||||
if (dd_stats.in.bytes >= (ibs * count)) {
|
||||
ifile->io_flags |= DD_END_OF_FILE;
|
||||
}
|
||||
|
||||
/* If we wanted to be a legitimate dd, we would do character
|
||||
* conversions and other shenanigans here.
|
||||
*/
|
||||
|
||||
/* Flush what we read in units of obs bytes. We want to have
|
||||
* at least obs bytes in the IO buffer but might not if the
|
||||
* file is too small.
|
||||
*/
|
||||
if (data_size &&
|
||||
!dd_flush_block(ofile, iobuf, &data_size, obs)) {
|
||||
return(IOERROR_EXIT_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
print_transfer_stats();
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Main. */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
struct poptOption cifsddHelpOptions[] = {
|
||||
{ NULL, '\0', POPT_ARG_CALLBACK, (void *)&cifsdd_help_message, '\0', NULL, NULL },
|
||||
{ "help", '?', 0, NULL, '?', "Show this help message", NULL },
|
||||
{ "usage", '\0', 0, NULL, 'u', "Display brief usage message", NULL },
|
||||
{ NULL }
|
||||
} ;
|
||||
|
||||
int main(int argc, const char ** argv)
|
||||
{
|
||||
int i;
|
||||
const char ** dd_args;
|
||||
|
||||
poptContext pctx;
|
||||
struct poptOption poptions[] = {
|
||||
/* POPT_AUTOHELP */
|
||||
{ NULL, '\0', POPT_ARG_INCLUDE_TABLE, cifsddHelpOptions,
|
||||
0, "Help options:", NULL },
|
||||
POPT_COMMON_SAMBA
|
||||
POPT_COMMON_CONNECTION
|
||||
POPT_COMMON_CREDENTIALS
|
||||
POPT_COMMON_VERSION
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
/* Block sizes. */
|
||||
set_arg_val("bs", (uint64_t)4096);
|
||||
set_arg_val("ibs", (uint64_t)4096);
|
||||
set_arg_val("obs", (uint64_t)4096);
|
||||
/* Block counts. */
|
||||
set_arg_val("count", (uint64_t)-1);
|
||||
set_arg_val("seek", (uint64_t)0);
|
||||
set_arg_val("seek", (uint64_t)0);
|
||||
/* Files. */
|
||||
set_arg_val("if", NULL);
|
||||
set_arg_val("of", NULL);
|
||||
/* Options. */
|
||||
set_arg_val("direct", False);
|
||||
set_arg_val("sync", False);
|
||||
set_arg_val("oplock", False);
|
||||
|
||||
pctx = poptGetContext(PROGNAME, argc, argv, poptions, 0);
|
||||
while ((i = poptGetNextOpt(pctx)) != -1) {
|
||||
;
|
||||
}
|
||||
|
||||
for (dd_args = poptGetArgs(pctx); dd_args && *dd_args; ++dd_args) {
|
||||
|
||||
if (!set_arg_argv(*dd_args)) {
|
||||
fprintf(stderr, "%s: invalid option: %s\n",
|
||||
PROGNAME, *dd_args);
|
||||
exit(SYNTAX_EXIT_CODE);
|
||||
}
|
||||
|
||||
/* "bs" has the side-effect of setting "ibs" and "obs". */
|
||||
if (strncmp(*dd_args, "bs=", 3) == 0) {
|
||||
uint64_t bs = check_arg_numeric("bs");
|
||||
set_arg_val("ibs", bs);
|
||||
set_arg_val("obs", bs);
|
||||
}
|
||||
}
|
||||
|
||||
gensec_init();
|
||||
dump_args();
|
||||
|
||||
if (check_arg_numeric("ibs") == 0 || check_arg_numeric("ibs") == 0) {
|
||||
fprintf(stderr, "%s: block sizes must be greater that zero\n",
|
||||
PROGNAME);
|
||||
exit(SYNTAX_EXIT_CODE);
|
||||
}
|
||||
|
||||
if (check_arg_pathname("if") == NULL) {
|
||||
fprintf(stderr, "%s: missing input filename\n", PROGNAME);
|
||||
exit(SYNTAX_EXIT_CODE);
|
||||
}
|
||||
|
||||
if (check_arg_pathname("of") == NULL) {
|
||||
fprintf(stderr, "%s: missing output filename\n", PROGNAME);
|
||||
exit(SYNTAX_EXIT_CODE);
|
||||
}
|
||||
|
||||
CatchSignal(SIGINT, dd_handle_signal);
|
||||
CatchSignal(SIGUSR1, dd_handle_signal);
|
||||
return(copy_files());
|
||||
}
|
||||
|
||||
/* vim: set sw=8 sts=8 ts=8 tw=79 : */
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
CIFSDD - dd for SMB.
|
||||
Declarations and administrivia.
|
||||
|
||||
Copyright (C) James Peach 2005-2006
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
extern const char * const PROGNAME;
|
||||
|
||||
enum argtype
|
||||
{
|
||||
ARG_NUMERIC,
|
||||
ARG_SIZE,
|
||||
ARG_PATHNAME,
|
||||
ARG_BOOL,
|
||||
};
|
||||
|
||||
struct argdef
|
||||
{
|
||||
const char * arg_name;
|
||||
enum argtype arg_type;
|
||||
const char * arg_help;
|
||||
|
||||
union
|
||||
{
|
||||
BOOL bval;
|
||||
uint64_t nval;
|
||||
const char * pval;
|
||||
} arg_val;
|
||||
};
|
||||
|
||||
int set_arg_argv(const char * argv);
|
||||
void set_arg_val(const char * name, ...);
|
||||
|
||||
BOOL check_arg_bool(const char * name);
|
||||
uint64_t check_arg_numeric(const char * name);
|
||||
const char * check_arg_pathname(const char * name);
|
||||
|
||||
typedef BOOL (*dd_seek_func)(void * handle, uint64_t offset);
|
||||
typedef BOOL (*dd_read_func)(void * handle, uint8_t * buf,
|
||||
uint64_t wanted, uint64_t * actual);
|
||||
typedef BOOL (*dd_write_func)(void * handle, uint8_t * buf,
|
||||
uint64_t wanted, uint64_t * actual);
|
||||
|
||||
struct dd_stats_record
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint64_t fblocks; /* Full blocks. */
|
||||
uint64_t pblocks; /* Partial blocks. */
|
||||
uint64_t bytes; /* Total bytes read. */
|
||||
} in;
|
||||
struct
|
||||
{
|
||||
uint64_t fblocks; /* Full blocks. */
|
||||
uint64_t pblocks; /* Partial blocks. */
|
||||
uint64_t bytes; /* Total bytes written. */
|
||||
} out;
|
||||
};
|
||||
|
||||
extern struct dd_stats_record dd_stats;
|
||||
|
||||
struct dd_iohandle
|
||||
{
|
||||
dd_seek_func io_seek;
|
||||
dd_read_func io_read;
|
||||
dd_write_func io_write;
|
||||
int io_flags;
|
||||
};
|
||||
|
||||
#define DD_END_OF_FILE 0x10000000
|
||||
|
||||
#define DD_DIRECT_IO 0x00000001
|
||||
#define DD_SYNC_IO 0x00000002
|
||||
#define DD_WRITE 0x00000004
|
||||
#define DD_OPLOCK 0x00000008
|
||||
|
||||
struct dd_iohandle * dd_open_path(const char * path,
|
||||
uint64_t io_size, int options);
|
||||
BOOL dd_fill_block(struct dd_iohandle * h, uint8_t * buf,
|
||||
uint64_t * buf_size, uint64_t need_size, uint64_t block_size);
|
||||
BOOL dd_flush_block(struct dd_iohandle * h, uint8_t * buf,
|
||||
uint64_t * buf_size, uint64_t block_size);
|
||||
|
||||
/* vim: set sw=8 sts=8 ts=8 tw=79 : */
|
||||
@@ -0,0 +1,496 @@
|
||||
/*
|
||||
CIFSDD - dd for SMB.
|
||||
IO routines, generic and specific.
|
||||
|
||||
Copyright (C) James Peach 2005-2006
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/filesys.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "lib/cmdline/popt_common.h"
|
||||
|
||||
#include "cifsdd.h"
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* UNIX file descriptor IO. */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
struct fd_handle
|
||||
{
|
||||
struct dd_iohandle h;
|
||||
int fd;
|
||||
};
|
||||
|
||||
#define IO_HANDLE_TO_FD(h) (((struct fd_handle *)(h))->fd)
|
||||
|
||||
static BOOL fd_seek_func(void * handle, uint64_t offset)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
ret = lseek(IO_HANDLE_TO_FD(handle), offset, SEEK_SET);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "%s: seek failed: %s\n",
|
||||
PROGNAME, strerror(errno));
|
||||
return(False);
|
||||
}
|
||||
|
||||
return(True);
|
||||
}
|
||||
|
||||
static BOOL fd_read_func(void * handle,
|
||||
uint8_t * buf,
|
||||
uint64_t wanted,
|
||||
uint64_t * actual)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
ret = read(IO_HANDLE_TO_FD(handle), buf, wanted);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "%s: %llu byte read failed: %s\n",
|
||||
PROGNAME, (unsigned long long)wanted,
|
||||
strerror(errno));
|
||||
return(False);
|
||||
}
|
||||
|
||||
*actual = (uint64_t)ret;
|
||||
return(True);
|
||||
}
|
||||
|
||||
static BOOL fd_write_func(void * handle,
|
||||
uint8_t * buf,
|
||||
uint64_t wanted,
|
||||
uint64_t * actual)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
ret = write(IO_HANDLE_TO_FD(handle), buf, wanted);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "%s: %llu byte write failed: %s\n",
|
||||
PROGNAME, (unsigned long long)wanted,
|
||||
strerror(errno));
|
||||
return(False);
|
||||
}
|
||||
|
||||
*actual = (uint64_t)ret;
|
||||
return(True);
|
||||
}
|
||||
|
||||
static struct dd_iohandle * open_fd_handle(const char * path,
|
||||
uint64_t io_size,
|
||||
int options)
|
||||
{
|
||||
struct fd_handle * fdh;
|
||||
int oflags = 0;
|
||||
|
||||
DEBUG(4, ("opening fd stream for %s\n", path));
|
||||
if ((fdh = talloc_zero(NULL, struct fd_handle)) == NULL) {
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
fdh->h.io_read = fd_read_func;
|
||||
fdh->h.io_write = fd_write_func;
|
||||
fdh->h.io_seek = fd_seek_func;
|
||||
|
||||
if (options & DD_DIRECT_IO) {
|
||||
#ifdef HAVE_OPEN_O_DIRECT
|
||||
oflags |= O_DIRECT;
|
||||
#else
|
||||
DEBUG(1, ("no support for direct IO on this platform\n"));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (options & DD_SYNC_IO)
|
||||
oflags |= O_SYNC;
|
||||
|
||||
oflags |= (options & DD_WRITE) ? (O_WRONLY | O_CREAT) : (O_RDONLY);
|
||||
|
||||
fdh->fd = open(path, oflags, 0644);
|
||||
if (fdh->fd < 0) {
|
||||
fprintf(stderr, "%s: %s: %s\n",
|
||||
PROGNAME, path, strerror(errno));
|
||||
talloc_free(fdh);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
if (options & DD_OPLOCK) {
|
||||
DEBUG(2, ("FIXME: take local oplock on %s\n", path));
|
||||
}
|
||||
|
||||
SMB_ASSERT((void *)fdh == (void *)&fdh->h);
|
||||
return(&fdh->h);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* CIFS client IO. */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
struct cifs_handle
|
||||
{
|
||||
struct dd_iohandle h;
|
||||
struct smbcli_state * cli;
|
||||
int fnum;
|
||||
uint64_t offset;
|
||||
};
|
||||
|
||||
#define IO_HANDLE_TO_SMB(h) ((struct cifs_handle *)(h))
|
||||
|
||||
BOOL smb_seek_func(void * handle, uint64_t offset)
|
||||
{
|
||||
IO_HANDLE_TO_SMB(handle)->offset = offset;
|
||||
return(True);
|
||||
}
|
||||
|
||||
BOOL smb_read_func(void * handle,
|
||||
uint8_t * buf,
|
||||
uint64_t wanted,
|
||||
uint64_t * actual)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
union smb_read r;
|
||||
struct cifs_handle * smbh;
|
||||
|
||||
ZERO_STRUCT(r);
|
||||
smbh = IO_HANDLE_TO_SMB(handle);
|
||||
|
||||
r.generic.level = RAW_READ_READX;
|
||||
r.readx.in.file.fnum = smbh->fnum;
|
||||
r.readx.in.offset = smbh->offset;
|
||||
r.readx.in.mincnt = wanted;
|
||||
r.readx.in.maxcnt = wanted;
|
||||
r.readx.out.data = buf;
|
||||
|
||||
/* FIXME: Should I really set readx.in.remaining? That just seems
|
||||
* redundant.
|
||||
*/
|
||||
ret = smb_raw_read(smbh->cli->tree, &r);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
fprintf(stderr, "%s: %llu byte read failed: %s\n",
|
||||
PROGNAME, (unsigned long long)wanted,
|
||||
nt_errstr(ret));
|
||||
return(False);
|
||||
}
|
||||
|
||||
/* Trap integer wrap. */
|
||||
SMB_ASSERT((smbh->offset + r.readx.out.nread) >= smbh->offset);
|
||||
|
||||
*actual = r.readx.out.nread;
|
||||
smbh->offset += r.readx.out.nread;
|
||||
return(True);
|
||||
}
|
||||
|
||||
BOOL smb_write_func(void * handle,
|
||||
uint8_t * buf,
|
||||
uint64_t wanted,
|
||||
uint64_t * actual)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
union smb_write w;
|
||||
struct cifs_handle * smbh;
|
||||
|
||||
ZERO_STRUCT(w);
|
||||
smbh = IO_HANDLE_TO_SMB(handle);
|
||||
|
||||
w.generic.level = RAW_WRITE_WRITEX;
|
||||
w.writex.in.file.fnum = smbh->fnum;
|
||||
w.writex.in.offset = smbh->offset;
|
||||
w.writex.in.count = wanted;
|
||||
w.writex.in.data = buf;
|
||||
|
||||
ret = smb_raw_write(smbh->cli->tree, &w);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
fprintf(stderr, "%s: %llu byte write failed: %s\n",
|
||||
PROGNAME, (unsigned long long)wanted,
|
||||
nt_errstr(ret));
|
||||
return(False);
|
||||
}
|
||||
|
||||
*actual = w.writex.out.nwritten;
|
||||
smbh->offset += w.writex.out.nwritten;
|
||||
return(True);
|
||||
}
|
||||
|
||||
static struct smbcli_state * init_smb_session(const char * host,
|
||||
const char * share)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
struct smbcli_state * cli = NULL;
|
||||
|
||||
/* When we support SMB URLs, we can get different user credentials for
|
||||
* each connection, but for now, we just use the same one for both.
|
||||
*/
|
||||
ret = smbcli_full_connection(NULL, &cli, host, share,
|
||||
NULL /* devtype */, cmdline_credentials, NULL /* events */);
|
||||
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
fprintf(stderr, "%s: connecting to //%s/%s: %s\n",
|
||||
PROGNAME, host, share, nt_errstr(ret));
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
return(cli);
|
||||
}
|
||||
|
||||
static int open_smb_file(struct smbcli_state * cli,
|
||||
const char * path,
|
||||
int options)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
union smb_open o;
|
||||
|
||||
ZERO_STRUCT(o);
|
||||
|
||||
o.ntcreatex.level = RAW_OPEN_NTCREATEX;
|
||||
o.ntcreatex.in.fname = path;
|
||||
|
||||
/* TODO: It's not clear whether to use these flags or to use the
|
||||
* similarly named NTCREATEX flags in the create_options field.
|
||||
*/
|
||||
if (options & DD_DIRECT_IO)
|
||||
o.ntcreatex.in.flags |= FILE_FLAG_NO_BUFFERING;
|
||||
|
||||
if (options & DD_SYNC_IO)
|
||||
o.ntcreatex.in.flags |= FILE_FLAG_WRITE_THROUGH;
|
||||
|
||||
o.ntcreatex.in.access_mask |=
|
||||
(options & DD_WRITE) ? SEC_FILE_WRITE_DATA
|
||||
: SEC_FILE_READ_DATA;
|
||||
|
||||
/* Try to create the file only if we will be writing to it. */
|
||||
o.ntcreatex.in.open_disposition =
|
||||
(options & DD_WRITE) ? NTCREATEX_DISP_OPEN_IF
|
||||
: NTCREATEX_DISP_OPEN;
|
||||
|
||||
o.ntcreatex.in.share_access =
|
||||
NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
|
||||
|
||||
if (options & DD_OPLOCK) {
|
||||
o.ntcreatex.in.flags |= NTCREATEX_FLAGS_REQUEST_OPLOCK;
|
||||
}
|
||||
|
||||
ret = smb_raw_open(cli->tree, NULL, &o);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
fprintf(stderr, "%s: opening %s: %s\n",
|
||||
PROGNAME, path, nt_errstr(ret));
|
||||
return(-1);
|
||||
}
|
||||
|
||||
return(o.ntcreatex.out.file.fnum);
|
||||
}
|
||||
|
||||
static struct dd_iohandle * open_cifs_handle(const char * host,
|
||||
const char * share,
|
||||
const char * path,
|
||||
uint64_t io_size,
|
||||
int options)
|
||||
{
|
||||
struct cifs_handle * smbh;
|
||||
|
||||
if (path == NULL || *path == '\0') {
|
||||
fprintf(stderr, "%s: missing path name within share //%s/%s\n",
|
||||
PROGNAME, host, share);
|
||||
}
|
||||
|
||||
DEBUG(4, ("opening SMB stream to //%s/%s for %s\n",
|
||||
host, share, path));
|
||||
|
||||
if ((smbh = talloc_zero(NULL, struct cifs_handle)) == NULL) {
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
smbh->h.io_read = smb_read_func;
|
||||
smbh->h.io_write = smb_write_func;
|
||||
smbh->h.io_seek = smb_seek_func;
|
||||
|
||||
if ((smbh->cli = init_smb_session(host, share)) == NULL) {
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
DEBUG(4, ("connected to //%s/%s with xmit size of %u bytes\n",
|
||||
host, share, smbh->cli->transport->negotiate.max_xmit));
|
||||
|
||||
smbh->fnum = open_smb_file(smbh->cli, path, options);
|
||||
return(&smbh->h);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Abstract IO interface. */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
struct dd_iohandle * dd_open_path(const char * path,
|
||||
uint64_t io_size,
|
||||
int options)
|
||||
{
|
||||
if (file_exist(path)) {
|
||||
return(open_fd_handle(path, io_size, options));
|
||||
} else {
|
||||
char * host;
|
||||
char * share;
|
||||
|
||||
if (smbcli_parse_unc(path, NULL, &host, &share)) {
|
||||
const char * remain;
|
||||
remain = strstr(path, share) + strlen(share);
|
||||
|
||||
/* Skip over leading directory separators. */
|
||||
while (*remain == '/' || *remain == '\\') { remain++; }
|
||||
|
||||
return(open_cifs_handle(host, share, remain,
|
||||
io_size, options));
|
||||
}
|
||||
|
||||
return(open_fd_handle(path, io_size, options));
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill the buffer till it has at least need_size bytes. Use read operations of
|
||||
* block_size bytes. Return the number of bytes read and fill buf_size with
|
||||
* the new buffer size.
|
||||
*
|
||||
* NOTE: The IO buffer is guaranteed to be big enough to fit
|
||||
* need_size + block_size bytes into it.
|
||||
*/
|
||||
BOOL dd_fill_block(struct dd_iohandle * h,
|
||||
uint8_t * buf,
|
||||
uint64_t * buf_size,
|
||||
uint64_t need_size,
|
||||
uint64_t block_size)
|
||||
{
|
||||
uint64_t read_size;
|
||||
|
||||
SMB_ASSERT(block_size > 0);
|
||||
SMB_ASSERT(need_size > 0);
|
||||
|
||||
while (*buf_size < need_size) {
|
||||
|
||||
if (!h->io_read(h, buf + (*buf_size), block_size, &read_size)) {
|
||||
return(False);
|
||||
}
|
||||
|
||||
if (read_size == 0) {
|
||||
h->io_flags |= DD_END_OF_FILE;
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUG(6, ("added %llu bytes to IO buffer (need %llu bytes)\n",
|
||||
(unsigned long long)read_size,
|
||||
(unsigned long long)need_size));
|
||||
|
||||
*buf_size += read_size;
|
||||
dd_stats.in.bytes += read_size;
|
||||
|
||||
if (read_size == block_size) {
|
||||
dd_stats.in.fblocks++;
|
||||
} else {
|
||||
DEBUG(3, ("partial read of %llu bytes (expected %llu)\n",
|
||||
(unsigned long long)read_size,
|
||||
(unsigned long long)block_size));
|
||||
dd_stats.in.pblocks++;
|
||||
}
|
||||
}
|
||||
|
||||
return(True);
|
||||
}
|
||||
|
||||
/* Flush a buffer that contains buf_size bytes. Use writes of block_size to do it,
|
||||
* and shift any remaining bytes back to the head of the buffer when there are
|
||||
* no more block_size sized IOs left.
|
||||
*/
|
||||
BOOL dd_flush_block(struct dd_iohandle * h,
|
||||
uint8_t * buf,
|
||||
uint64_t * buf_size,
|
||||
uint64_t block_size)
|
||||
{
|
||||
uint64_t write_size;
|
||||
uint64_t total_size = 0;
|
||||
|
||||
SMB_ASSERT(block_size > 0);
|
||||
|
||||
/* We have explicitly been asked to write a partial block. */
|
||||
if ((*buf_size) < block_size) {
|
||||
|
||||
if (!h->io_write(h, buf, *buf_size, &write_size)) {
|
||||
return(False);
|
||||
}
|
||||
|
||||
if (write_size == 0) {
|
||||
fprintf(stderr, "%s: unexpectedly wrote 0 bytes\n",
|
||||
PROGNAME);
|
||||
return(False);
|
||||
}
|
||||
|
||||
total_size += write_size;
|
||||
dd_stats.out.bytes += write_size;
|
||||
dd_stats.out.pblocks++;
|
||||
}
|
||||
|
||||
/* Write as many full blocks as there are in the buffer. */
|
||||
while (((*buf_size) - total_size) >= block_size) {
|
||||
|
||||
if (!h->io_write(h, buf + total_size, block_size, &write_size)) {
|
||||
return(False);
|
||||
}
|
||||
|
||||
if (write_size == 0) {
|
||||
fprintf(stderr, "%s: unexpectedly wrote 0 bytes\n",
|
||||
PROGNAME);
|
||||
return(False);
|
||||
}
|
||||
|
||||
if (write_size == block_size) {
|
||||
dd_stats.out.fblocks++;
|
||||
} else {
|
||||
dd_stats.out.pblocks++;
|
||||
}
|
||||
|
||||
total_size += write_size;
|
||||
dd_stats.out.bytes += write_size;
|
||||
|
||||
DEBUG(6, ("flushed %llu bytes from IO buffer of %llu bytes (%llu remain)\n",
|
||||
(unsigned long long)block_size,
|
||||
(unsigned long long)block_size,
|
||||
(unsigned long long)(block_size - total_size)));
|
||||
}
|
||||
|
||||
SMB_ASSERT(total_size > 0);
|
||||
|
||||
/* We have flushed as much of the IO buffer as we can while
|
||||
* still doing block_size'd operations. Shift any remaining data
|
||||
* to the front of the IO buffer.
|
||||
*/
|
||||
if ((*buf_size) > total_size) {
|
||||
uint64_t remain = (*buf_size) - total_size;
|
||||
|
||||
DEBUG(3, ("shifting %llu remainder bytes to IO buffer head\n",
|
||||
(unsigned long long)remain));
|
||||
|
||||
memmove(buf, buf + total_size, remain);
|
||||
(*buf_size) = remain;
|
||||
} else if ((*buf_size) == total_size) {
|
||||
(*buf_size) = 0;
|
||||
} else {
|
||||
/* Else buffer contains buf_size bytes that we will append
|
||||
* to next time round.
|
||||
*/
|
||||
DEBUG(3, ("%llu unflushed bytes left in IO buffer\n",
|
||||
(unsigned long long)(*buf_size)));
|
||||
}
|
||||
|
||||
return(True);
|
||||
}
|
||||
|
||||
/* vim: set sw=8 sts=8 ts=8 tw=79 : */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
||||
# client subsystem
|
||||
|
||||
#################################
|
||||
# Start BINARY smbclient
|
||||
[BINARY::smbclient]
|
||||
INSTALLDIR = BINDIR
|
||||
OBJ_FILES = \
|
||||
client.o
|
||||
PRIVATE_DEPENDENCIES = \
|
||||
LIBSAMBA-CONFIG \
|
||||
SMBREADLINE \
|
||||
LIBSAMBA-UTIL \
|
||||
LIBCLI_SMB \
|
||||
RPC_NDR_SRVSVC \
|
||||
LIBCLI_LSA \
|
||||
LIBPOPT \
|
||||
POPT_SAMBA \
|
||||
POPT_CREDENTIALS
|
||||
# End BINARY smbclient
|
||||
#################################
|
||||
|
||||
#################################
|
||||
# Start BINARY cifsdd
|
||||
[BINARY::cifsdd]
|
||||
INSTALLDIR = BINDIR
|
||||
OBJ_FILES = \
|
||||
cifsdd.o \
|
||||
cifsddio.o
|
||||
PRIVATE_DEPENDENCIES = \
|
||||
LIBSAMBA-CONFIG \
|
||||
LIBCLI_SMB \
|
||||
LIBPOPT \
|
||||
POPT_SAMBA \
|
||||
POPT_CREDENTIALS
|
||||
# End BINARY sdd
|
||||
#################################
|
||||
|
||||
Executable
+557
@@ -0,0 +1,557 @@
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <mntent.h>
|
||||
|
||||
#define MOUNT_CIFS_VERSION "1"
|
||||
|
||||
extern char *getusername(void);
|
||||
|
||||
char * thisprogram;
|
||||
int verboseflag = 0;
|
||||
static int got_password = 0;
|
||||
static int got_user = 0;
|
||||
static int got_domain = 0;
|
||||
static int got_ip = 0;
|
||||
static int got_unc = 0;
|
||||
static int got_uid = 0;
|
||||
static int got_gid = 0;
|
||||
static char * user_name = NULL;
|
||||
char * mountpassword = NULL;
|
||||
|
||||
|
||||
void mount_cifs_usage()
|
||||
{
|
||||
printf("\nUsage: %s remotetarget dir\n", thisprogram);
|
||||
printf("\nMount the remotetarget, specified as either a UNC name or ");
|
||||
printf(" CIFS URL, to the local directory, dir.\n");
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* caller frees username if necessary */
|
||||
char * getusername() {
|
||||
char *username = NULL;
|
||||
struct passwd *password = getpwuid(getuid());
|
||||
|
||||
if (password) {
|
||||
username = password->pw_name;
|
||||
}
|
||||
return username;
|
||||
}
|
||||
|
||||
char * parse_cifs_url(unc_name)
|
||||
{
|
||||
printf("\ncifs url %s\n",unc_name);
|
||||
}
|
||||
|
||||
int parse_options(char * options)
|
||||
{
|
||||
char * data;
|
||||
char * value = 0;
|
||||
|
||||
if (!options)
|
||||
return 1;
|
||||
|
||||
while ((data = strsep(&options, ",")) != NULL) {
|
||||
if (!*data)
|
||||
continue;
|
||||
if ((value = strchr(data, '=')) != NULL) {
|
||||
*value++ = '\0';
|
||||
}
|
||||
if (strncmp(data, "user", 4) == 0) {
|
||||
if (!value || !*value) {
|
||||
printf("invalid or missing username\n");
|
||||
return 1; /* needs_arg; */
|
||||
}
|
||||
if (strnlen(value, 260) < 260) {
|
||||
got_user=1;
|
||||
/* BB add check for format user%pass */
|
||||
/* if(strchr(username%passw) got_password = 1) */
|
||||
} else {
|
||||
printf("username too long\n");
|
||||
return 1;
|
||||
}
|
||||
} else if (strncmp(data, "pass", 4) == 0) {
|
||||
if (!value || !*value) {
|
||||
if(got_password) {
|
||||
printf("password specified twice, ignoring second\n");
|
||||
} else
|
||||
got_password = 1;
|
||||
} else if (strnlen(value, 17) < 17) {
|
||||
got_password = 1;
|
||||
} else {
|
||||
printf("password too long\n");
|
||||
return 1;
|
||||
}
|
||||
} else if (strncmp(data, "ip", 2) == 0) {
|
||||
if (!value || !*value) {
|
||||
printf("target ip address argument missing");
|
||||
} else if (strnlen(value, 35) < 35) {
|
||||
got_ip = 1;
|
||||
} else {
|
||||
printf("ip address too long\n");
|
||||
return 1;
|
||||
}
|
||||
} else if ((strncmp(data, "unc", 3) == 0)
|
||||
|| (strncmp(data, "target", 6) == 0)
|
||||
|| (strncmp(data, "path", 4) == 0)) {
|
||||
if (!value || !*value) {
|
||||
printf("invalid path to network resource\n");
|
||||
return 1; /* needs_arg; */
|
||||
} else if(strnlen(value,5) < 5) {
|
||||
printf("UNC name too short");
|
||||
}
|
||||
|
||||
if (strnlen(value, 300) < 300) {
|
||||
got_unc = 1;
|
||||
if (strncmp(value, "//", 2) == 0) {
|
||||
if(got_unc)
|
||||
printf("unc name specified twice, ignoring second\n");
|
||||
else
|
||||
got_unc = 1;
|
||||
} else if (strncmp(value, "\\\\", 2) != 0) {
|
||||
printf("UNC Path does not begin with // or \\\\ \n");
|
||||
return 1;
|
||||
} else {
|
||||
if(got_unc)
|
||||
printf("unc name specified twice, ignoring second\n");
|
||||
else
|
||||
got_unc = 1;
|
||||
}
|
||||
} else {
|
||||
printf("CIFS: UNC name too long\n");
|
||||
return 1;
|
||||
}
|
||||
} else if ((strncmp(data, "domain", 3) == 0)
|
||||
|| (strncmp(data, "workgroup", 5) == 0)) {
|
||||
if (!value || !*value) {
|
||||
printf("CIFS: invalid domain name\n");
|
||||
return 1; /* needs_arg; */
|
||||
}
|
||||
if (strnlen(value, 65) < 65) {
|
||||
got_domain = 1;
|
||||
} else {
|
||||
printf("domain name too long\n");
|
||||
return 1;
|
||||
}
|
||||
} else if (strncmp(data, "uid", 3) == 0) {
|
||||
if (value && *value) {
|
||||
got_uid = 1;
|
||||
}
|
||||
} else if (strncmp(data, "gid", 3) == 0) {
|
||||
if (value && *value) {
|
||||
got_gid = 1;
|
||||
}
|
||||
} /* else if (strnicmp(data, "file_mode", 4) == 0) {
|
||||
if (value && *value) {
|
||||
vol->file_mode =
|
||||
simple_strtoul(value, &value, 0);
|
||||
}
|
||||
} else if (strnicmp(data, "dir_mode", 3) == 0) {
|
||||
if (value && *value) {
|
||||
vol->dir_mode =
|
||||
simple_strtoul(value, &value, 0);
|
||||
}
|
||||
} else if (strnicmp(data, "port", 4) == 0) {
|
||||
if (value && *value) {
|
||||
vol->port =
|
||||
simple_strtoul(value, &value, 0);
|
||||
}
|
||||
} else if (strnicmp(data, "rsize", 5) == 0) {
|
||||
if (value && *value) {
|
||||
vol->rsize =
|
||||
simple_strtoul(value, &value, 0);
|
||||
}
|
||||
} else if (strnicmp(data, "wsize", 5) == 0) {
|
||||
if (value && *value) {
|
||||
vol->wsize =
|
||||
simple_strtoul(value, &value, 0);
|
||||
}
|
||||
} else if (strnicmp(data, "version", 3) == 0) {
|
||||
|
||||
} else if (strnicmp(data, "rw", 2) == 0) {
|
||||
|
||||
} else
|
||||
printf("CIFS: Unknown mount option %s\n",data); */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Note that caller frees the returned buffer if necessary */
|
||||
char * parse_server(char * unc_name)
|
||||
{
|
||||
int length = strnlen(unc_name,1024);
|
||||
char * share;
|
||||
char * ipaddress_string = NULL;
|
||||
struct hostent * host_entry;
|
||||
struct ipv4_addr server_ipaddr;
|
||||
int rc,j;
|
||||
char temp[64];
|
||||
|
||||
if(length > 1023) {
|
||||
printf("mount error: UNC name too long");
|
||||
return 0;
|
||||
}
|
||||
if (strncasecmp("cifs://",unc_name,7) == 0)
|
||||
return parse_cifs_url(unc_name+7);
|
||||
if (strncasecmp("smb://",unc_name,6) == 0) {
|
||||
return parse_cifs_url(unc_name+6);
|
||||
}
|
||||
|
||||
if(length < 3) {
|
||||
/* BB add code to find DFS root here */
|
||||
printf("\nMounting the DFS root for domain not implemented yet");
|
||||
return 0;
|
||||
} else {
|
||||
/* BB add support for \\\\ not just // */
|
||||
if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
|
||||
printf("mount error: improperly formatted UNC name.");
|
||||
printf(" %s does not begin with \\\\ or //\n",unc_name);
|
||||
return 0;
|
||||
} else {
|
||||
unc_name[0] = '\\';
|
||||
unc_name[1] = '\\';
|
||||
unc_name += 2;
|
||||
if ((share = strchr(unc_name, '/')) ||
|
||||
(share = strchr(unc_name,'\\'))) {
|
||||
*share = 0; /* temporarily terminate the string */
|
||||
share += 1;
|
||||
host_entry = gethostbyname(unc_name);
|
||||
*(share - 1) = '\\'; /* put the slash back */
|
||||
/* rc = getipnodebyname(unc_name, AF_INET, AT_ADDRCONFIG ,&rc);*/
|
||||
if(host_entry == NULL) {
|
||||
printf("mount error: could not find target server. TCP name %s not found ", unc_name);
|
||||
printf(" rc = %d\n",rc);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
/* BB should we pass an alternate version of the share name as Unicode */
|
||||
/* BB what about ipv6? BB */
|
||||
/* BB add retries with alternate servers in list */
|
||||
|
||||
memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
|
||||
|
||||
ipaddress_string = inet_ntoa(server_ipaddr);
|
||||
if(ipaddress_string == NULL) {
|
||||
printf("mount error: could not get valid ip address for target server\n");
|
||||
return 0;
|
||||
}
|
||||
return ipaddress_string;
|
||||
}
|
||||
} else {
|
||||
/* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
|
||||
printf("Mounting the DFS root for a particular server not implemented yet\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct option longopts[] = {
|
||||
{ "all", 0, 0, 'a' },
|
||||
{ "help", 0, 0, 'h' },
|
||||
{ "read-only", 0, 0, 'r' },
|
||||
{ "ro", 0, 0, 'r' },
|
||||
{ "verbose", 0, 0, 'v' },
|
||||
{ "version", 0, 0, 'V' },
|
||||
{ "read-write", 0, 0, 'w' },
|
||||
{ "rw", 0, 0, 'w' },
|
||||
{ "options", 1, 0, 'o' },
|
||||
{ "types", 1, 0, 't' },
|
||||
{ "replace", 0, 0, 129 },
|
||||
{ "after", 0, 0, 130 },
|
||||
{ "before", 0, 0, 131 },
|
||||
{ "over", 0, 0, 132 },
|
||||
{ "move", 0, 0, 133 },
|
||||
{ "rsize",1, 0, 136 },
|
||||
{ "wsize",1, 0, 137 },
|
||||
{ "uid", 1, 0, 138},
|
||||
{ "gid", 1, 0, 139},
|
||||
{ "uuid",1,0,'U' },
|
||||
{ "user",1,0,140},
|
||||
{ "username",1,0,140},
|
||||
{ "dom",1,0,141},
|
||||
{ "domain",1,0,141},
|
||||
{ "password",1,0,142},
|
||||
{ NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
int c;
|
||||
int flags = MS_MANDLOCK | MS_MGC_VAL;
|
||||
char * orgoptions = NULL;
|
||||
char * share_name = NULL;
|
||||
char * domain_name = NULL;
|
||||
char * ipaddr = NULL;
|
||||
char * uuid = NULL;
|
||||
char * mountpoint;
|
||||
char * options;
|
||||
int rc,i;
|
||||
int rsize = 0;
|
||||
int wsize = 0;
|
||||
int nomtab = 0;
|
||||
int uid = 0;
|
||||
int gid = 0;
|
||||
int optlen = 0;
|
||||
struct stat statbuf;
|
||||
struct utsname sysinfo;
|
||||
struct mntent mountent;
|
||||
FILE * pmntfile;
|
||||
|
||||
/* setlocale(LC_ALL, "");
|
||||
bindtextdomain(PACKAGE, LOCALEDIR);
|
||||
textdomain(PACKAGE); */
|
||||
|
||||
if(argc && argv) {
|
||||
thisprogram = argv[0];
|
||||
}
|
||||
if(thisprogram == NULL)
|
||||
thisprogram = "mount.cifs";
|
||||
|
||||
uname(&sysinfo);
|
||||
/* BB add workstation name and domain and pass down */
|
||||
/*#ifdef _GNU_SOURCE
|
||||
printf(" node: %s machine: %s\n", sysinfo.nodename,sysinfo.machine);
|
||||
#endif*/
|
||||
if(argc < 3)
|
||||
mount_cifs_usage();
|
||||
share_name = argv[1];
|
||||
mountpoint = argv[2];
|
||||
/* add sharename in opts string as unc= parm */
|
||||
|
||||
while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsU:vVwt:",
|
||||
longopts, NULL)) != -1) {
|
||||
switch (c) {
|
||||
/* case 'a':
|
||||
++mount_all;
|
||||
break;
|
||||
case 'f':
|
||||
++fake;
|
||||
break;
|
||||
case 'F':
|
||||
++optfork;
|
||||
break; */
|
||||
case 'h': /* help */
|
||||
mount_cifs_usage ();
|
||||
break;
|
||||
/* case 'i':
|
||||
external_allowed = 0;
|
||||
break;
|
||||
case 'l':
|
||||
list_with_volumelabel = 1;
|
||||
break;
|
||||
case 'L':
|
||||
volumelabel = optarg;
|
||||
break; */
|
||||
case 'n':
|
||||
++nomtab;
|
||||
break;
|
||||
case 'o':
|
||||
if (orgoptions) {
|
||||
orgoptions = strcat(orgoptions, ",");
|
||||
orgoptions = strcat(orgoptions,optarg);
|
||||
} else
|
||||
orgoptions = strdup(optarg);
|
||||
break;
|
||||
|
||||
/* case 'O':
|
||||
if (test_opts)
|
||||
test_opts = xstrconcat3(test_opts, ",", optarg);
|
||||
else
|
||||
test_opts = xstrdup(optarg);
|
||||
break;*/
|
||||
case 'r': /* mount readonly */
|
||||
flags |= MS_RDONLY;;
|
||||
break;
|
||||
case 'U':
|
||||
uuid = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
++verboseflag;
|
||||
break;
|
||||
/* case 'V':
|
||||
printf ("mount: %s\n", version);
|
||||
exit (0);*/
|
||||
case 'w':
|
||||
flags &= ~MS_RDONLY;;
|
||||
break;
|
||||
/* case 0:
|
||||
break;
|
||||
|
||||
case 128:
|
||||
mounttype = MS_BIND;
|
||||
break;
|
||||
case 129:
|
||||
mounttype = MS_REPLACE;
|
||||
break;
|
||||
case 130:
|
||||
mounttype = MS_AFTER;
|
||||
break;
|
||||
case 131:
|
||||
mounttype = MS_BEFORE;
|
||||
break;
|
||||
case 132:
|
||||
mounttype = MS_OVER;
|
||||
break;
|
||||
case 133:
|
||||
mounttype = MS_MOVE;
|
||||
break;
|
||||
case 135:
|
||||
mounttype = (MS_BIND | MS_REC);
|
||||
break; */
|
||||
case 136:
|
||||
rsize = atoi(optarg) ;
|
||||
break;
|
||||
case 137:
|
||||
wsize = atoi(optarg);
|
||||
break;
|
||||
case 138:
|
||||
uid = atoi(optarg);
|
||||
break;
|
||||
case 139:
|
||||
gid = atoi(optarg);
|
||||
break;
|
||||
case 140:
|
||||
got_user = 1;
|
||||
user_name = optarg;
|
||||
break;
|
||||
case 141:
|
||||
domain_name = optarg;
|
||||
break;
|
||||
case 142:
|
||||
got_password = 1;
|
||||
mountpassword = optarg;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
mount_cifs_usage ();
|
||||
}
|
||||
}
|
||||
|
||||
/* canonicalize the path in argv[1]? */
|
||||
|
||||
if(stat (mountpoint, &statbuf)) {
|
||||
printf("mount error: mount point %s does not exist\n",mountpoint);
|
||||
return -1;
|
||||
}
|
||||
if (S_ISDIR(statbuf.st_mode) == 0) {
|
||||
printf("mount error: mount point %s is not a directory\n",mountpoint);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(geteuid()) {
|
||||
printf("mount error: permission denied, not superuser and cifs.mount not installed SUID\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ipaddr = parse_server(share_name);
|
||||
/* if(share_name == NULL)
|
||||
return 1; */
|
||||
if (parse_options(strdup(orgoptions)))
|
||||
return 1;
|
||||
|
||||
if(got_user == 0)
|
||||
user_name = getusername();
|
||||
|
||||
/* check username for user%password format */
|
||||
|
||||
if(got_password == 0) {
|
||||
if (getenv("PASSWD")) {
|
||||
mountpassword = malloc(33);
|
||||
if(mountpassword) {
|
||||
strncpy(mountpassword,getenv("PASSWD"),32);
|
||||
got_password = 1;
|
||||
}
|
||||
/* } else if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
|
||||
get_password_file();
|
||||
got_password = 1;*/ /* BB add missing function */
|
||||
} else {
|
||||
mountpassword = getpass("Password: "); /* BB obsolete */
|
||||
got_password = 1;
|
||||
}
|
||||
}
|
||||
/* FIXME launch daemon (handles dfs name resolution and credential change)
|
||||
remember to clear parms and overwrite password field before launching */
|
||||
if(orgoptions) {
|
||||
optlen = strlen(orgoptions);
|
||||
} else
|
||||
optlen = 0;
|
||||
if(share_name)
|
||||
optlen += strlen(share_name) + 4;
|
||||
if(user_name)
|
||||
optlen += strlen(user_name) + 6;
|
||||
if(ipaddr)
|
||||
optlen += strlen(ipaddr) + 4;
|
||||
if(mountpassword)
|
||||
optlen += strlen(mountpassword) + 6;
|
||||
options = malloc(optlen + 10);
|
||||
|
||||
options[0] = 0;
|
||||
strncat(options,"unc=",4);
|
||||
strcat(options,share_name);
|
||||
if(ipaddr) {
|
||||
strncat(options,",ip=",4);
|
||||
strcat(options,ipaddr);
|
||||
}
|
||||
if(user_name) {
|
||||
strncat(options,",user=",6);
|
||||
strcat(options,user_name);
|
||||
}
|
||||
if(mountpassword) {
|
||||
strncat(options,",pass=",6);
|
||||
strcat(options,mountpassword);
|
||||
}
|
||||
strncat(options,",ver=",5);
|
||||
strcat(options,MOUNT_CIFS_VERSION);
|
||||
|
||||
if(orgoptions) {
|
||||
strcat(options,",");
|
||||
strcat(options,orgoptions);
|
||||
}
|
||||
/* printf("\noptions %s \n",options);*/
|
||||
if(mount(share_name, mountpoint, "cifs", flags, options)) {
|
||||
/* remember to kill daemon on error */
|
||||
switch (errno) {
|
||||
case 0:
|
||||
printf("mount failed but no error number set\n");
|
||||
return 0;
|
||||
case ENODEV:
|
||||
printf("mount error: cifs filesystem not supported by the system\n");
|
||||
break;
|
||||
default:
|
||||
printf("mount error %d = %s",errno,strerror(errno));
|
||||
}
|
||||
printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
|
||||
return -1;
|
||||
} else {
|
||||
pmntfile = setmntent(MOUNTED, "a+");
|
||||
if(pmntfile) {
|
||||
mountent.mnt_fsname = share_name;
|
||||
mountent.mnt_dir = mountpoint;
|
||||
mountent.mnt_type = "cifs";
|
||||
mountent.mnt_opts = "";
|
||||
mountent.mnt_freq = 0;
|
||||
mountent.mnt_passno = 0;
|
||||
rc = addmntent(pmntfile,&mountent);
|
||||
endmntent(pmntfile);
|
||||
} else {
|
||||
printf("could not update mount table\n");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* smbmnt.c
|
||||
*
|
||||
* Copyright (C) 1995-1998 by Paal-Kr. Engstad and Volker Lendecke
|
||||
* extensively modified by Tridge
|
||||
*
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#include <mntent.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <asm/posix_types.h>
|
||||
#include <linux/smb.h>
|
||||
#include <linux/smb_mount.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
#ifndef MS_MGC_VAL
|
||||
/* This may look strange but MS_MGC_VAL is what we are looking for and
|
||||
is what we need from <linux/fs.h> under libc systems and is
|
||||
provided in standard includes on glibc systems. So... We
|
||||
switch on what we need... */
|
||||
#include <linux/fs.h>
|
||||
#endif
|
||||
|
||||
static uid_t mount_uid;
|
||||
static gid_t mount_gid;
|
||||
static int mount_ro;
|
||||
static uint_t mount_fmask;
|
||||
static uint_t mount_dmask;
|
||||
static int user_mount;
|
||||
static char *options;
|
||||
|
||||
static void
|
||||
help(void)
|
||||
{
|
||||
printf("\n");
|
||||
printf("Usage: smbmnt mount-point [options]\n");
|
||||
printf("Version %s\n\n",VERSION);
|
||||
printf("-s share share name on server\n"
|
||||
"-r mount read-only\n"
|
||||
"-u uid mount as uid\n"
|
||||
"-g gid mount as gid\n"
|
||||
"-f mask permission mask for files\n"
|
||||
"-d mask permission mask for directories\n"
|
||||
"-o options name=value, list of options\n"
|
||||
"-h print this help text\n");
|
||||
}
|
||||
|
||||
static int
|
||||
parse_args(int argc, char *argv[], struct smb_mount_data *data, char **share)
|
||||
{
|
||||
int opt;
|
||||
|
||||
while ((opt = getopt (argc, argv, "s:u:g:rf:d:o:")) != EOF)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 's':
|
||||
*share = optarg;
|
||||
break;
|
||||
case 'u':
|
||||
if (!user_mount) {
|
||||
mount_uid = strtol(optarg, NULL, 0);
|
||||
}
|
||||
break;
|
||||
case 'g':
|
||||
if (!user_mount) {
|
||||
mount_gid = strtol(optarg, NULL, 0);
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
mount_ro = 1;
|
||||
break;
|
||||
case 'f':
|
||||
mount_fmask = strtol(optarg, NULL, 8);
|
||||
break;
|
||||
case 'd':
|
||||
mount_dmask = strtol(optarg, NULL, 8);
|
||||
break;
|
||||
case 'o':
|
||||
options = optarg;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static char *
|
||||
fullpath(const char *p)
|
||||
{
|
||||
char path[MAXPATHLEN];
|
||||
|
||||
if (strlen(p) > MAXPATHLEN-1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (realpath(p, path) == NULL) {
|
||||
fprintf(stderr,"Failed to find real path for mount point\n");
|
||||
exit(1);
|
||||
}
|
||||
return strdup(path);
|
||||
}
|
||||
|
||||
/* Check whether user is allowed to mount on the specified mount point. If it's
|
||||
OK then we change into that directory - this prevents race conditions */
|
||||
static int mount_ok(char *mount_point)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (chdir(mount_point) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (stat(".", &st) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
errno = ENOTDIR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((getuid() != 0) &&
|
||||
((getuid() != st.st_uid) ||
|
||||
((st.st_mode & S_IRWXU) != S_IRWXU))) {
|
||||
errno = EPERM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Tries to mount using the appropriate format. For 2.2 the struct,
|
||||
for 2.4 the ascii version. */
|
||||
static int
|
||||
do_mount(char *share_name, uint_t flags, struct smb_mount_data *data)
|
||||
{
|
||||
pstring opts;
|
||||
struct utsname uts;
|
||||
char *release, *major, *minor;
|
||||
char *data1, *data2;
|
||||
|
||||
uname(&uts);
|
||||
release = uts.release;
|
||||
major = strtok(release, ".");
|
||||
minor = strtok(NULL, ".");
|
||||
if (major && minor && atoi(major) == 2 && atoi(minor) < 4) {
|
||||
/* < 2.4, assume struct */
|
||||
data1 = (char *) data;
|
||||
data2 = opts;
|
||||
} else {
|
||||
/* >= 2.4, assume ascii but fall back on struct */
|
||||
data1 = opts;
|
||||
data2 = (char *) data;
|
||||
}
|
||||
|
||||
slprintf(opts, sizeof(opts)-1,
|
||||
"version=7,uid=%d,gid=%d,file_mode=0%o,dir_mode=0%o,%s",
|
||||
data->uid, data->gid, data->file_mode, data->dir_mode,options);
|
||||
if (mount(share_name, ".", "smbfs", flags, data1) == 0)
|
||||
return 0;
|
||||
return mount(share_name, ".", "smbfs", flags, data2);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *mount_point, *share_name = NULL;
|
||||
FILE *mtab;
|
||||
int fd;
|
||||
uint_t flags;
|
||||
struct smb_mount_data data;
|
||||
struct mntent ment;
|
||||
|
||||
memset(&data, 0, sizeof(struct smb_mount_data));
|
||||
|
||||
if (argc < 2) {
|
||||
help();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (argv[1][0] == '-') {
|
||||
help();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (getuid() != 0) {
|
||||
user_mount = 1;
|
||||
}
|
||||
|
||||
if (geteuid() != 0) {
|
||||
fprintf(stderr, "smbmnt must be installed suid root for direct user mounts (%d,%d)\n", getuid(), geteuid());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
mount_uid = getuid();
|
||||
mount_gid = getgid();
|
||||
mount_fmask = umask(0);
|
||||
umask(mount_fmask);
|
||||
mount_fmask = ~mount_fmask;
|
||||
|
||||
mount_point = fullpath(argv[1]);
|
||||
|
||||
argv += 1;
|
||||
argc -= 1;
|
||||
|
||||
if (mount_ok(mount_point) != 0) {
|
||||
fprintf(stderr, "cannot mount on %s: %s\n",
|
||||
mount_point, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
data.version = SMB_MOUNT_VERSION;
|
||||
|
||||
/* getuid() gives us the real uid, who may umount the fs */
|
||||
data.mounted_uid = getuid();
|
||||
|
||||
if (parse_args(argc, argv, &data, &share_name) != 0) {
|
||||
help();
|
||||
return -1;
|
||||
}
|
||||
|
||||
data.uid = mount_uid;
|
||||
data.gid = mount_gid;
|
||||
data.file_mode = (S_IRWXU|S_IRWXG|S_IRWXO) & mount_fmask;
|
||||
data.dir_mode = (S_IRWXU|S_IRWXG|S_IRWXO) & mount_dmask;
|
||||
|
||||
if (mount_dmask == 0) {
|
||||
data.dir_mode = data.file_mode;
|
||||
if ((data.dir_mode & S_IRUSR) != 0)
|
||||
data.dir_mode |= S_IXUSR;
|
||||
if ((data.dir_mode & S_IRGRP) != 0)
|
||||
data.dir_mode |= S_IXGRP;
|
||||
if ((data.dir_mode & S_IROTH) != 0)
|
||||
data.dir_mode |= S_IXOTH;
|
||||
}
|
||||
|
||||
flags = MS_MGC_VAL;
|
||||
|
||||
if (mount_ro) flags |= MS_RDONLY;
|
||||
|
||||
if (do_mount(share_name, flags, &data) < 0) {
|
||||
switch (errno) {
|
||||
case ENODEV:
|
||||
fprintf(stderr, "ERROR: smbfs filesystem not supported by the kernel\n");
|
||||
break;
|
||||
default:
|
||||
perror("mount error");
|
||||
}
|
||||
fprintf(stderr, "Please refer to the smbmnt(8) manual page\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ment.mnt_fsname = share_name ? share_name : "none";
|
||||
ment.mnt_dir = mount_point;
|
||||
ment.mnt_type = "smbfs";
|
||||
ment.mnt_opts = "";
|
||||
ment.mnt_freq = 0;
|
||||
ment.mnt_passno= 0;
|
||||
|
||||
mount_point = ment.mnt_dir;
|
||||
|
||||
if (mount_point == NULL)
|
||||
{
|
||||
fprintf(stderr, "Mount point too long\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1)
|
||||
{
|
||||
fprintf(stderr, "Can't get "MOUNTED"~ lock file");
|
||||
return 1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
if ((mtab = setmntent(MOUNTED, "a+")) == NULL)
|
||||
{
|
||||
fprintf(stderr, "Can't open " MOUNTED);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (addmntent(mtab, &ment) == 1)
|
||||
{
|
||||
fprintf(stderr, "Can't write mount entry");
|
||||
return 1;
|
||||
}
|
||||
if (fchmod(fileno(mtab), 0644) == -1)
|
||||
{
|
||||
fprintf(stderr, "Can't set perms on "MOUNTED);
|
||||
return 1;
|
||||
}
|
||||
endmntent(mtab);
|
||||
|
||||
if (unlink(MOUNTED"~") == -1)
|
||||
{
|
||||
fprintf(stderr, "Can't remove "MOUNTED"~");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,925 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
SMBFS mount program
|
||||
Copyright (C) Andrew Tridgell 1999
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/passwd.h"
|
||||
|
||||
#include <mntent.h>
|
||||
#include <asm/types.h>
|
||||
#include <linux/smb_fs.h>
|
||||
|
||||
static pstring credentials;
|
||||
static pstring my_netbios_name;
|
||||
static pstring password;
|
||||
static pstring username;
|
||||
static pstring workgroup;
|
||||
static pstring mpoint;
|
||||
static pstring service;
|
||||
static pstring options;
|
||||
|
||||
static struct ipv4_addr dest_ip;
|
||||
static BOOL have_ip;
|
||||
static int smb_port = 0;
|
||||
static BOOL got_user;
|
||||
static BOOL got_pass;
|
||||
static uid_t mount_uid;
|
||||
static gid_t mount_gid;
|
||||
static int mount_ro;
|
||||
static uint_t mount_fmask;
|
||||
static uint_t mount_dmask;
|
||||
static BOOL use_kerberos;
|
||||
/* TODO: Add code to detect smbfs version in kernel */
|
||||
static BOOL status32_smbfs = False;
|
||||
|
||||
static void usage(void);
|
||||
|
||||
static void exit_parent(int sig)
|
||||
{
|
||||
/* parent simply exits when child says go... */
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void daemonize(void)
|
||||
{
|
||||
int j, status;
|
||||
pid_t child_pid;
|
||||
|
||||
signal( SIGTERM, exit_parent );
|
||||
|
||||
if ((child_pid = sys_fork()) < 0) {
|
||||
DEBUG(0,("could not fork\n"));
|
||||
}
|
||||
|
||||
if (child_pid > 0) {
|
||||
while( 1 ) {
|
||||
j = waitpid( child_pid, &status, 0 );
|
||||
if( j < 0 ) {
|
||||
if( EINTR == errno ) {
|
||||
continue;
|
||||
}
|
||||
status = errno;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we get here - the child exited with some error status */
|
||||
if (WIFSIGNALED(status))
|
||||
exit(128 + WTERMSIG(status));
|
||||
else
|
||||
exit(WEXITSTATUS(status));
|
||||
}
|
||||
|
||||
signal( SIGTERM, SIG_DFL );
|
||||
chdir("/");
|
||||
}
|
||||
|
||||
static void close_our_files(int client_fd)
|
||||
{
|
||||
int i;
|
||||
struct rlimit limits;
|
||||
|
||||
getrlimit(RLIMIT_NOFILE,&limits);
|
||||
for (i = 0; i< limits.rlim_max; i++) {
|
||||
if (i == client_fd)
|
||||
continue;
|
||||
close(i);
|
||||
}
|
||||
}
|
||||
|
||||
static void usr1_handler(int x)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************
|
||||
return a connection to a server
|
||||
*******************************************************/
|
||||
static struct smbcli_state *do_connection(char *the_service)
|
||||
{
|
||||
struct smbcli_state *c;
|
||||
struct nmb_name called, calling;
|
||||
char *server_n;
|
||||
struct ipv4_addr ip;
|
||||
pstring server;
|
||||
char *share;
|
||||
|
||||
if (the_service[0] != '\\' || the_service[1] != '\\') {
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pstrcpy(server, the_service+2);
|
||||
share = strchr_m(server,'\\');
|
||||
if (!share) {
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
*share = 0;
|
||||
share++;
|
||||
|
||||
server_n = server;
|
||||
|
||||
make_nmb_name(&calling, my_netbios_name, 0x0);
|
||||
choose_called_name(&called, server, 0x20);
|
||||
|
||||
again:
|
||||
zero_ip(&ip);
|
||||
if (have_ip) ip = dest_ip;
|
||||
|
||||
/* have to open a new connection */
|
||||
if (!(c=smbcli_initialise(NULL)) || (smbcli_set_port(c, smb_port) != smb_port) ||
|
||||
!smbcli_connect(c, server_n, &ip)) {
|
||||
DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
|
||||
if (c) {
|
||||
talloc_free(c);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* SPNEGO doesn't work till we get NTSTATUS error support */
|
||||
/* But it is REQUIRED for kerberos authentication */
|
||||
if(!use_kerberos) c->use_spnego = False;
|
||||
|
||||
/* The kernel doesn't yet know how to sign it's packets */
|
||||
c->sign_info.allow_smb_signing = False;
|
||||
|
||||
/* Use kerberos authentication if specified */
|
||||
c->use_kerberos = use_kerberos;
|
||||
|
||||
if (!smbcli_session_request(c, &calling, &called)) {
|
||||
char *p;
|
||||
DEBUG(0,("%d: session request to %s failed (%s)\n",
|
||||
sys_getpid(), called.name, smbcli_errstr(c)));
|
||||
talloc_free(c);
|
||||
if ((p=strchr_m(called.name, '.'))) {
|
||||
*p = 0;
|
||||
goto again;
|
||||
}
|
||||
if (strcmp(called.name, "*SMBSERVER")) {
|
||||
make_nmb_name(&called , "*SMBSERVER", 0x20);
|
||||
goto again;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEBUG(4,("%d: session request ok\n", sys_getpid()));
|
||||
|
||||
if (!smbcli_negprot(c)) {
|
||||
DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
|
||||
talloc_free(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!got_pass) {
|
||||
char *pass = getpass("Password: ");
|
||||
if (pass) {
|
||||
pstrcpy(password, pass);
|
||||
}
|
||||
}
|
||||
|
||||
/* This should be right for current smbfs. Future versions will support
|
||||
large files as well as unicode and oplocks. */
|
||||
if (status32_smbfs) {
|
||||
c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
|
||||
CAP_NT_FIND | CAP_LEVEL_II_OPLOCKS);
|
||||
}
|
||||
else {
|
||||
c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
|
||||
CAP_NT_FIND | CAP_STATUS32 |
|
||||
CAP_LEVEL_II_OPLOCKS);
|
||||
c->force_dos_errors = True;
|
||||
}
|
||||
|
||||
if (!smbcli_session_setup(c, username,
|
||||
password, strlen(password),
|
||||
password, strlen(password),
|
||||
workgroup)) {
|
||||
/* if a password was not supplied then try again with a
|
||||
null username */
|
||||
if (password[0] || !username[0] ||
|
||||
!smbcli_session_setup(c, "", "", 0, "", 0, workgroup)) {
|
||||
DEBUG(0,("%d: session setup failed: %s\n",
|
||||
sys_getpid(), smbcli_errstr(c)));
|
||||
talloc_free(c);
|
||||
return NULL;
|
||||
}
|
||||
DEBUG(0,("Anonymous login successful\n"));
|
||||
}
|
||||
|
||||
DEBUG(4,("%d: session setup ok\n", sys_getpid()));
|
||||
|
||||
if (!smbcli_tconX(c, share, "?????", password, strlen(password)+1)) {
|
||||
DEBUG(0,("%d: tree connect failed: %s\n",
|
||||
sys_getpid(), smbcli_errstr(c)));
|
||||
talloc_free(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEBUG(4,("%d: tconx ok\n", sys_getpid()));
|
||||
|
||||
got_pass = True;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
|
||||
Code blatently stolen from smbumount.c
|
||||
-mhw-
|
||||
****************************************************************************/
|
||||
static void smb_umount(char *mount_point)
|
||||
{
|
||||
int fd;
|
||||
struct mntent *mnt;
|
||||
FILE* mtab;
|
||||
FILE* new_mtab;
|
||||
|
||||
/* Programmers Note:
|
||||
This routine only gets called to the scene of a disaster
|
||||
to shoot the survivors... A connection that was working
|
||||
has now apparently failed. We have an active mount point
|
||||
(presumably) that we need to dump. If we get errors along
|
||||
the way - make some noise, but we are already turning out
|
||||
the lights to exit anyways...
|
||||
*/
|
||||
if (umount(mount_point) != 0) {
|
||||
DEBUG(0,("%d: Could not umount %s: %s\n",
|
||||
sys_getpid(), mount_point, strerror(errno)));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
|
||||
DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", sys_getpid()));
|
||||
return;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
|
||||
DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
|
||||
sys_getpid(), strerror(errno)));
|
||||
return;
|
||||
}
|
||||
|
||||
#define MOUNTED_TMP MOUNTED".tmp"
|
||||
|
||||
if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
|
||||
DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
|
||||
sys_getpid(), strerror(errno)));
|
||||
endmntent(mtab);
|
||||
return;
|
||||
}
|
||||
|
||||
while ((mnt = getmntent(mtab)) != NULL) {
|
||||
if (strcmp(mnt->mnt_dir, mount_point) != 0) {
|
||||
addmntent(new_mtab, mnt);
|
||||
}
|
||||
}
|
||||
|
||||
endmntent(mtab);
|
||||
|
||||
if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
|
||||
DEBUG(0,("%d: Error changing mode of %s: %s\n",
|
||||
sys_getpid(), MOUNTED_TMP, strerror(errno)));
|
||||
return;
|
||||
}
|
||||
|
||||
endmntent(new_mtab);
|
||||
|
||||
if (rename(MOUNTED_TMP, MOUNTED) < 0) {
|
||||
DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
|
||||
sys_getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (unlink(MOUNTED"~") == -1) {
|
||||
DEBUG(0,("%d: Can't remove "MOUNTED"~", sys_getpid()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Call the smbfs ioctl to install a connection socket,
|
||||
* then wait for a signal to reconnect. Note that we do
|
||||
* not exit after open_sockets() or send_login() errors,
|
||||
* as the smbfs mount would then have no way to recover.
|
||||
*/
|
||||
static void send_fs_socket(char *the_service, char *mount_point, struct smbcli_state *c)
|
||||
{
|
||||
int fd, closed = 0, res = 1;
|
||||
pid_t parentpid = getppid();
|
||||
struct smb_conn_opt conn_options;
|
||||
|
||||
memset(&conn_options, 0, sizeof(conn_options));
|
||||
|
||||
while (1) {
|
||||
if ((fd = open(mount_point, O_RDONLY)) < 0) {
|
||||
DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
|
||||
sys_getpid(), mount_point));
|
||||
break;
|
||||
}
|
||||
|
||||
conn_options.fd = c->fd;
|
||||
conn_options.protocol = c->protocol;
|
||||
conn_options.case_handling = SMB_CASE_DEFAULT;
|
||||
conn_options.max_xmit = c->max_xmit;
|
||||
conn_options.server_uid = c->vuid;
|
||||
conn_options.tid = c->cnum;
|
||||
conn_options.secmode = c->sec_mode;
|
||||
conn_options.rawmode = 0;
|
||||
conn_options.sesskey = c->sesskey;
|
||||
conn_options.maxraw = 0;
|
||||
conn_options.capabilities = c->capabilities;
|
||||
conn_options.serverzone = c->serverzone/60;
|
||||
|
||||
res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
|
||||
if (res != 0) {
|
||||
DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
|
||||
sys_getpid(), res));
|
||||
close(fd);
|
||||
break;
|
||||
}
|
||||
|
||||
if (parentpid) {
|
||||
/* Ok... We are going to kill the parent. Now
|
||||
is the time to break the process group... */
|
||||
setsid();
|
||||
/* Send a signal to the parent to terminate */
|
||||
kill(parentpid, SIGTERM);
|
||||
parentpid = 0;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
/* This looks wierd but we are only closing the userspace
|
||||
side, the connection has already been passed to smbfs and
|
||||
it has increased the usage count on the socket.
|
||||
|
||||
If we don't do this we will "leak" sockets and memory on
|
||||
each reconnection we have to make. */
|
||||
talloc_free(c);
|
||||
c = NULL;
|
||||
|
||||
if (!closed) {
|
||||
/* redirect stdout & stderr since we can't know that
|
||||
the library functions we use are using DEBUG. */
|
||||
if ( (fd = open("/dev/null", O_WRONLY)) < 0)
|
||||
DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
|
||||
close_our_files(fd);
|
||||
if (fd >= 0) {
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/* here we are no longer interactive */
|
||||
set_remote_machine_name("smbmount"); /* sneaky ... */
|
||||
setup_logging("mount.smbfs", DEBUG_STDERR);
|
||||
reopen_logs();
|
||||
DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
|
||||
|
||||
closed = 1;
|
||||
}
|
||||
|
||||
/* Wait for a signal from smbfs ... but don't continue
|
||||
until we actually get a new connection. */
|
||||
while (!c) {
|
||||
CatchSignal(SIGUSR1, &usr1_handler);
|
||||
pause();
|
||||
DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
|
||||
c = do_connection(the_service);
|
||||
}
|
||||
}
|
||||
|
||||
smb_umount(mount_point);
|
||||
DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mount a smbfs
|
||||
**/
|
||||
static void init_mount(void)
|
||||
{
|
||||
char mount_point[MAXPATHLEN+1];
|
||||
pstring tmp;
|
||||
pstring svc2;
|
||||
struct smbcli_state *c;
|
||||
char *args[20];
|
||||
int i, status;
|
||||
|
||||
if (realpath(mpoint, mount_point) == NULL) {
|
||||
fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
c = do_connection(service);
|
||||
if (!c) {
|
||||
fprintf(stderr,"SMB connection failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
Set up to return as a daemon child and wait in the parent
|
||||
until the child say it's ready...
|
||||
*/
|
||||
daemonize();
|
||||
|
||||
pstrcpy(svc2, service);
|
||||
string_replace(svc2, '\\','/');
|
||||
string_replace(svc2, ' ','_');
|
||||
|
||||
memset(args, 0, sizeof(args[0])*20);
|
||||
|
||||
i=0;
|
||||
args[i++] = "smbmnt";
|
||||
|
||||
args[i++] = mount_point;
|
||||
args[i++] = "-s";
|
||||
args[i++] = svc2;
|
||||
|
||||
if (mount_ro) {
|
||||
args[i++] = "-r";
|
||||
}
|
||||
if (mount_uid) {
|
||||
slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
|
||||
args[i++] = "-u";
|
||||
args[i++] = smb_xstrdup(tmp);
|
||||
}
|
||||
if (mount_gid) {
|
||||
slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
|
||||
args[i++] = "-g";
|
||||
args[i++] = smb_xstrdup(tmp);
|
||||
}
|
||||
if (mount_fmask) {
|
||||
slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
|
||||
args[i++] = "-f";
|
||||
args[i++] = smb_xstrdup(tmp);
|
||||
}
|
||||
if (mount_dmask) {
|
||||
slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
|
||||
args[i++] = "-d";
|
||||
args[i++] = smb_xstrdup(tmp);
|
||||
}
|
||||
if (options) {
|
||||
args[i++] = "-o";
|
||||
args[i++] = options;
|
||||
}
|
||||
|
||||
if (sys_fork() == 0) {
|
||||
char *smbmnt_path;
|
||||
|
||||
asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
|
||||
|
||||
if (file_exist(smbmnt_path)) {
|
||||
execv(smbmnt_path, args);
|
||||
fprintf(stderr,
|
||||
"smbfs/init_mount: execv of %s failed. Error was %s.",
|
||||
smbmnt_path, strerror(errno));
|
||||
} else {
|
||||
execvp("smbmnt", args);
|
||||
fprintf(stderr,
|
||||
"smbfs/init_mount: execv of %s failed. Error was %s.",
|
||||
"smbmnt", strerror(errno));
|
||||
}
|
||||
free(smbmnt_path);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (waitpid(-1, &status, 0) == -1) {
|
||||
fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
|
||||
/* FIXME: do some proper error handling */
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
|
||||
fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
|
||||
/* FIXME: do some proper error handling */
|
||||
exit(1);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
fprintf(stderr, "smbmnt killed by signal %d\n", WTERMSIG(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Ok... This is the rubicon for that mount point... At any point
|
||||
after this, if the connections fail and can not be reconstructed
|
||||
for any reason, we will have to unmount the mount point. There
|
||||
is no exit from the next call...
|
||||
*/
|
||||
send_fs_socket(service, mount_point, c);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
get a password from a a file or file descriptor
|
||||
exit on failure (from smbclient, move to libsmb or shared .c file?)
|
||||
****************************************************************************/
|
||||
static void get_password_file(void)
|
||||
{
|
||||
int fd = -1;
|
||||
char *p;
|
||||
BOOL close_it = False;
|
||||
pstring spec;
|
||||
char pass[128];
|
||||
|
||||
if ((p = getenv("PASSWD_FD")) != NULL) {
|
||||
pstrcpy(spec, "descriptor ");
|
||||
pstrcat(spec, p);
|
||||
sscanf(p, "%d", &fd);
|
||||
close_it = False;
|
||||
} else if ((p = getenv("PASSWD_FILE")) != NULL) {
|
||||
fd = open(p, O_RDONLY, 0);
|
||||
pstrcpy(spec, p);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
|
||||
spec, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
close_it = True;
|
||||
}
|
||||
|
||||
for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
|
||||
p && p - pass < sizeof(pass);) {
|
||||
switch (read(fd, p, 1)) {
|
||||
case 1:
|
||||
if (*p != '\n' && *p != '\0') {
|
||||
*++p = '\0'; /* advance p, and null-terminate pass */
|
||||
break;
|
||||
}
|
||||
case 0:
|
||||
if (p - pass) {
|
||||
*p = '\0'; /* null-terminate it, just in case... */
|
||||
p = NULL; /* then force the loop condition to become false */
|
||||
break;
|
||||
} else {
|
||||
fprintf(stderr, "Error reading password from file %s: %s\n",
|
||||
spec, "empty password\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Error reading password from file %s: %s\n",
|
||||
spec, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
pstrcpy(password, pass);
|
||||
if (close_it)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
get username and password from a credentials file
|
||||
exit on failure (from smbclient, move to libsmb or shared .c file?)
|
||||
****************************************************************************/
|
||||
static void read_credentials_file(char *filename)
|
||||
{
|
||||
FILE *auth;
|
||||
fstring buf;
|
||||
uint16_t len = 0;
|
||||
char *ptr, *val, *param;
|
||||
|
||||
if ((auth=sys_fopen(filename, "r")) == NULL)
|
||||
{
|
||||
/* fail if we can't open the credentials file */
|
||||
DEBUG(0,("ERROR: Unable to open credentials file!\n"));
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
while (!feof(auth))
|
||||
{
|
||||
/* get a line from the file */
|
||||
if (!fgets (buf, sizeof(buf), auth))
|
||||
continue;
|
||||
len = strlen(buf);
|
||||
|
||||
if ((len) && (buf[len-1]=='\n'))
|
||||
{
|
||||
buf[len-1] = '\0';
|
||||
len--;
|
||||
}
|
||||
if (len == 0)
|
||||
continue;
|
||||
|
||||
/* break up the line into parameter & value.
|
||||
will need to eat a little whitespace possibly */
|
||||
param = buf;
|
||||
if (!(ptr = strchr (buf, '=')))
|
||||
continue;
|
||||
val = ptr+1;
|
||||
*ptr = '\0';
|
||||
|
||||
/* eat leading white space */
|
||||
while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
|
||||
val++;
|
||||
|
||||
if (strwicmp("password", param) == 0)
|
||||
{
|
||||
pstrcpy(password, val);
|
||||
got_pass = True;
|
||||
}
|
||||
else if (strwicmp("username", param) == 0) {
|
||||
pstrcpy(username, val);
|
||||
}
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
}
|
||||
fclose(auth);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
usage on the program
|
||||
****************************************************************************/
|
||||
static void usage(void)
|
||||
{
|
||||
printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
|
||||
|
||||
printf("Version %s\n\n",VERSION);
|
||||
|
||||
printf(
|
||||
"Options:\n\
|
||||
username=<arg> SMB username\n\
|
||||
password=<arg> SMB password\n\
|
||||
credentials=<filename> file with username/password\n\
|
||||
krb use kerberos (active directory)\n\
|
||||
netbiosname=<arg> source NetBIOS name\n\
|
||||
uid=<arg> mount uid or username\n\
|
||||
gid=<arg> mount gid or groupname\n\
|
||||
port=<arg> remote SMB port number\n\
|
||||
fmask=<arg> file umask\n\
|
||||
dmask=<arg> directory umask\n\
|
||||
debug=<arg> debug level\n\
|
||||
ip=<arg> destination host or IP address\n\
|
||||
workgroup=<arg> workgroup on destination\n\
|
||||
sockopt=<arg> TCP socket options\n\
|
||||
scope=<arg> NetBIOS scope\n\
|
||||
iocharset=<arg> Linux charset (iso8859-1, utf8)\n\
|
||||
codepage=<arg> server codepage (cp850)\n\
|
||||
ttl=<arg> dircache time to live\n\
|
||||
guest don't prompt for a password\n\
|
||||
ro mount read-only\n\
|
||||
rw mount read-write\n\
|
||||
\n\
|
||||
This command is designed to be run from within /bin/mount by giving\n\
|
||||
the option '-t smbfs'. For example:\n\
|
||||
mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
|
||||
");
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Argument parsing for mount.smbfs interface
|
||||
mount will call us like this:
|
||||
mount.smbfs device mountpoint -o <options>
|
||||
|
||||
<options> is never empty, containing at least rw or ro
|
||||
****************************************************************************/
|
||||
static void parse_mount_smb(int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
char *opts;
|
||||
char *opteq;
|
||||
extern char *optarg;
|
||||
int val;
|
||||
char *p;
|
||||
|
||||
/* FIXME: This function can silently fail if the arguments are
|
||||
* not in the expected order.
|
||||
|
||||
> The arguments syntax of smbmount 2.2.3a (smbfs of Debian stable)
|
||||
> requires that one gives "-o" before further options like username=...
|
||||
> . Without -o, the username=.. setting is *silently* ignored. I've
|
||||
> spent about an hour trying to find out why I couldn't log in now..
|
||||
|
||||
*/
|
||||
|
||||
|
||||
if (argc < 2 || argv[1][0] == '-') {
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pstrcpy(service, argv[1]);
|
||||
pstrcpy(mpoint, argv[2]);
|
||||
|
||||
/* Convert any '/' characters in the service name to
|
||||
'\' characters */
|
||||
string_replace(service, '/','\\');
|
||||
argc -= 2;
|
||||
argv += 2;
|
||||
|
||||
opt = getopt(argc, argv, "o:");
|
||||
if(opt != 'o') {
|
||||
return;
|
||||
}
|
||||
|
||||
options[0] = 0;
|
||||
p = options;
|
||||
|
||||
/*
|
||||
* option parsing from nfsmount.c (util-linux-2.9u)
|
||||
*/
|
||||
for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
|
||||
DEBUG(3, ("opts: %s\n", opts));
|
||||
if ((opteq = strchr_m(opts, '='))) {
|
||||
val = atoi(opteq + 1);
|
||||
*opteq = '\0';
|
||||
|
||||
if (!strcmp(opts, "username") ||
|
||||
!strcmp(opts, "logon")) {
|
||||
char *lp;
|
||||
got_user = True;
|
||||
pstrcpy(username,opteq+1);
|
||||
if ((lp=strchr_m(username,'%'))) {
|
||||
*lp = 0;
|
||||
pstrcpy(password,lp+1);
|
||||
got_pass = True;
|
||||
memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
|
||||
}
|
||||
if ((lp=strchr_m(username,'/'))) {
|
||||
*lp = 0;
|
||||
pstrcpy(workgroup,lp+1);
|
||||
}
|
||||
} else if(!strcmp(opts, "passwd") ||
|
||||
!strcmp(opts, "password")) {
|
||||
pstrcpy(password,opteq+1);
|
||||
got_pass = True;
|
||||
memset(opteq+1,'X',strlen(password));
|
||||
} else if(!strcmp(opts, "credentials")) {
|
||||
pstrcpy(credentials,opteq+1);
|
||||
} else if(!strcmp(opts, "netbiosname")) {
|
||||
pstrcpy(my_netbios_name,opteq+1);
|
||||
} else if(!strcmp(opts, "uid")) {
|
||||
mount_uid = nametouid(opteq+1);
|
||||
} else if(!strcmp(opts, "gid")) {
|
||||
mount_gid = nametogid(opteq+1);
|
||||
} else if(!strcmp(opts, "port")) {
|
||||
smb_port = val;
|
||||
} else if(!strcmp(opts, "fmask")) {
|
||||
mount_fmask = strtol(opteq+1, NULL, 8);
|
||||
} else if(!strcmp(opts, "dmask")) {
|
||||
mount_dmask = strtol(opteq+1, NULL, 8);
|
||||
} else if(!strcmp(opts, "debug")) {
|
||||
DEBUGLEVEL = val;
|
||||
} else if(!strcmp(opts, "ip")) {
|
||||
dest_ip = interpret_addr2(opteq+1);
|
||||
if (is_zero_ip(dest_ip)) {
|
||||
fprintf(stderr,"Can't resolve address %s\n", opteq+1);
|
||||
exit(1);
|
||||
}
|
||||
have_ip = True;
|
||||
} else if(!strcmp(opts, "workgroup")) {
|
||||
pstrcpy(workgroup,opteq+1);
|
||||
} else if(!strcmp(opts, "sockopt")) {
|
||||
lp_set_cmdline("socket options", opteq+1);
|
||||
} else if(!strcmp(opts, "scope")) {
|
||||
lp_set_cmdline("netbios scope", opteq+1);
|
||||
} else {
|
||||
slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
|
||||
p += strlen(p);
|
||||
}
|
||||
} else {
|
||||
val = 1;
|
||||
if(!strcmp(opts, "nocaps")) {
|
||||
fprintf(stderr, "Unhandled option: %s\n", opteq+1);
|
||||
exit(1);
|
||||
} else if(!strcmp(opts, "guest")) {
|
||||
*password = '\0';
|
||||
got_pass = True;
|
||||
} else if(!strcmp(opts, "krb")) {
|
||||
#ifdef HAVE_KRB5
|
||||
|
||||
use_kerberos = True;
|
||||
if(!status32_smbfs)
|
||||
fprintf(stderr, "Warning: kerberos support will only work for samba servers\n");
|
||||
#else
|
||||
fprintf(stderr,"No kerberos support compiled in\n");
|
||||
exit(1);
|
||||
#endif
|
||||
} else if(!strcmp(opts, "rw")) {
|
||||
mount_ro = 0;
|
||||
} else if(!strcmp(opts, "ro")) {
|
||||
mount_ro = 1;
|
||||
} else {
|
||||
strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
|
||||
p += strlen(opts);
|
||||
*p++ = ',';
|
||||
*p = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!*service) {
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (p != options) {
|
||||
*(p-1) = 0; /* remove trailing , */
|
||||
DEBUG(3,("passthrough options '%s'\n", options));
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
main program
|
||||
****************************************************************************/
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
char *p;
|
||||
|
||||
DEBUGLEVEL = 1;
|
||||
|
||||
/* here we are interactive, even if run from autofs */
|
||||
setup_logging("mount.smbfs",DEBUG_STDERR);
|
||||
|
||||
#if 0 /* JRA - Urban says not needed ? */
|
||||
/* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
|
||||
is to not announce any unicode capabilities as current smbfs does
|
||||
not support it. */
|
||||
p = getenv("CLI_FORCE_ASCII");
|
||||
if (p && !strcmp(p, "false"))
|
||||
unsetenv("CLI_FORCE_ASCII");
|
||||
else
|
||||
setenv("CLI_FORCE_ASCII", "true", 1);
|
||||
#endif
|
||||
|
||||
if (getenv("USER")) {
|
||||
pstrcpy(username,getenv("USER"));
|
||||
|
||||
if ((p=strchr_m(username,'%'))) {
|
||||
*p = 0;
|
||||
pstrcpy(password,p+1);
|
||||
got_pass = True;
|
||||
memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
|
||||
}
|
||||
strupper(username);
|
||||
}
|
||||
|
||||
if (getenv("PASSWD")) {
|
||||
pstrcpy(password,getenv("PASSWD"));
|
||||
got_pass = True;
|
||||
}
|
||||
|
||||
if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
|
||||
get_password_file();
|
||||
got_pass = True;
|
||||
}
|
||||
|
||||
if (*username == 0 && getenv("LOGNAME")) {
|
||||
pstrcpy(username,getenv("LOGNAME"));
|
||||
}
|
||||
|
||||
if (!lp_load()) {
|
||||
fprintf(stderr, "Can't load %s - run testparm to debug it\n",
|
||||
lp_config_file());
|
||||
}
|
||||
|
||||
parse_mount_smb(argc, argv);
|
||||
|
||||
if (use_kerberos && !got_user) {
|
||||
got_pass = True;
|
||||
}
|
||||
|
||||
if (*credentials != 0) {
|
||||
read_credentials_file(credentials);
|
||||
}
|
||||
|
||||
DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
|
||||
|
||||
if (*workgroup == 0) {
|
||||
pstrcpy(workgroup,lp_workgroup());
|
||||
}
|
||||
|
||||
if (!*my_netbios_name) {
|
||||
pstrcpy(my_netbios_name, myhostname());
|
||||
}
|
||||
strupper(my_netbios_name);
|
||||
|
||||
init_mount();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
SMB backend for the Common UNIX Printing System ("CUPS")
|
||||
Copyright 1999 by Easy Software Products
|
||||
Copyright Andrew Tridgell 1994-1998
|
||||
Copyright Andrew Bartlett 2002
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
/*
|
||||
* Local functions...
|
||||
*/
|
||||
|
||||
static void list_devices(void);
|
||||
static struct smbcli_state *smb_connect(const char *, const char *, const char *, const char *, const char *);
|
||||
static int smb_print(struct smbcli_state *, char *, FILE *);
|
||||
|
||||
|
||||
/*
|
||||
* 'main()' - Main entry for SMB backend.
|
||||
*/
|
||||
|
||||
int /* O - Exit status */
|
||||
main(int argc, /* I - Number of command-line arguments */
|
||||
char *argv[]) /* I - Command-line arguments */
|
||||
{
|
||||
int i; /* Looping var */
|
||||
int copies; /* Number of copies */
|
||||
char uri[1024], /* URI */
|
||||
*sep, /* Pointer to separator */
|
||||
*password; /* Password */
|
||||
const char *username, /* Username */
|
||||
*server, /* Server name */
|
||||
*printer; /* Printer name */
|
||||
const char *workgroup; /* Workgroup */
|
||||
FILE *fp; /* File to print */
|
||||
int status=0; /* Status of LPD job */
|
||||
struct smbcli_state *cli; /* SMB interface */
|
||||
|
||||
/* we expect the URI in argv[0]. Detect the case where it is in argv[1] and cope */
|
||||
if (argc > 2 && strncmp(argv[0],"smb://", 6) && !strncmp(argv[1],"smb://", 6)) {
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
|
||||
if (argc == 1)
|
||||
{
|
||||
/*
|
||||
* NEW! In CUPS 1.1 the backends are run with no arguments to list the
|
||||
* available devices. These can be devices served by this backend
|
||||
* or any other backends (i.e. you can have an SNMP backend that
|
||||
* is only used to enumerate the available network printers... :)
|
||||
*/
|
||||
|
||||
list_devices();
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (argc < 6 || argc > 7)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [DEVICE_URI] job-id user title copies options [file]\n",
|
||||
argv[0]);
|
||||
fputs(" The DEVICE_URI environment variable can also contain the\n", stderr);
|
||||
fputs(" destination printer:\n", stderr);
|
||||
fputs("\n", stderr);
|
||||
fputs(" smb://[username:password@][workgroup/]server/printer\n", stderr);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have 7 arguments, print the file named on the command-line.
|
||||
* Otherwise, print data from stdin...
|
||||
*/
|
||||
|
||||
if (argc == 6)
|
||||
{
|
||||
/*
|
||||
* Print from Copy stdin to a temporary file...
|
||||
*/
|
||||
|
||||
fp = stdin;
|
||||
copies = 1;
|
||||
}
|
||||
else if ((fp = fopen(argv[6], "rb")) == NULL)
|
||||
{
|
||||
perror("ERROR: Unable to open print file");
|
||||
return (1);
|
||||
}
|
||||
else
|
||||
copies = atoi(argv[4]);
|
||||
|
||||
/*
|
||||
* Find the URI...
|
||||
*/
|
||||
|
||||
if (strncmp(argv[0], "smb://", 6) == 0)
|
||||
strncpy(uri, argv[0], sizeof(uri) - 1);
|
||||
else if (getenv("DEVICE_URI") != NULL)
|
||||
strncpy(uri, getenv("DEVICE_URI"), sizeof(uri) - 1);
|
||||
else
|
||||
{
|
||||
fputs("ERROR: No device URI found in argv[0] or DEVICE_URI environment variable!\n", stderr);
|
||||
return (1);
|
||||
}
|
||||
|
||||
uri[sizeof(uri) - 1] = '\0';
|
||||
|
||||
/*
|
||||
* Extract the destination from the URI...
|
||||
*/
|
||||
|
||||
if ((sep = strrchr_m(uri, '@')) != NULL)
|
||||
{
|
||||
username = uri + 6;
|
||||
*sep++ = '\0';
|
||||
|
||||
server = sep;
|
||||
|
||||
/*
|
||||
* Extract password as needed...
|
||||
*/
|
||||
|
||||
if ((password = strchr_m(username, ':')) != NULL)
|
||||
*password++ = '\0';
|
||||
else
|
||||
password = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
username = "";
|
||||
password = "";
|
||||
server = uri + 6;
|
||||
}
|
||||
|
||||
if ((sep = strchr_m(server, '/')) == NULL)
|
||||
{
|
||||
fputs("ERROR: Bad URI - need printer name!\n", stderr);
|
||||
return (1);
|
||||
}
|
||||
|
||||
*sep++ = '\0';
|
||||
printer = sep;
|
||||
|
||||
if ((sep = strchr_m(printer, '/')) != NULL)
|
||||
{
|
||||
/*
|
||||
* Convert to smb://[username:password@]workgroup/server/printer...
|
||||
*/
|
||||
|
||||
*sep++ = '\0';
|
||||
|
||||
workgroup = server;
|
||||
server = printer;
|
||||
printer = sep;
|
||||
}
|
||||
else
|
||||
workgroup = NULL;
|
||||
|
||||
/*
|
||||
* Setup the SAMBA server state...
|
||||
*/
|
||||
|
||||
setup_logging(argv[0], DEBUG_STDOUT);
|
||||
|
||||
if (!lp_load()) {
|
||||
fprintf(stderr, "ERROR: Can't load %s - run testparm to debug it\n", lp_config_file());
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (workgroup == NULL)
|
||||
workgroup = lp_workgroup();
|
||||
|
||||
do
|
||||
{
|
||||
if ((cli = smb_connect(workgroup, server, printer, username, password)) == NULL)
|
||||
{
|
||||
if (getenv("CLASS") == NULL)
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to connect to SAMBA host, will retry in 60 seconds...");
|
||||
sleep (60);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unable to connect to SAMBA host, trying next printer...");
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
}
|
||||
while (cli == NULL);
|
||||
|
||||
/*
|
||||
* Now that we are connected to the server, ignore SIGTERM so that we
|
||||
* can finish out any page data the driver sends (e.g. to eject the
|
||||
* current page... Only ignore SIGTERM if we are printing data from
|
||||
* stdin (otherwise you can't cancel raw jobs...)
|
||||
*/
|
||||
|
||||
if (argc < 7)
|
||||
CatchSignal(SIGTERM, SIG_IGN);
|
||||
|
||||
/*
|
||||
* Queue the job...
|
||||
*/
|
||||
|
||||
for (i = 0; i < copies; i ++)
|
||||
if ((status = smb_print(cli, argv[3] /* title */, fp)) != 0)
|
||||
break;
|
||||
|
||||
talloc_free(cli);
|
||||
|
||||
/*
|
||||
* Return the queue status...
|
||||
*/
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'list_devices()' - List the available printers seen on the network...
|
||||
*/
|
||||
|
||||
static void
|
||||
list_devices(void)
|
||||
{
|
||||
/*
|
||||
* Eventually, search the local workgroup for available hosts and printers.
|
||||
*/
|
||||
|
||||
puts("network smb \"Unknown\" \"Windows Printer via SAMBA\"");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'smb_connect()' - Return a connection to a server.
|
||||
*/
|
||||
|
||||
static struct smbcli_state * /* O - SMB connection */
|
||||
smb_connect(const char *workgroup, /* I - Workgroup */
|
||||
const char *server, /* I - Server */
|
||||
const char *share, /* I - Printer */
|
||||
const char *username, /* I - Username */
|
||||
const char *password) /* I - Password */
|
||||
{
|
||||
struct smbcli_state *c; /* New connection */
|
||||
char *myname; /* Client name */
|
||||
NTSTATUS nt_status;
|
||||
|
||||
/*
|
||||
* Get the names and addresses of the client and server...
|
||||
*/
|
||||
|
||||
myname = get_myname();
|
||||
|
||||
nt_status = smbcli_full_connection(NULL, &c, myname, server, 0, share, NULL,
|
||||
username, workgroup, password, NULL);
|
||||
|
||||
free(myname);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
fprintf(stderr, "ERROR: Connection failed with error %s\n", nt_errstr(nt_status));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the new connection...
|
||||
*/
|
||||
|
||||
return (c);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'smb_print()' - Queue a job for printing using the SMB protocol.
|
||||
*/
|
||||
|
||||
static int /* O - 0 = success, non-0 = failure */
|
||||
smb_print(struct smbcli_state *cli, /* I - SMB connection */
|
||||
char *title, /* I - Title/job name */
|
||||
FILE *fp) /* I - File to print */
|
||||
{
|
||||
int fnum; /* File number */
|
||||
int nbytes, /* Number of bytes read */
|
||||
tbytes; /* Total bytes read */
|
||||
char buffer[8192], /* Buffer for copy */
|
||||
*ptr; /* Pointer into tile */
|
||||
|
||||
|
||||
/*
|
||||
* Sanitize the title...
|
||||
*/
|
||||
|
||||
for (ptr = title; *ptr; ptr ++)
|
||||
if (!isalnum((int)*ptr) && !isspace((int)*ptr))
|
||||
*ptr = '_';
|
||||
|
||||
/*
|
||||
* Open the printer device...
|
||||
*/
|
||||
|
||||
if ((fnum = smbcli_open(cli, title, O_RDWR | O_CREAT | O_TRUNC, DENY_NONE)) == -1)
|
||||
{
|
||||
fprintf(stderr, "ERROR: %s opening remote file %s\n",
|
||||
smbcli_errstr(cli), title);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy the file to the printer...
|
||||
*/
|
||||
|
||||
if (fp != stdin)
|
||||
rewind(fp);
|
||||
|
||||
tbytes = 0;
|
||||
|
||||
while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
|
||||
{
|
||||
if (smbcli_write(cli, fnum, 0, buffer, tbytes, nbytes) != nbytes)
|
||||
{
|
||||
fprintf(stderr, "ERROR: Error writing file: %s\n", smbcli_errstr(cli));
|
||||
break;
|
||||
}
|
||||
|
||||
tbytes += nbytes;
|
||||
}
|
||||
|
||||
if (!smbcli_close(cli, fnum))
|
||||
{
|
||||
fprintf(stderr, "ERROR: %s closing remote file %s\n",
|
||||
smbcli_errstr(cli), title);
|
||||
return (1);
|
||||
}
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* smbumount.c
|
||||
*
|
||||
* Copyright (C) 1995-1998 by Volker Lendecke
|
||||
*
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#include <mntent.h>
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <asm/posix_types.h>
|
||||
#include <linux/smb.h>
|
||||
#include <linux/smb_mount.h>
|
||||
#include <linux/smb_fs.h>
|
||||
|
||||
/* This is a (hopefully) temporary hack due to the fact that
|
||||
sizeof( uid_t ) != sizeof( __kernel_uid_t ) under glibc.
|
||||
This may change in the future and smb.h may get fixed in the
|
||||
future. In the mean time, it's ugly hack time - get over it.
|
||||
*/
|
||||
#undef SMB_IOC_GETMOUNTUID
|
||||
#define SMB_IOC_GETMOUNTUID _IOR('u', 1, __kernel_uid_t)
|
||||
|
||||
#ifndef O_NOFOLLOW
|
||||
#define O_NOFOLLOW 0400000
|
||||
#endif
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
printf("usage: smbumount mountpoint\n");
|
||||
}
|
||||
|
||||
static int
|
||||
umount_ok(const char *mount_point)
|
||||
{
|
||||
/* we set O_NOFOLLOW to prevent users playing games with symlinks to
|
||||
umount filesystems they don't own */
|
||||
int fid = open(mount_point, O_RDONLY|O_NOFOLLOW, 0);
|
||||
__kernel_uid_t mount_uid;
|
||||
|
||||
if (fid == -1) {
|
||||
fprintf(stderr, "Could not open %s: %s\n",
|
||||
mount_point, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(fid, SMB_IOC_GETMOUNTUID, &mount_uid) != 0) {
|
||||
fprintf(stderr, "%s probably not smb-filesystem\n",
|
||||
mount_point);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((getuid() != 0)
|
||||
&& (mount_uid != getuid())) {
|
||||
fprintf(stderr, "You are not allowed to umount %s\n",
|
||||
mount_point);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Make a canonical pathname from PATH. Returns a freshly malloced string.
|
||||
It is up the *caller* to ensure that the PATH is sensible. i.e.
|
||||
canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.''
|
||||
is not a legal pathname for ``/dev/fd0'' Anything we cannot parse
|
||||
we return unmodified. */
|
||||
static char *
|
||||
canonicalize (char *path)
|
||||
{
|
||||
char *canonical = malloc (PATH_MAX + 1);
|
||||
|
||||
if (!canonical) {
|
||||
fprintf(stderr, "Error! Not enough memory!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strlen(path) > PATH_MAX) {
|
||||
fprintf(stderr, "Mount point string too long\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (path == NULL)
|
||||
return NULL;
|
||||
|
||||
if (realpath (path, canonical))
|
||||
return canonical;
|
||||
|
||||
strncpy (canonical, path, PATH_MAX);
|
||||
canonical[PATH_MAX] = '\0';
|
||||
return canonical;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int fd;
|
||||
char* mount_point;
|
||||
struct mntent *mnt;
|
||||
FILE* mtab;
|
||||
FILE* new_mtab;
|
||||
|
||||
if (argc != 2) {
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (geteuid() != 0) {
|
||||
fprintf(stderr, "smbumount must be installed suid root\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
mount_point = canonicalize(argv[1]);
|
||||
|
||||
if (mount_point == NULL)
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (umount_ok(mount_point) != 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (umount(mount_point) != 0) {
|
||||
fprintf(stderr, "Could not umount %s: %s\n",
|
||||
mount_point, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1)
|
||||
{
|
||||
fprintf(stderr, "Can't get "MOUNTED"~ lock file");
|
||||
return 1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
|
||||
fprintf(stderr, "Can't open " MOUNTED ": %s\n",
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define MOUNTED_TMP MOUNTED".tmp"
|
||||
|
||||
if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
|
||||
fprintf(stderr, "Can't open " MOUNTED_TMP ": %s\n",
|
||||
strerror(errno));
|
||||
endmntent(mtab);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while ((mnt = getmntent(mtab)) != NULL) {
|
||||
if (strcmp(mnt->mnt_dir, mount_point) != 0) {
|
||||
addmntent(new_mtab, mnt);
|
||||
}
|
||||
}
|
||||
|
||||
endmntent(mtab);
|
||||
|
||||
if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
|
||||
fprintf(stderr, "Error changing mode of %s: %s\n",
|
||||
MOUNTED_TMP, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
endmntent(new_mtab);
|
||||
|
||||
if (rename(MOUNTED_TMP, MOUNTED) < 0) {
|
||||
fprintf(stderr, "Cannot rename %s to %s: %s\n",
|
||||
MOUNTED, MOUNTED_TMP, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (unlink(MOUNTED"~") == -1)
|
||||
{
|
||||
fprintf(stderr, "Can't remove "MOUNTED"~");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,810 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
SMB client GTK+ tree-based application
|
||||
Copyright (C) Andrew Tridgell 1998
|
||||
Copyright (C) Richard Sharpe 2001
|
||||
Copyright (C) John Terpstra 2001
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* example-gtk+ application, ripped off from the gtk+ tree.c sample */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include "libsmbclient.h"
|
||||
|
||||
static GtkWidget *clist;
|
||||
|
||||
struct tree_data {
|
||||
|
||||
guint32 type; /* Type of tree item, an SMBC_TYPE */
|
||||
char name[256]; /* May need to change this later */
|
||||
|
||||
};
|
||||
|
||||
void error_message(gchar *message) {
|
||||
|
||||
GtkWidget *dialog, *label, *okay_button;
|
||||
|
||||
/* Create the widgets */
|
||||
|
||||
dialog = gtk_dialog_new();
|
||||
gtk_window_set_modal(GTK_WINDOW(dialog), True);
|
||||
label = gtk_label_new (message);
|
||||
okay_button = gtk_button_new_with_label("Okay");
|
||||
|
||||
/* Ensure that the dialog box is destroyed when the user clicks ok. */
|
||||
|
||||
gtk_signal_connect_object (GTK_OBJECT (okay_button), "clicked",
|
||||
GTK_SIGNAL_FUNC (gtk_widget_destroy), dialog);
|
||||
gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->action_area),
|
||||
okay_button);
|
||||
|
||||
/* Add the label, and show everything we've added to the dialog. */
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
|
||||
label);
|
||||
gtk_widget_show_all (dialog);
|
||||
}
|
||||
|
||||
/*
|
||||
* We are given a widget, and we want to retrieve its URL so we
|
||||
* can do a directory listing.
|
||||
*
|
||||
* We walk back up the tree, picking up pieces until we hit a server or
|
||||
* workgroup type and return a path from there
|
||||
*/
|
||||
|
||||
static char path_string[1024];
|
||||
|
||||
char *get_path(GtkWidget *item)
|
||||
{
|
||||
GtkWidget *p = item;
|
||||
struct tree_data *pd;
|
||||
char *comps[1024]; /* We keep pointers to the components here */
|
||||
int i = 0, j, level,type;
|
||||
|
||||
/* Walk back up the tree, getting the private data */
|
||||
|
||||
level = GTK_TREE(item->parent)->level;
|
||||
|
||||
/* Pick up this item's component info */
|
||||
|
||||
pd = (struct tree_data *)gtk_object_get_user_data(GTK_OBJECT(item));
|
||||
|
||||
comps[i++] = pd->name;
|
||||
type = pd->type;
|
||||
|
||||
while (level > 0 && type != SMBC_SERVER && type != SMBC_WORKGROUP) {
|
||||
|
||||
/* Find the parent and extract the data etc ... */
|
||||
|
||||
p = GTK_WIDGET(p->parent);
|
||||
p = GTK_WIDGET(GTK_TREE(p)->tree_owner);
|
||||
|
||||
pd = (struct tree_data *)gtk_object_get_user_data(GTK_OBJECT(p));
|
||||
|
||||
level = GTK_TREE(item->parent)->level;
|
||||
|
||||
comps[i++] = pd->name;
|
||||
type = pd->type;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Got a list of comps now, should check that we did not hit a workgroup
|
||||
* when we got other things as well ... Later
|
||||
*
|
||||
* Now, build the path
|
||||
*/
|
||||
|
||||
snprintf(path_string, sizeof(path_string), "smb:/");
|
||||
|
||||
for (j = i - 1; j >= 0; j--) {
|
||||
|
||||
strncat(path_string, "/", sizeof(path_string) - strlen(path_string));
|
||||
strncat(path_string, comps[j], sizeof(path_string) - strlen(path_string));
|
||||
|
||||
}
|
||||
|
||||
fprintf(stdout, "Path string = %s\n", path_string);
|
||||
|
||||
return path_string;
|
||||
|
||||
}
|
||||
|
||||
struct tree_data *make_tree_data(guint32 type, const char *name)
|
||||
{
|
||||
struct tree_data *p = malloc_p(struct tree_data);
|
||||
|
||||
if (p) {
|
||||
|
||||
p->type = type;
|
||||
strncpy(p->name, name, sizeof(p->name));
|
||||
|
||||
}
|
||||
|
||||
return p;
|
||||
|
||||
}
|
||||
|
||||
/* Note that this is called every time the user clicks on an item,
|
||||
whether it is already selected or not. */
|
||||
static void cb_select_child (GtkWidget *root_tree, GtkWidget *child,
|
||||
GtkWidget *subtree)
|
||||
{
|
||||
gint dh, err, dirlen;
|
||||
char dirbuf[512];
|
||||
struct smbc_dirent *dirp;
|
||||
struct stat st1;
|
||||
char path[1024], path1[1024];
|
||||
|
||||
g_print ("select_child called for root tree %p, subtree %p, child %p\n",
|
||||
root_tree, subtree, child);
|
||||
|
||||
/* Now, figure out what it is, and display it in the clist ... */
|
||||
|
||||
gtk_clist_clear(GTK_CLIST(clist)); /* Clear the CLIST */
|
||||
|
||||
/* Now, get the private data for the subtree */
|
||||
|
||||
strncpy(path, get_path(child), 1024);
|
||||
|
||||
if ((dh = smbc_opendir(path)) < 0) { /* Handle error */
|
||||
|
||||
g_print("cb_select_child: Could not open dir %s, %s\n", path,
|
||||
strerror(errno));
|
||||
|
||||
gtk_main_quit();
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf,
|
||||
sizeof(dirbuf))) != 0) {
|
||||
|
||||
if (err < 0) {
|
||||
|
||||
g_print("cb_select_child: Could not read dir %s, %s\n", path,
|
||||
strerror(errno));
|
||||
|
||||
gtk_main_quit();
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
dirp = (struct smbc_dirent *)dirbuf;
|
||||
|
||||
while (err > 0) {
|
||||
gchar col1[128], col2[128], col3[128], col4[128];
|
||||
gchar *rowdata[4] = {col1, col2, col3, col4};
|
||||
|
||||
dirlen = dirp->dirlen;
|
||||
|
||||
/* Format each of the items ... */
|
||||
|
||||
strncpy(col1, dirp->name, 128);
|
||||
|
||||
col2[0] = col3[0] = col4[0] = (char)0;
|
||||
|
||||
switch (dirp->smbc_type) {
|
||||
|
||||
case SMBC_WORKGROUP:
|
||||
|
||||
break;
|
||||
|
||||
case SMBC_SERVER:
|
||||
|
||||
strncpy(col2, (dirp->comment?dirp->comment:""), 128);
|
||||
|
||||
break;
|
||||
|
||||
case SMBC_FILE_SHARE:
|
||||
|
||||
strncpy(col2, (dirp->comment?dirp->comment:""), 128);
|
||||
|
||||
break;
|
||||
|
||||
case SMBC_PRINTER_SHARE:
|
||||
|
||||
strncpy(col2, (dirp->comment?dirp->comment:""), 128);
|
||||
break;
|
||||
|
||||
case SMBC_COMMS_SHARE:
|
||||
|
||||
break;
|
||||
|
||||
case SMBC_IPC_SHARE:
|
||||
|
||||
break;
|
||||
|
||||
case SMBC_DIR:
|
||||
case SMBC_FILE:
|
||||
|
||||
/* Get stats on the file/dir and see what we have */
|
||||
|
||||
if (!ISDOT(dirp->name) && !ISDOTDOT(dirp->name)) {
|
||||
|
||||
strncpy(path1, path, sizeof(path1));
|
||||
strncat(path1, "/", sizeof(path) - strlen(path));
|
||||
strncat(path1, dirp->name, sizeof(path) - strlen(path));
|
||||
|
||||
if (smbc_stat(path1, &st1) < 0) {
|
||||
|
||||
if (errno != EBUSY) {
|
||||
|
||||
g_print("cb_select_child: Could not stat file %s, %s\n", path1,
|
||||
strerror(errno));
|
||||
|
||||
gtk_main_quit();
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
strncpy(col2, "Device or resource busy", sizeof(col2));
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Now format each of the relevant things ... */
|
||||
|
||||
snprintf(col2, sizeof(col2), "%c%c%c%c%c%c%c%c%c(%0X)",
|
||||
(st1.st_mode&S_IRUSR?'r':'-'),
|
||||
(st1.st_mode&S_IWUSR?'w':'-'),
|
||||
(st1.st_mode&S_IXUSR?'x':'-'),
|
||||
(st1.st_mode&S_IRGRP?'r':'-'),
|
||||
(st1.st_mode&S_IWGRP?'w':'-'),
|
||||
(st1.st_mode&S_IXGRP?'x':'-'),
|
||||
(st1.st_mode&S_IROTH?'r':'-'),
|
||||
(st1.st_mode&S_IWOTH?'w':'-'),
|
||||
(st1.st_mode&S_IXOTH?'x':'-'),
|
||||
st1.st_mode);
|
||||
snprintf(col3, sizeof(col3), "%u", st1.st_size);
|
||||
snprintf(col4, sizeof(col4), "%s", ctime(&st1.st_mtime));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
gtk_clist_append(GTK_CLIST(clist), rowdata);
|
||||
|
||||
(char *)dirp += dirlen;
|
||||
err -= dirlen;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Note that this is never called */
|
||||
static void cb_unselect_child( GtkWidget *root_tree,
|
||||
GtkWidget *child,
|
||||
GtkWidget *subtree )
|
||||
{
|
||||
g_print ("unselect_child called for root tree %p, subtree %p, child %p\n",
|
||||
root_tree, subtree, child);
|
||||
}
|
||||
|
||||
/* for all the GtkItem:: and GtkTreeItem:: signals */
|
||||
static void cb_itemsignal( GtkWidget *item,
|
||||
gchar *signame )
|
||||
{
|
||||
GtkWidget *real_tree, *aitem, *subtree;
|
||||
gchar *name;
|
||||
GtkLabel *label;
|
||||
gint dh, err, dirlen, level;
|
||||
char dirbuf[512];
|
||||
struct smbc_dirent *dirp;
|
||||
|
||||
label = GTK_LABEL (GTK_BIN (item)->child);
|
||||
/* Get the text of the label */
|
||||
gtk_label_get (label, &name);
|
||||
|
||||
level = GTK_TREE(item->parent)->level;
|
||||
|
||||
/* Get the level of the tree which the item is in */
|
||||
g_print ("%s called for item %s->%p, level %d\n", signame, name,
|
||||
item, GTK_TREE (item->parent)->level);
|
||||
|
||||
real_tree = GTK_TREE_ITEM_SUBTREE(item); /* Get the subtree */
|
||||
|
||||
if (strncmp(signame, "expand", 6) == 0) { /* Expand called */
|
||||
char server[128];
|
||||
|
||||
if ((dh = smbc_opendir(get_path(item))) < 0) { /* Handle error */
|
||||
gchar errmsg[256];
|
||||
|
||||
g_print("cb_itemsignal: Could not open dir %s, %s\n", get_path(item),
|
||||
strerror(errno));
|
||||
|
||||
slprintf(errmsg, sizeof(errmsg), "cb_itemsignal: Could not open dir %s, %s\n", get_path(item), strerror(errno));
|
||||
|
||||
error_message(errmsg);
|
||||
|
||||
/* gtk_main_quit();*/
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf,
|
||||
sizeof(dirbuf))) != 0) {
|
||||
|
||||
if (err < 0) { /* An error, report it */
|
||||
gchar errmsg[256];
|
||||
|
||||
g_print("cb_itemsignal: Could not read dir smbc://, %s\n",
|
||||
strerror(errno));
|
||||
|
||||
slprintf(errmsg, sizeof(errmsg), "cb_itemsignal: Could not read dir smbc://, %s\n", strerror(errno));
|
||||
|
||||
error_message(errmsg);
|
||||
|
||||
/* gtk_main_quit();*/
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
dirp = (struct smbc_dirent *)dirbuf;
|
||||
|
||||
while (err > 0) {
|
||||
struct tree_data *my_data;
|
||||
|
||||
dirlen = dirp->dirlen;
|
||||
|
||||
my_data = make_tree_data(dirp->smbc_type, dirp->name);
|
||||
|
||||
if (!my_data) {
|
||||
|
||||
g_print("Could not allocate space for tree_data: %s\n",
|
||||
dirp->name);
|
||||
|
||||
gtk_main_quit();
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
aitem = gtk_tree_item_new_with_label(dirp->name);
|
||||
|
||||
/* Connect all GtkItem:: and GtkTreeItem:: signals */
|
||||
gtk_signal_connect (GTK_OBJECT(aitem), "select",
|
||||
GTK_SIGNAL_FUNC(cb_itemsignal), "select");
|
||||
gtk_signal_connect (GTK_OBJECT(aitem), "deselect",
|
||||
GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
|
||||
gtk_signal_connect (GTK_OBJECT(aitem), "toggle",
|
||||
GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
|
||||
gtk_signal_connect (GTK_OBJECT(aitem), "expand",
|
||||
GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
|
||||
gtk_signal_connect (GTK_OBJECT(aitem), "collapse",
|
||||
GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
|
||||
/* Add it to the parent tree */
|
||||
gtk_tree_append (GTK_TREE(real_tree), aitem);
|
||||
|
||||
gtk_widget_show (aitem);
|
||||
|
||||
gtk_object_set_user_data(GTK_OBJECT(aitem), (gpointer)my_data);
|
||||
|
||||
fprintf(stdout, "Added: %s, len: %u\n", dirp->name, dirlen);
|
||||
|
||||
if (dirp->smbc_type != SMBC_FILE &&
|
||||
dirp->smbc_type != SMBC_IPC_SHARE &&
|
||||
(!ISDOT(dirp->name)) &&
|
||||
(!ISDOTDOT(dirp->name))){
|
||||
|
||||
subtree = gtk_tree_new();
|
||||
gtk_tree_item_set_subtree(GTK_TREE_ITEM(aitem), subtree);
|
||||
|
||||
gtk_signal_connect(GTK_OBJECT(subtree), "select_child",
|
||||
GTK_SIGNAL_FUNC(cb_select_child), real_tree);
|
||||
gtk_signal_connect(GTK_OBJECT(subtree), "unselect_child",
|
||||
GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
|
||||
|
||||
}
|
||||
|
||||
(char *)dirp += dirlen;
|
||||
err -= dirlen;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
smbc_closedir(dh);
|
||||
|
||||
}
|
||||
else if (strncmp(signame, "collapse", 8) == 0) {
|
||||
GtkWidget *subtree = gtk_tree_new();
|
||||
|
||||
gtk_tree_remove_items(GTK_TREE(real_tree), GTK_TREE(real_tree)->children);
|
||||
|
||||
gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
|
||||
|
||||
gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
|
||||
GTK_SIGNAL_FUNC(cb_select_child), real_tree);
|
||||
gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
|
||||
GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void cb_selection_changed( GtkWidget *tree )
|
||||
{
|
||||
GList *i;
|
||||
|
||||
g_print ("selection_change called for tree %p\n", tree);
|
||||
g_print ("selected objects are:\n");
|
||||
|
||||
i = GTK_TREE_SELECTION(tree);
|
||||
while (i){
|
||||
gchar *name;
|
||||
GtkLabel *label;
|
||||
GtkWidget *item;
|
||||
|
||||
/* Get a GtkWidget pointer from the list node */
|
||||
item = GTK_WIDGET (i->data);
|
||||
label = GTK_LABEL (GTK_BIN (item)->child);
|
||||
gtk_label_get (label, &name);
|
||||
g_print ("\t%s on level %d\n", name, GTK_TREE
|
||||
(item->parent)->level);
|
||||
i = i->next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Expand or collapse the whole network ...
|
||||
*/
|
||||
static void cb_wholenet(GtkWidget *item, gchar *signame)
|
||||
{
|
||||
GtkWidget *real_tree, *aitem, *subtree;
|
||||
gchar *name;
|
||||
GtkLabel *label;
|
||||
gint dh, err, dirlen;
|
||||
char dirbuf[512];
|
||||
struct smbc_dirent *dirp;
|
||||
|
||||
label = GTK_LABEL (GTK_BIN (item)->child);
|
||||
gtk_label_get (label, &name);
|
||||
g_print ("%s called for item %s->%p, level %d\n", signame, name,
|
||||
item, GTK_TREE (item->parent)->level);
|
||||
|
||||
real_tree = GTK_TREE_ITEM_SUBTREE(item); /* Get the subtree */
|
||||
|
||||
if (strncmp(signame, "expand", 6) == 0) { /* Expand called */
|
||||
|
||||
if ((dh = smbc_opendir("smb://")) < 0) { /* Handle error */
|
||||
|
||||
g_print("cb_wholenet: Could not open dir smbc://, %s\n",
|
||||
strerror(errno));
|
||||
|
||||
gtk_main_quit();
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf,
|
||||
sizeof(dirbuf))) != 0) {
|
||||
|
||||
if (err < 0) { /* An error, report it */
|
||||
|
||||
g_print("cb_wholenet: Could not read dir smbc://, %s\n",
|
||||
strerror(errno));
|
||||
|
||||
gtk_main_quit();
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
dirp = (struct smbc_dirent *)dirbuf;
|
||||
|
||||
while (err > 0) {
|
||||
struct tree_data *my_data;
|
||||
|
||||
dirlen = dirp->dirlen;
|
||||
|
||||
my_data = make_tree_data(dirp->smbc_type, dirp->name);
|
||||
|
||||
aitem = gtk_tree_item_new_with_label(dirp->name);
|
||||
|
||||
/* Connect all GtkItem:: and GtkTreeItem:: signals */
|
||||
gtk_signal_connect (GTK_OBJECT(aitem), "select",
|
||||
GTK_SIGNAL_FUNC(cb_itemsignal), "select");
|
||||
gtk_signal_connect (GTK_OBJECT(aitem), "deselect",
|
||||
GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
|
||||
gtk_signal_connect (GTK_OBJECT(aitem), "toggle",
|
||||
GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
|
||||
gtk_signal_connect (GTK_OBJECT(aitem), "expand",
|
||||
GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
|
||||
gtk_signal_connect (GTK_OBJECT(aitem), "collapse",
|
||||
GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
|
||||
|
||||
gtk_tree_append (GTK_TREE(real_tree), aitem);
|
||||
/* Show it - this can be done at any time */
|
||||
gtk_widget_show (aitem);
|
||||
|
||||
gtk_object_set_user_data(GTK_OBJECT(aitem), (gpointer)my_data);
|
||||
|
||||
fprintf(stdout, "Added: %s, len: %u\n", dirp->name, dirlen);
|
||||
|
||||
subtree = gtk_tree_new();
|
||||
|
||||
gtk_tree_item_set_subtree(GTK_TREE_ITEM(aitem), subtree);
|
||||
|
||||
gtk_signal_connect(GTK_OBJECT(subtree), "select_child",
|
||||
GTK_SIGNAL_FUNC(cb_select_child), real_tree);
|
||||
gtk_signal_connect(GTK_OBJECT(subtree), "unselect_child",
|
||||
GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
|
||||
|
||||
(char *)dirp += dirlen;
|
||||
err -= dirlen;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
smbc_closedir(dh);
|
||||
|
||||
}
|
||||
else { /* Must be collapse ... FIXME ... */
|
||||
GtkWidget *subtree = gtk_tree_new();
|
||||
|
||||
gtk_tree_remove_items(GTK_TREE(real_tree), GTK_TREE(real_tree)->children);
|
||||
|
||||
gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
|
||||
|
||||
gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
|
||||
GTK_SIGNAL_FUNC(cb_select_child), real_tree);
|
||||
gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
|
||||
GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Should put up a dialog box to ask the user for username and password */
|
||||
|
||||
static void
|
||||
auth_fn(const char *server, const char *share,
|
||||
char *workgroup, int wgmaxlen, char *username, int unmaxlen,
|
||||
char *password, int pwmaxlen)
|
||||
{
|
||||
|
||||
strncpy(username, "test", unmaxlen);
|
||||
strncpy(password, "test", pwmaxlen);
|
||||
|
||||
}
|
||||
|
||||
static char *col_titles[] = {
|
||||
"Name", "Attributes", "Size", "Modification Date",
|
||||
};
|
||||
|
||||
int main( int argc,
|
||||
char *argv[] )
|
||||
{
|
||||
GtkWidget *window, *scrolled_win, *scrolled_win2, *tree;
|
||||
GtkWidget *subtree, *item, *main_hbox, *r_pane, *l_pane;
|
||||
gint err, dh;
|
||||
gint i;
|
||||
char dirbuf[512];
|
||||
struct smbc_dirent *dirp;
|
||||
|
||||
gtk_init (&argc, &argv);
|
||||
|
||||
/* Init the smbclient library */
|
||||
|
||||
err = smbc_init(auth_fn, 10);
|
||||
|
||||
/* Print an error response ... */
|
||||
|
||||
if (err < 0) {
|
||||
|
||||
fprintf(stderr, "smbc_init returned %s (%i)\nDo you have a ~/.smb/smb.conf file?\n", strerror(errno), errno);
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
/* a generic toplevel window */
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_widget_set_name(window, "main browser window");
|
||||
gtk_signal_connect (GTK_OBJECT(window), "delete_event",
|
||||
GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
|
||||
gtk_window_set_title(GTK_WINDOW(window), "The Linux Windows Network Browser");
|
||||
gtk_widget_set_usize(GTK_WIDGET(window), 750, -1);
|
||||
gtk_container_set_border_width (GTK_CONTAINER(window), 5);
|
||||
|
||||
gtk_widget_show (window);
|
||||
|
||||
/* A container for the two panes ... */
|
||||
|
||||
main_hbox = gtk_hbox_new(FALSE, 1);
|
||||
gtk_container_border_width(GTK_CONTAINER(main_hbox), 1);
|
||||
gtk_container_add(GTK_CONTAINER(window), main_hbox);
|
||||
|
||||
gtk_widget_show(main_hbox);
|
||||
|
||||
l_pane = gtk_hpaned_new();
|
||||
gtk_paned_gutter_size(GTK_PANED(l_pane), (GTK_PANED(l_pane))->handle_size);
|
||||
r_pane = gtk_hpaned_new();
|
||||
gtk_paned_gutter_size(GTK_PANED(r_pane), (GTK_PANED(r_pane))->handle_size);
|
||||
gtk_container_add(GTK_CONTAINER(main_hbox), l_pane);
|
||||
gtk_widget_show(l_pane);
|
||||
|
||||
/* A generic scrolled window */
|
||||
scrolled_win = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
|
||||
GTK_POLICY_AUTOMATIC,
|
||||
GTK_POLICY_AUTOMATIC);
|
||||
gtk_widget_set_usize (scrolled_win, 150, 200);
|
||||
gtk_container_add (GTK_CONTAINER(l_pane), scrolled_win);
|
||||
gtk_widget_show (scrolled_win);
|
||||
|
||||
/* Another generic scrolled window */
|
||||
scrolled_win2 = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win2),
|
||||
GTK_POLICY_AUTOMATIC,
|
||||
GTK_POLICY_AUTOMATIC);
|
||||
gtk_widget_set_usize (scrolled_win2, 150, 200);
|
||||
gtk_paned_add2 (GTK_PANED(l_pane), scrolled_win2);
|
||||
gtk_widget_show (scrolled_win2);
|
||||
|
||||
/* Create the root tree */
|
||||
tree = gtk_tree_new();
|
||||
g_print ("root tree is %p\n", tree);
|
||||
/* connect all GtkTree:: signals */
|
||||
gtk_signal_connect (GTK_OBJECT(tree), "select_child",
|
||||
GTK_SIGNAL_FUNC(cb_select_child), tree);
|
||||
gtk_signal_connect (GTK_OBJECT(tree), "unselect_child",
|
||||
GTK_SIGNAL_FUNC(cb_unselect_child), tree);
|
||||
gtk_signal_connect (GTK_OBJECT(tree), "selection_changed",
|
||||
GTK_SIGNAL_FUNC(cb_selection_changed), tree);
|
||||
/* Add it to the scrolled window */
|
||||
gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(scrolled_win),
|
||||
tree);
|
||||
/* Set the selection mode */
|
||||
gtk_tree_set_selection_mode (GTK_TREE(tree),
|
||||
GTK_SELECTION_MULTIPLE);
|
||||
/* Show it */
|
||||
gtk_widget_show (tree);
|
||||
|
||||
/* Now, create a clist and attach it to the second pane */
|
||||
|
||||
clist = gtk_clist_new_with_titles(4, col_titles);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER(scrolled_win2), clist);
|
||||
|
||||
gtk_widget_show(clist);
|
||||
|
||||
/* Now, build the top level display ... */
|
||||
|
||||
if ((dh = smbc_opendir("smb:///")) < 0) {
|
||||
|
||||
fprintf(stderr, "Could not list default workgroup: smb:///: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
/* Create a tree item for Whole Network */
|
||||
|
||||
item = gtk_tree_item_new_with_label ("Whole Network");
|
||||
/* Connect all GtkItem:: and GtkTreeItem:: signals */
|
||||
gtk_signal_connect (GTK_OBJECT(item), "select",
|
||||
GTK_SIGNAL_FUNC(cb_itemsignal), "select");
|
||||
gtk_signal_connect (GTK_OBJECT(item), "deselect",
|
||||
GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
|
||||
gtk_signal_connect (GTK_OBJECT(item), "toggle",
|
||||
GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
|
||||
gtk_signal_connect (GTK_OBJECT(item), "expand",
|
||||
GTK_SIGNAL_FUNC(cb_wholenet), "expand");
|
||||
gtk_signal_connect (GTK_OBJECT(item), "collapse",
|
||||
GTK_SIGNAL_FUNC(cb_wholenet), "collapse");
|
||||
/* Add it to the parent tree */
|
||||
gtk_tree_append (GTK_TREE(tree), item);
|
||||
/* Show it - this can be done at any time */
|
||||
gtk_widget_show (item);
|
||||
|
||||
subtree = gtk_tree_new(); /* A subtree for Whole Network */
|
||||
|
||||
gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
|
||||
|
||||
gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
|
||||
GTK_SIGNAL_FUNC(cb_select_child), tree);
|
||||
gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
|
||||
GTK_SIGNAL_FUNC(cb_unselect_child), tree);
|
||||
|
||||
/* Now, get the items in smb:/// and add them to the tree */
|
||||
|
||||
dirp = (struct smbc_dirent *)dirbuf;
|
||||
|
||||
while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf,
|
||||
sizeof(dirbuf))) != 0) {
|
||||
|
||||
if (err < 0) { /* Handle the error */
|
||||
|
||||
fprintf(stderr, "Could not read directory for smbc:///: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
fprintf(stdout, "Dir len: %u\n", err);
|
||||
|
||||
while (err > 0) { /* Extract each entry and make a sub-tree */
|
||||
struct tree_data *my_data;
|
||||
int dirlen = dirp->dirlen;
|
||||
|
||||
my_data = make_tree_data(dirp->smbc_type, dirp->name);
|
||||
|
||||
item = gtk_tree_item_new_with_label(dirp->name);
|
||||
/* Connect all GtkItem:: and GtkTreeItem:: signals */
|
||||
gtk_signal_connect (GTK_OBJECT(item), "select",
|
||||
GTK_SIGNAL_FUNC(cb_itemsignal), "select");
|
||||
gtk_signal_connect (GTK_OBJECT(item), "deselect",
|
||||
GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
|
||||
gtk_signal_connect (GTK_OBJECT(item), "toggle",
|
||||
GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
|
||||
gtk_signal_connect (GTK_OBJECT(item), "expand",
|
||||
GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
|
||||
gtk_signal_connect (GTK_OBJECT(item), "collapse",
|
||||
GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
|
||||
/* Add it to the parent tree */
|
||||
gtk_tree_append (GTK_TREE(tree), item);
|
||||
/* Show it - this can be done at any time */
|
||||
gtk_widget_show (item);
|
||||
|
||||
gtk_object_set_user_data(GTK_OBJECT(item), (gpointer)my_data);
|
||||
|
||||
fprintf(stdout, "Added: %s, len: %u\n", dirp->name, dirlen);
|
||||
|
||||
subtree = gtk_tree_new();
|
||||
|
||||
gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
|
||||
|
||||
gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
|
||||
GTK_SIGNAL_FUNC(cb_select_child), tree);
|
||||
gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
|
||||
GTK_SIGNAL_FUNC(cb_unselect_child), tree);
|
||||
|
||||
(char *)dirp += dirlen;
|
||||
err -= dirlen;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
smbc_closedir(dh); /* FIXME, check for error :-) */
|
||||
|
||||
/* Show the window and loop endlessly */
|
||||
gtk_main();
|
||||
return 0;
|
||||
}
|
||||
/* example-end */
|
||||
Reference in New Issue
Block a user