Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion configure.ac
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
AC_INIT([libsmb2],[6.1.0],[[email protected]])

AC_PREREQ([2.71])
AC_PREREQ([2.69])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([-Wall foreign subdir-objects 1.11])
AC_CANONICAL_HOST
Expand Down Expand Up @@ -87,6 +87,8 @@ esac
AM_CONDITIONAL([HAVE_WIN32], [test "${SYS}" = "mingw32"])
AC_SUBST([LIBSOCKET])

AC_CHECK_LIB([dl], [dlsym], [], [AC_MSG_ERROR([dlsym not found, libdl is required])])

dnl Check for poll.h
AC_CHECK_HEADERS([poll.h])

Expand Down
31 changes: 21 additions & 10 deletions lib/socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,28 @@ smb2_close_connecting_fds(struct smb2_context *smb2)
smb2->next_addrinfo = NULL;
}

static int
smb2_get_real_credit_charge_for_one_pdu(struct smb2_context *smb2, struct smb2_header *hdr)
{
int credits;

credits = hdr->credit_charge;
if (hdr->command == SMB2_NEGOTIATE) {
/* Mirror the special case in smb2_allocate_pdu. */
} else if (smb2->dialect <= SMB2_VERSION_0202) {
++ credits;
}

return credits;
}

