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 3 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, see <http://www.gnu.org/licenses/>. 18*/ 19 20#include "includes.h" 21#include "torture/torture.h" 22#include "system/filesys.h" 23#include "system/dir.h" 24#include "../tdb/include/tdb.h" 25#include "../lib/util/util_tdb.h" 26#include "libcli/libcli.h" 27#include "torture/util.h" 28 29static TDB_CONTEXT *tdb; 30 31#define NAME_LENGTH 20 32 33static uint_t total, collisions, failures; 34 35static bool test_one(struct torture_context *tctx ,struct smbcli_state *cli, 36 const char *name) 37{ 38 int fnum; 39 const char *shortname; 40 const char *name2; 41 NTSTATUS status; 42 TDB_DATA data; 43 44 total++; 45 46 fnum = smbcli_open(cli->tree, name, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); 47 if (fnum == -1) { 48 printf("open of %s failed (%s)\n", name, smbcli_errstr(cli->tree)); 49 return false; 50 } 51 52 if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, fnum))) { 53 printf("close of %s failed (%s)\n", name, smbcli_errstr(cli->tree)); 54 return false; 55 } 56 57 /* get the short name */ 58 status = smbcli_qpathinfo_alt_name(cli->tree, name, &shortname); 59 if (!NT_STATUS_IS_OK(status)) { 60 printf("query altname of %s failed (%s)\n", name, smbcli_errstr(cli->tree)); 61 return false; 62 } 63 64 name2 = talloc_asprintf(tctx, "\\mangle_test\\%s", shortname); 65 if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, name2))) { 66 printf("unlink of %s (%s) failed (%s)\n", 67 name2, name, smbcli_errstr(cli->tree)); 68 return false; 69 } 70 71 /* recreate by short name */ 72 fnum = smbcli_open(cli->tree, name2, O_RDWR|O_CREAT|O_EXCL, DENY_NONE); 73 if (fnum == -1) { 74 printf("open2 of %s failed (%s)\n", name2, smbcli_errstr(cli->tree)); 75 return false; 76 } 77 if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, fnum))) { 78 printf("close of %s failed (%s)\n", name, smbcli_errstr(cli->tree)); 79 return false; 80 } 81 82 /* and unlink by long name */ 83 if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, name))) { 84 printf("unlink2 of %s (%s) failed (%s)\n", 85 name, name2, smbcli_errstr(cli->tree)); 86 failures++; 87 smbcli_unlink(cli->tree, name2); 88 return true; 89 } 90 91 /* see if the short name is already in the tdb */ 92 data = tdb_fetch_bystring(tdb, shortname); 93 if (data.dptr) { 94 /* maybe its a duplicate long name? */ 95 if (strcasecmp(name, (const char *)data.dptr) != 0) { 96 /* we have a collision */ 97 collisions++; 98 printf("Collision between %s and %s -> %s " 99 " (coll/tot: %u/%u)\n", 100 name, data.dptr, shortname, collisions, total); 101 } 102 free(data.dptr); 103 } else { 104 TDB_DATA namedata; 105 /* store it for later */ 106 namedata.dptr = discard_const_p(uint8_t, name); 107 namedata.dsize = strlen(name)+1; 108 tdb_store_bystring(tdb, shortname, namedata, TDB_REPLACE); 109 } 110 111 return true; 112} 113 114 115static char *gen_name(TALLOC_CTX *mem_ctx) 116{ 117 const char *chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._-$~..."; 118 uint_t max_idx = strlen(chars); 119 uint_t len; 120 int i; 121 char *p; 122 char *name; 123 124 name = talloc_strdup(mem_ctx, "\\mangle_test\\"); 125 126 len = 1 + random() % NAME_LENGTH; 127 128 name = talloc_realloc(mem_ctx, name, char, strlen(name) + len + 6); 129 p = name + strlen(name); 130 131 for (i=0;i<len;i++) { 132 p[i] = chars[random() % max_idx]; 133 } 134 135 p[i] = 0; 136 137 if (ISDOT(p) || ISDOTDOT(p)) { 138 p[0] = '_'; 139 } 140 141 /* have a high probability of a common lead char */ 142 if (random() % 2 == 0) { 143 p[0] = 'A'; 144 } 145 146 /* and a medium probability of a common lead string */ 147 if ((len > 5) && (random() % 10 == 0)) { 148 strncpy(p, "ABCDE", 5); 149 } 150 151 /* and a high probability of a good extension length */ 152 if (random() % 2 == 0) { 153 char *s = strrchr(p, '.'); 154 if (s) { 155 s[4] = 0; 156 } 157 } 158 159 return name; 160} 161 162 163bool torture_mangle(struct torture_context *torture, 164 struct smbcli_state *cli) 165{ 166 extern int torture_numops; 167 int i; 168 169 /* we will use an internal tdb to store the names we have used */ 170 tdb = tdb_open(NULL, 100000, TDB_INTERNAL, 0, 0); 171 if (!tdb) { 172 printf("ERROR: Failed to open tdb\n"); 173 return false; 174 } 175 176 if (!torture_setup_dir(cli, "\\mangle_test")) { 177 return false; 178 } 179 180 for (i=0;i<torture_numops;i++) { 181 char *name; 182 183 name = gen_name(torture); 184 185 if (!test_one(torture, cli, name)) { 186 break; 187 } 188 if (total && total % 100 == 0) { 189 if (torture_setting_bool(torture, "progress", true)) { 190 printf("collisions %u/%u - %.2f%% (%u failures)\r", 191 collisions, total, (100.0*collisions) / total, failures); 192 } 193 } 194 } 195 196 smbcli_unlink(cli->tree, "\\mangle_test\\*"); 197 if (NT_STATUS_IS_ERR(smbcli_rmdir(cli->tree, "\\mangle_test"))) { 198 printf("ERROR: Failed to remove directory\n"); 199 return false; 200 } 201 202 printf("\nTotal collisions %u/%u - %.2f%% (%u failures)\n", 203 collisions, total, (100.0*collisions) / total, failures); 204 205 return (failures == 0); 206} 207