1/*
2   Unix SMB/CIFS implementation.
3   SMB torture tester - mangling test
4   Copyright (C) Andrew Tridgell 2002
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2 of the License, or
9   (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*/
20
21#include "includes.h"
22
23static TDB_CONTEXT *tdb;
24
25#define NAME_LENGTH 20
26
27static unsigned total, collisions, failures;
28
29static BOOL test_one(struct cli_state *cli, const char *name)
30{
31	int fnum;
32	fstring shortname;
33	fstring name2;
34	NTSTATUS status;
35	TDB_DATA data;
36
37	total++;
38
39	fnum = cli_open(cli, name, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
40	if (fnum == -1) {
41		printf("open of %s failed (%s)\n", name, cli_errstr(cli));
42		return False;
43	}
44
45	if (!cli_close(cli, fnum)) {
46		printf("close of %s failed (%s)\n", name, cli_errstr(cli));
47		return False;
48	}
49
50	/* get the short name */
51	status = cli_qpathinfo_alt_name(cli, name, shortname);
52	if (!NT_STATUS_IS_OK(status)) {
53		printf("query altname of %s failed (%s)\n", name, cli_errstr(cli));
54		return False;
55	}
56
57	fstr_sprintf(name2, "\\mangle_test\\%s", shortname);
58	if (!cli_unlink(cli, name2)) {
59		printf("unlink of %s  (%s) failed (%s)\n",
60		       name2, name, cli_errstr(cli));
61		return False;
62	}
63
64	/* recreate by short name */
65	fnum = cli_open(cli, name2, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
66	if (fnum == -1) {
67		printf("open2 of %s failed (%s)\n", name2, cli_errstr(cli));
68		return False;
69	}
70	if (!cli_close(cli, fnum)) {
71		printf("close of %s failed (%s)\n", name, cli_errstr(cli));
72		return False;
73	}
74
75	/* and unlink by long name */
76	if (!cli_unlink(cli, name)) {
77		printf("unlink2 of %s  (%s) failed (%s)\n",
78		       name, name2, cli_errstr(cli));
79		failures++;
80		cli_unlink(cli, name2);
81		return True;
82	}
83
84	/* see if the short name is already in the tdb */
85	data = tdb_fetch_bystring(tdb, shortname);
86	if (data.dptr) {
87		/* maybe its a duplicate long name? */
88		if (!strequal(name, data.dptr)) {
89			/* we have a collision */
90			collisions++;
91			printf("Collision between %s and %s   ->  %s "
92				" (coll/tot: %u/%u)\n",
93				name, data.dptr, shortname, collisions, total);
94		}
95		free(data.dptr);
96	} else {
97		TDB_DATA namedata;
98		/* store it for later */
99		namedata.dptr = name;
100		namedata.dsize = strlen(name)+1;
101		tdb_store_bystring(tdb, shortname, namedata, TDB_REPLACE);
102	}
103
104	return True;
105}
106
107
108static void gen_name(char *name)
109{
110	const char *chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._-$~... ";
111	unsigned max_idx = strlen(chars);
112	unsigned len;
113	int i;
114	char *p;
115
116	fstrcpy(name, "\\mangle_test\\");
117	p = name + strlen(name);
118
119	len = 1 + random() % NAME_LENGTH;
120
121	for (i=0;i<len;i++) {
122		p[i] = chars[random() % max_idx];
123	}
124
125	p[i] = 0;
126
127	if (strcmp(p, ".") == 0 || strcmp(p, "..") == 0) {
128		p[0] = '_';
129	}
130
131	/* have a high probability of a common lead char */
132	if (random() % 2 == 0) {
133		p[0] = 'A';
134	}
135
136	/* and a medium probability of a common lead string */
137	if (random() % 10 == 0) {
138		if (strlen(p) <= 5) {
139			fstrcpy(p, "ABCDE");
140		} else {
141			/* try not to kill off the null termination */
142			memcpy(p, "ABCDE", 5);
143		}
144	}
145
146	/* and a high probability of a good extension length */
147	if (random() % 2 == 0) {
148		char *s = strrchr(p, '.');
149		if (s) {
150			s[4] = 0;
151		}
152	}
153
154	/* ..... and a 100% proability of a file not ending in "." */
155	if (p[strlen(p)-1] == '.')
156		p[strlen(p)-1] = '_';
157}
158
159
160BOOL torture_mangle(int dummy)
161{
162	extern int torture_numops;
163	static struct cli_state *cli;
164	int i;
165	BOOL ret = True;
166
167	printf("starting mangle test\n");
168
169	if (!torture_open_connection(&cli)) {
170		return False;
171	}
172
173	/* we will use an internal tdb to store the names we have used */
174	tdb = tdb_open(NULL, 100000, TDB_INTERNAL, 0, 0);
175	if (!tdb) {
176		printf("ERROR: Failed to open tdb\n");
177		return False;
178	}
179
180	cli_unlink(cli, "\\mangle_test\\*");
181	cli_rmdir(cli, "\\mangle_test");
182
183	if (!cli_mkdir(cli, "\\mangle_test")) {
184		printf("ERROR: Failed to make directory\n");
185		return False;
186	}
187
188	for (i=0;i<torture_numops;i++) {
189		fstring name;
190		ZERO_STRUCT(name);
191
192		gen_name(name);
193
194		if (!test_one(cli, name)) {
195			ret = False;
196			break;
197		}
198		if (total && total % 100 == 0) {
199			printf("collisions %u/%u  - %.2f%%   (%u failures)\r",
200			       collisions, total, (100.0*collisions) / total, failures);
201		}
202	}
203
204	cli_unlink(cli, "\\mangle_test\\*");
205	if (!cli_rmdir(cli, "\\mangle_test")) {
206		printf("ERROR: Failed to remove directory\n");
207		return False;
208	}
209
210	printf("\nTotal collisions %u/%u  - %.2f%%   (%u failures)\n",
211	       collisions, total, (100.0*collisions) / total, failures);
212
213	torture_close_connection(cli);
214
215	printf("mangle test finished\n");
216	return (ret && (failures == 0));
217}
218