wmi-1.3.16 from opsview.com

This commit is contained in:
Are Casilla
2019-02-16 00:16:52 +01:00
parent 163fdd3d1b
commit 17b3af2911
2146 changed files with 678824 additions and 0 deletions
+601
View File
@@ -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 : */
+99
View File
@@ -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 : */
+496
View File
@@ -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
+37
View File
@@ -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
#################################
+557
View File
@@ -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;
}
+306
View File
@@ -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;
}
+925
View File
@@ -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;
}
+350
View File
@@ -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);
}
+186
View File
@@ -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;
}
+810
View File
@@ -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 */