static int
smb2_get_credit_charge(struct smb2_context *smb2, struct smb2_pdu *pdu)
{
int credits = 0;

while (pdu) {
credits += pdu->header.credit_charge;
credits += smb2_get_real_credit_charge_for_one_pdu(smb2, &pdu->header);
pdu = pdu->next_compound;
}

Expand Down Expand Up @@ -222,15 +237,11 @@ smb2_write_to_socket(struct smb2_context *smb2)
size_t num_done = pdu->out.num_done;
int i, niov = 1;
ssize_t count;
uint32_t spl = 0, tmp_spl, credit_charge = 0;
uint32_t spl = 0, tmp_spl, credit_charge;

for (tmp_pdu = pdu; tmp_pdu; tmp_pdu = tmp_pdu->next_compound) {
credit_charge += tmp_pdu->header.credit_charge;
}
if (smb2->dialect > SMB2_VERSION_0202) {
if (credit_charge > (uint32_t)smb2->credits) {
return 0;
}
credit_charge = smb2_get_credit_charge(smb2, pdu);
if (credit_charge > (uint32_t)smb2->credits) {
return 0;
}

if (pdu->seal) {
Expand Down Expand Up @@ -306,7 +317,7 @@ smb2_write_to_socket(struct smb2_context *smb2)
pdu->next_compound = NULL;

if (!smb2_is_server(smb2)) {
smb2->credits -= pdu->header.credit_charge;
smb2->credits -= smb2_get_real_credit_charge_for_one_pdu(smb2, &pdu->header);
/* queue requests we send to correlate replies with */
SMB2_LIST_ADD_END(&smb2->waitqueue, pdu);
}
Expand Down
1 change: 1 addition & 0 deletions tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ LDADD = ../lib/libsmb2.la

noinst_PROGRAMS = prog_ls prog_mkdir prog_rmdir prog_cat \
prog_cat_cancel smb2-dcerpc-coder-test
noinst_PROGRAMS += metastat-0202-censored

EXTRA_PROGRAMS = ld_sockerr
CLEANFILES = ld_sockerr.o ld_sockerr.so
Expand Down
136 changes: 136 additions & 0 deletions tests/metastat-0202-censored.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// metastat.c — queue multiple SMB2 STATs in parallel on one connection
#define _GNU_SOURCE
#include <errno.h>
#include <poll.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#include <smb2/smb2.h>
#include <smb2/libsmb2.h>

struct op {
const char *path; // relative path under share (or URL path + filename)
struct smb2_stat_64 st; // result buffer filled by libsmb2
int status; // 0 = OK from callback
};

struct cbwrap {
struct op *op;
int *pending; // shared counter
};

static void stat_cb(struct smb2_context *ctx, int status, void *cmd_data, void *cb_data) {
(void)ctx; (void)cmd_data;
struct cbwrap *w = (struct cbwrap *)cb_data;
w->op->status = status;
if (w->pending) (*w->pending)--;
free(w);
}

static int service_loop(struct smb2_context *ctx, int *pending) {
while (*pending > 0) {
struct pollfd pfd = { .fd = smb2_get_fd(ctx), .events = smb2_which_events(ctx) };
int ret = poll(&pfd, 1, 1000);
if (ret < 0) { perror("poll"); return -1; }
if (ret == 0) continue;
if (smb2_service(ctx, pfd.revents) < 0) {
fprintf(stderr, "smb2_service: %s\n", smb2_get_error(ctx));
return -1;
}
}
return 0;
}

static void usage(const char *prog) {
fprintf(stderr,
"Usage: %s [-p PASSWORD] smb://[domain;][user@]server/share[/base][?args] file1 [file2 ...]\n",
prog);
}

int main(int argc, char **argv) {
const char *password = NULL;
int opt;
while ((opt = getopt(argc, argv, "p:")) != -1) {
if (opt == 'p') password = optarg;
else { usage(argv[0]); return 2; }
}
if (optind >= argc || optind == argc-1) { usage(argv[0]); return 2; }

const char *urlstr = argv[optind++];
int nfiles = argc - optind;

struct smb2_context *ctx = smb2_init_context();
if (!ctx) { fprintf(stderr, "smb2_init_context failed\n"); return 1; }

struct smb2_url *url = smb2_parse_url(ctx, urlstr);
if (!url) { fprintf(stderr, "parse_url: %s\n", smb2_get_error(ctx)); return 1; }

if (url->user) smb2_set_user(ctx, url->user);
if (url->domain) smb2_set_domain(ctx, url->domain);
if (password) smb2_set_password(ctx, password);
smb2_set_version(ctx, SMB2_VERSION_0202);

// One connection/session to the share
if (smb2_connect_share(ctx, url->server, url->share, url->user) != 0) {
fprintf(stderr, "connect_share: %s\n", smb2_get_error(ctx));
return 1;
}

// Queue parallel STATs
int pending = 0;
for (int i = 0; i < nfiles; i++) {
const char *name = argv[optind + i];
char rel[4096];

if (url->path && *url->path)
snprintf(rel, sizeof rel, "%s/%s", url->path, name);
else
snprintf(rel, sizeof rel, "%s", name);

struct op *op = calloc(1, sizeof *op);
if (!op) { perror("calloc"); return 1; }
op->path = name;

struct cbwrap *w = malloc(sizeof *w);
if (!w) { perror("malloc"); free(op); return 1; }
w->op = op;
w->pending = &pending;

// NOTE: third arg is the OUT buffer where libsmb2 writes results
if (smb2_stat_async(ctx, rel, &op->st, stat_cb, w) != 0) {
fprintf(stderr, "smb2_stat_async(%s): %s\n", rel, smb2_get_error(ctx));
free(w); free(op);
continue;
}
pending++;

// stash pointer to 'op' for later printing (re-use argv slot)
*((struct op **)&argv[optind + i]) = op;
}

if (service_loop(ctx, &pending) < 0) return 1;

// Results
for (int i = 0; i < nfiles; i++) {
struct op *op = *((struct op **)&argv[optind + i]);
if (!op) continue;
if (op->status != 0) {
fprintf(stderr, "%s : error %d\n", op->path, op->status);
} else {
printf("%-40s size=%llu mtime=%lld isdir=%d\n",
op->path,
(unsigned long long)op->st.smb2_size,
(long long)op->st.smb2_mtime,
(op->st.smb2_type == SMB2_TYPE_DIRECTORY));
}
free(op);
}

smb2_destroy_url(url);
smb2_destroy_context(ctx);
return 0;
}
7 changes: 6 additions & 1 deletion tests/prog_ls.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,12 @@ void *calloc(size_t nelem, size_t size)
static void *(*real_calloc)(size_t, size_t) = NULL;

if (real_calloc == NULL) {
real_calloc = dlsym(RTLD_NEXT, "calloc");
static int done_once = 0;
if (done_once) {
return NULL;
}
done_once = 1;
real_calloc = dlsym(RTLD_NEXT, "calloc");
}

call_idx++;
Expand Down
25 changes: 25 additions & 0 deletions tests/test_0400_overdrawn_0202.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

. ./functions.sh

echo "test credits with SMB2 v2.0.2"

echo -n "Discovering file on root of share ... "
FILENAME=$(./prog_ls "${TESTURL}" | head -2 | tail -1 | cut -f1 -d" ")
echo "${FILENAME}"

echo -n "Testing metastat on root of share ... "
pids=()
for jj in $(seq 10)
do
for ii in $(seq 2000)
do
echo "$FILENAME"
done | xargs ./metastat-0202-censored "${TESTURL}" > /dev/null &
pids[$jj]=$!
done
for pid in ${pids[*]}
do
wait $pid || { wait; failure; }
done
success
Loading