#include "libcli/smb2/smb2_calls.h"
#include "../libcli/smb/smbXcli_base.h"
#include "torture/torture.h"
+#include "torture/util.h"
#include "torture/smb2/proto.h"
-#include "../libcli/smb/smbXcli_base.h"
-#include "lib/util/time_basic.h"
+#include "../lib/util/time_basic.h"
#define CHECK_VAL(v, correct) do { \
if ((v) != (correct)) { \
} \
} while (0)
-static bool test_delay_writetime(struct torture_context *tctx,
- double used_delay,
- double normal_delay,
- const char *description,
- bool (*get_basic_info_cb)(void *private_data,
- union smb_fileinfo *finfo),
- bool (*write_data_cb)(void *private_data),
- void *private_data)
-{
- union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfoT;
- struct timeval before_write;
- struct timeval after_write;
- struct timeval before_last_write;
- struct timeval after_last_write;
- struct timeval start;
- struct timeval end;
- double sec = used_delay / normal_delay;
- double msec = 1000 * sec;
- double nsec = 1000 * msec;
- double expected_delay = used_delay / nsec;
- double min_delay = expected_delay * 0.01;
- double max_delay = 5 * expected_delay;
- bool ret = true;
- bool ok;
-
- torture_comment(tctx, "START: %s\n", description);
-
- /* get the initial times */
- ok = get_basic_info_cb(private_data, &finfo0);
- torture_assert(tctx, ok, "get_basic_info_cb: finfo0");
-
- /*
- * Make sure the time doesn't change during the next 5 seconds
- */
- start = timeval_current();
- end = timeval_add(&start, max_delay * 1.25, 0);
- while (!timeval_expired(&end)) {
- smb_msleep(1 * msec);
- torture_comment(tctx, "Check for no change\n");
- ok = get_basic_info_cb(private_data, &finfoT);
- torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
- COMPARE_ALL_TIMES_EQUAL(finfoT, finfo0);
- }
-
- ok = get_basic_info_cb(private_data, &finfoT);
- torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
- COMPARE_WRITE_TIME_EQUAL(finfoT, finfo0);
-
- torture_comment(tctx, "Do a write on the file handle\n");
- before_write = timeval_current();
- ok = write_data_cb(private_data);
- after_write = timeval_current();
- torture_assert(tctx, ok, "write_data_cb");
-
- start = timeval_current();
- end = timeval_add(&start, max_delay * 2, 0);
- while (!timeval_expired(&end)) {
- struct timeval before_get;
- struct timeval after_get;
-
- torture_comment(tctx, "Wait for change\n");
- before_get = timeval_current();
- ok = get_basic_info_cb(private_data, &finfoT);
- after_get = timeval_current();
- torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
-
- if (finfoT.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
- double delayS = timeval_elapsed2(&after_write, &before_get);
- double delayL = timeval_elapsed2(&before_write, &after_get);
-
- torture_comment(tctx, "Server updated write_time after %.2f/%.2f seconds "
- "(min delay == %.2f, max delay == %.2f)\n",
- delayS, delayL, min_delay, max_delay);
- torture_assert(tctx, (delayL >= min_delay),
- "Server updated write_time to early!");
- torture_assert(tctx, (delayS <= max_delay),
- "Server didn't update write_time!");
-
- COMPARE_TIMES_AFTER_WRITE(finfoT, finfo0);
- break;
- }
-
- COMPARE_ALL_TIMES_EQUAL(finfoT, finfo0);
- smb_msleep(0.01 * msec);
- }
-
- ok = get_basic_info_cb(private_data, &finfo1);
- torture_assert(tctx, ok, "get_basic_info_cb: finfo1");
- COMPARE_TIMES_AFTER_WRITE(finfo1, finfo0);
-
- /*
- * Make sure the time doesn't change during the next 5 seconds
- */
- start = timeval_current();
- end = timeval_add(&start, max_delay * 1.25, 0);
- while (!timeval_expired(&end)) {
- smb_msleep(1 * msec);
- torture_comment(tctx, "Check for no additional change\n");
- ok = get_basic_info_cb(private_data, &finfoT);
- torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
- COMPARE_ALL_TIMES_EQUAL(finfoT, finfo1);
- }
-
- ok = get_basic_info_cb(private_data, &finfoT);
- torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
- COMPARE_ALL_TIMES_EQUAL(finfoT, finfo1);
-
- torture_comment(tctx, "Do a write on the file handle\n");
- before_write = timeval_current();
- ok = write_data_cb(private_data);
- after_write = timeval_current();
- torture_assert(tctx, ok, "write_data_cb");
-
- ZERO_STRUCT(finfo2);
- before_last_write = before_write;
- after_last_write = after_write;
- start = timeval_current();
- end = timeval_add(&start, max_delay * 2, 0);
- while (!timeval_expired(&end)) {
- struct timeval before_get;
- struct timeval after_get;
-
- smb_msleep(0.01 * msec);
-
- torture_comment(tctx, "Wait for change\n");
- before_get = timeval_current();
- ok = get_basic_info_cb(private_data, &finfoT);
- after_get = timeval_current();
- torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
-
- if (finfoT.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
- double delayS = timeval_elapsed2(&after_write, &before_get);
- double delayL = timeval_elapsed2(&before_write, &after_get);
-
- torture_comment(tctx, "Server updated write_time after %.2f/%.2f seconds "
- "(min delay == %.2f, max delay == %.2f)\n",
- delayS, delayL, min_delay, max_delay);
- torture_assert(tctx, (delayL >= min_delay),
- "Server updated write_time to early!");
- torture_assert(tctx, (delayS <= max_delay),
- "Server didn't update write_time!");
-
- COMPARE_TIMES_AFTER_WRITE(finfoT, finfo1);
- before_write = before_last_write;
- after_write = after_last_write;
- finfo2 = finfoT;
- break;
- }
-
- COMPARE_ALL_TIMES_EQUAL(finfoT, finfo1);
-
- torture_comment(tctx, "Write while waiting\n");
- before_last_write = timeval_current();
- ok = write_data_cb(private_data);
- after_last_write = timeval_current();
- torture_assert(tctx, ok, "write_data_cb");
- }
-
- // We may get one additional change...
-
- ok = get_basic_info_cb(private_data, &finfo3);
- torture_assert(tctx, ok, "get_basic_info_cb: finfo3");
- COMPARE_ALL_TIMES_EQUAL(finfo3, finfo2);
-
- /*
- * Make sure the time doesn't change during the next 5 seconds
- */
- start = timeval_current();
- end = timeval_add(&start, max_delay * 1.25, 0);
- while (!timeval_expired(&end)) {
- smb_msleep(1 * msec);
- torture_comment(tctx, "Check for no additional change\n");
- ok = get_basic_info_cb(private_data, &finfoT);
- torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
- COMPARE_ALL_TIMES_EQUAL(finfoT, finfo3);
- }
-
- ok = get_basic_info_cb(private_data, &finfoT);
- torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
- COMPARE_ALL_TIMES_EQUAL(finfoT, finfo3);
-
-done:
- return ret;
-}
-
struct test_durable_open_delaywrite1_state {
struct torture_context *tctx;
struct smb2_tree *tree1;
state.h1 = h1;
state.h2 = h2;
- ok = test_delay_writetime(tctx, used_delay, normal_delay,
+ ok = test_delay_writetime1(tctx, used_delay, normal_delay,
"run1",
test_durable_open_delaywrite1_get_info,
test_durable_open_delaywrite1_write_data,
&state);
- torture_assert(tctx, ok, "test_delay_writetime(1)");
- ok = test_delay_writetime(tctx, used_delay, normal_delay,
+ torture_assert(tctx, ok, "test_delay_writetime1(1)");
+ ok = test_delay_writetime1(tctx, used_delay, normal_delay,
"run2",
test_durable_open_delaywrite1_get_info,
test_durable_open_delaywrite1_write_data,
&state);
- torture_assert(tctx, ok, "test_delay_writetime(2)");
+ torture_assert(tctx, ok, "test_delay_writetime2(2)");
GET_INFO_BOTH(c1finfo1, c2finfo1);
COMPARE_TIMES_AFTER_WRITE(c1finfo1, c1finfo0);
return ret;
}
-
struct torture_suite *torture_smb2_durable_open_init(TALLOC_CTX *ctx)
{
struct torture_suite *suite =
--- /dev/null
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for delayed write time updates
+
+ Copyright (C) Stefan Metzmacher 2018
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/time_basic.h"
+#include "../libcli/smb/smb_common.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "libcli/raw/libcliraw.h"
+#include "torture/torture.h"
+#include "torture/util.h"
+
+#define COMPARE_TIME_CMP(given, gelem, correct, celem, cmp) do { \
+ const uint64_t _r = 10*1000*1000; \
+ NTTIME _g = (given).basic_info.out.gelem; \
+ NTTIME _gr = (_g / _r) * _r; \
+ NTTIME _c = (correct).basic_info.out.celem; \
+ NTTIME _cr = (_c / _r) * _r; \
+ bool _strict = torture_setting_bool(tctx, "strict mode", false); \
+ const char *_err = NULL; \
+ if (_strict && (_g cmp _c)) { \
+ _err = "strict"; \
+ } else if ((_g cmp _c) && (_gr cmp _cr)) { \
+ /* handle filesystem without high resolution timestamps */ \
+ _err = "rounded"; \
+ } \
+ if (_err != NULL) { \
+ struct timeval _gtv; \
+ struct timeval _ctv; \
+ struct timeval_buf _gtvb; \
+ struct timeval_buf _ctvb; \
+ nttime_to_timeval(&_gtv, _g); \
+ nttime_to_timeval(&_ctv, _c); \
+ torture_result(tctx, TORTURE_FAIL, \
+ __location__": %s wrong (%s.%s)%s %s (%s.%s)%s", \
+ _err, \
+ #given, #gelem, \
+ timeval_str_buf(&_gtv, false, true, &_gtvb), \
+ #cmp, \
+ #correct, #celem, \
+ timeval_str_buf(&_ctv, false, true, &_ctvb)); \
+ ret = false; \
+ goto done; \
+ } \
+} while (0)
+#define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
+ COMPARE_TIME_CMP(given, write_time, correct, write_time, cmp); \
+} while (0)
+#define COMPARE_WRITE_TIME_EQUAL(given,correct) \
+ COMPARE_WRITE_TIME_CMP(given,correct,!=)
+#define COMPARE_WRITE_TIME_GREATER(given,correct) \
+ COMPARE_WRITE_TIME_CMP(given,correct,<=)
+
+#define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
+ COMPARE_TIME_CMP(given, access_time, correct, access_time, cmp); \
+} while (0)
+#define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
+ COMPARE_ACCESS_TIME_CMP(given,correct,!=)
+#define COMPARE_ACCESS_TIME_GREATER(given,correct) \
+ COMPARE_ACCESS_TIME_CMP(given,correct,<=)
+
+#define COMPARE_CHANGE_TIME_CMP(given, correct, cmp) do { \
+ COMPARE_TIME_CMP(given, change_time, correct, change_time, cmp); \
+} while (0)
+#define COMPARE_CHANGE_TIME_EQUAL(given,correct) \
+ COMPARE_CHANGE_TIME_CMP(given,correct,!=)
+#define COMPARE_CHANGE_TIME_GREATER(given,correct) \
+ COMPARE_CHANGE_TIME_CMP(given,correct,<=)
+
+#define COMPARE_CREATE_TIME_CMP(given, correct, cmp) do { \
+ COMPARE_TIME_CMP(given, create_time, correct, create_time, cmp); \
+} while (0)
+#define COMPARE_CREATE_TIME_EQUAL(given,correct) \
+ COMPARE_CREATE_TIME_CMP(given,correct,!=)
+
+#define COMPARE_ALL_TIMES_EQUAL(given,correct) do { \
+ COMPARE_WRITE_TIME_EQUAL(given,correct); \
+ COMPARE_CHANGE_TIME_EQUAL(given,correct); \
+ COMPARE_ACCESS_TIME_EQUAL(given,correct); \
+ COMPARE_CREATE_TIME_EQUAL(given,correct); \
+} while (0)
+
+#define COMPARE_TIMES_AFTER_WRITE(given,correct) do { \
+ COMPARE_WRITE_TIME_GREATER(given,correct); \
+ COMPARE_CHANGE_TIME_GREATER(given,correct); \
+ COMPARE_ACCESS_TIME_EQUAL(given,correct); \
+ COMPARE_CREATE_TIME_EQUAL(given,correct); \
+ COMPARE_TIME_CMP(given, change_time, given, write_time, !=); \
+} while (0)
+
+#define COMPARE_TIMES_AFTER_CLOSE(given,correct) do { \
+ COMPARE_WRITE_TIME_GREATER(given,correct); \
+ COMPARE_CHANGE_TIME_GREATER(given,correct); \
+ COMPARE_ACCESS_TIME_GREATER(given,correct); \
+ COMPARE_CREATE_TIME_EQUAL(given,correct); \
+ COMPARE_TIME_CMP(given, change_time, given, write_time, !=); \
+ COMPARE_TIME_CMP(given, access_time, given, write_time, !=); \
+} while (0)
+
+bool test_delay_writetime1(struct torture_context *tctx,
+ double used_delay,
+ double normal_delay,
+ const char *description,
+ bool (*get_basic_info_cb)(void *private_data,
+ union smb_fileinfo *finfo),
+ bool (*write_data_cb)(void *private_data),
+ void *private_data)
+{
+ union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfoT;
+ struct timeval before_write;
+ struct timeval after_write;
+ struct timeval before_last_write;
+ struct timeval after_last_write;
+ struct timeval start;
+ struct timeval end;
+ double sec = used_delay / normal_delay;
+ double msec = 1000 * sec;
+ double nsec = 1000 * msec;
+ double expected_delay = used_delay / nsec;
+ double min_delay = expected_delay * 0.01;
+ double max_delay = 5 * expected_delay;
+ bool ret = true;
+ bool ok;
+
+ torture_comment(tctx, "START: %s\n", description);
+
+ /* get the initial times */
+ ok = get_basic_info_cb(private_data, &finfo0);
+ torture_assert(tctx, ok, "get_basic_info_cb: finfo0");
+
+ /*
+ * Make sure the time doesn't change during the next 5 seconds
+ */
+ start = timeval_current();
+ end = timeval_add(&start, max_delay * 1.25, 0);
+ while (!timeval_expired(&end)) {
+ smb_msleep(1 * msec);
+ torture_comment(tctx, "Check for no change\n");
+ ok = get_basic_info_cb(private_data, &finfoT);
+ torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
+ COMPARE_ALL_TIMES_EQUAL(finfoT, finfo0);
+ }
+
+ ok = get_basic_info_cb(private_data, &finfoT);
+ torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
+ COMPARE_WRITE_TIME_EQUAL(finfoT, finfo0);
+
+ torture_comment(tctx, "Do a write on the file handle\n");
+ before_write = timeval_current();
+ ok = write_data_cb(private_data);
+ after_write = timeval_current();
+ torture_assert(tctx, ok, "write_data_cb");
+
+ start = timeval_current();
+ end = timeval_add(&start, max_delay * 2, 0);
+ while (!timeval_expired(&end)) {
+ struct timeval before_get;
+ struct timeval after_get;
+
+ torture_comment(tctx, "Wait for change\n");
+ before_get = timeval_current();
+ ok = get_basic_info_cb(private_data, &finfoT);
+ after_get = timeval_current();
+ torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
+
+ if (finfoT.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
+ double delayS = timeval_elapsed2(&after_write, &before_get);
+ double delayL = timeval_elapsed2(&before_write, &after_get);
+
+ torture_comment(tctx, "Server updated write_time after %.2f/%.2f seconds "
+ "(min delay == %.2f, max delay == %.2f)\n",
+ delayS, delayL, min_delay, max_delay);
+ torture_assert(tctx, (delayL >= min_delay),
+ "Server updated write_time to early!");
+ torture_assert(tctx, (delayS <= max_delay),
+ "Server didn't update write_time!");
+
+ COMPARE_TIMES_AFTER_WRITE(finfoT, finfo0);
+ break;
+ }
+
+ COMPARE_ALL_TIMES_EQUAL(finfoT, finfo0);
+ smb_msleep(0.01 * msec);
+ }
+
+ ok = get_basic_info_cb(private_data, &finfo1);
+ torture_assert(tctx, ok, "get_basic_info_cb: finfo1");
+ COMPARE_TIMES_AFTER_WRITE(finfo1, finfo0);
+
+ /*
+ * Make sure the time doesn't change during the next 5 seconds
+ */
+ start = timeval_current();
+ end = timeval_add(&start, max_delay * 1.25, 0);
+ while (!timeval_expired(&end)) {
+ smb_msleep(1 * msec);
+ torture_comment(tctx, "Check for no additional change\n");
+ ok = get_basic_info_cb(private_data, &finfoT);
+ torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
+ COMPARE_ALL_TIMES_EQUAL(finfoT, finfo1);
+ }
+
+ ok = get_basic_info_cb(private_data, &finfoT);
+ torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
+ COMPARE_ALL_TIMES_EQUAL(finfoT, finfo1);
+
+ torture_comment(tctx, "Do a write on the file handle\n");
+ before_write = timeval_current();
+ ok = write_data_cb(private_data);
+ after_write = timeval_current();
+ torture_assert(tctx, ok, "write_data_cb");
+
+ ZERO_STRUCT(finfo2);
+ before_last_write = before_write;
+ after_last_write = after_write;
+ start = timeval_current();
+ end = timeval_add(&start, max_delay * 2, 0);
+ while (!timeval_expired(&end)) {
+ struct timeval before_get;
+ struct timeval after_get;
+
+ smb_msleep(0.01 * msec);
+
+ torture_comment(tctx, "Wait for change\n");
+ before_get = timeval_current();
+ ok = get_basic_info_cb(private_data, &finfoT);
+ after_get = timeval_current();
+ torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
+
+ if (finfoT.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
+ double delayS = timeval_elapsed2(&after_write, &before_get);
+ double delayL = timeval_elapsed2(&before_write, &after_get);
+
+ torture_comment(tctx, "Server updated write_time after %.2f/%.2f seconds "
+ "(min delay == %.2f, max delay == %.2f)\n",
+ delayS, delayL, min_delay, max_delay);
+ torture_assert(tctx, (delayL >= min_delay),
+ "Server updated write_time to early!");
+ torture_assert(tctx, (delayS <= max_delay),
+ "Server didn't update write_time!");
+
+ COMPARE_TIMES_AFTER_WRITE(finfoT, finfo1);
+ before_write = before_last_write;
+ after_write = after_last_write;
+ finfo2 = finfoT;
+ break;
+ }
+
+ COMPARE_ALL_TIMES_EQUAL(finfoT, finfo1);
+
+ torture_comment(tctx, "Write while waiting\n");
+ before_last_write = timeval_current();
+ ok = write_data_cb(private_data);
+ after_last_write = timeval_current();
+ torture_assert(tctx, ok, "write_data_cb");
+ }
+
+ // We may get one additional change...
+
+ ok = get_basic_info_cb(private_data, &finfo3);
+ torture_assert(tctx, ok, "get_basic_info_cb: finfo3");
+ COMPARE_ALL_TIMES_EQUAL(finfo3, finfo2);
+
+ /*
+ * Make sure the time doesn't change during the next 5 seconds
+ */
+ start = timeval_current();
+ end = timeval_add(&start, max_delay * 1.25, 0);
+ while (!timeval_expired(&end)) {
+ smb_msleep(1 * msec);
+ torture_comment(tctx, "Check for no additional change\n");
+ ok = get_basic_info_cb(private_data, &finfoT);
+ torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
+ COMPARE_ALL_TIMES_EQUAL(finfoT, finfo3);
+ }
+
+ ok = get_basic_info_cb(private_data, &finfoT);
+ torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
+ COMPARE_ALL_TIMES_EQUAL(finfoT, finfo3);
+
+done:
+ return ret;
+}