Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

467 lines
16 KiB

  1. /*
  2. * remote checksum server
  3. *
  4. * scan.c file scanning and checksum module
  5. *
  6. * server creates a named pipe and waits for connections. a client connects,
  7. * and sends request packets to the server. One such request packet is
  8. * the SSREQ_SCAN request: we are given a pathname, and we are to checksum
  9. * every file below that point in the directory tree. We pass each
  10. * filename and checksum back individually in a separate response packet,
  11. * and finally a response packet saying that there are no more files.
  12. *
  13. * We sort everything into case-insensitive alphabetic order. In a given
  14. * directory, we pass out a sorted list of the files before we process
  15. * the subdirectories.
  16. *
  17. * Geraint, July 92
  18. */
  19. #include <windows.h>
  20. #include <stdio.h>
  21. #include <string.h>
  22. #include <gutils.h>
  23. #include "sumserve.h"
  24. #include "errlog.h"
  25. #include "server.h"
  26. /* module-internal type defns ---------------------------------------------*/
  27. /* sorted list of file names in current dir is in a chained list of these */
  28. typedef struct fnamelist {
  29. char szFile[MAX_PATH];
  30. struct fnamelist * next;
  31. } FNAMELIST; /* and PFNAMELIST already declared in server.h */
  32. /* forward declaration of functions ---------------------------------------*/
  33. PFNAMELIST ss_addtolist(PFNAMELIST head, PSTR name);
  34. BOOL ss_processfile(HANDLE hpipe, long lVersion, LPSTR pAbsName, LPSTR pRelName
  35. , BOOL bChecksum);
  36. BOOL ss_processdir( HANDLE hpipe, long lVersion, LPSTR pAbsName, LPSTR pRelName
  37. , BOOL bChecksum, BOOL fDeep);
  38. /*--- externally called functions ----------------------------------------*/
  39. /* ss_scan
  40. *
  41. * called from ss_handleclient on receipt of a SCAN request. scan the
  42. * directory passed in, and pass the files found back to the named pipe
  43. * one at a time. filenames returned should be relative to the
  44. * starting point (pRoot) and not absolute.
  45. *
  46. * returns TRUE if all ok; FALSE if an error occured and the connection
  47. * is lost.
  48. */
  49. BOOL
  50. ss_scan(HANDLE hpipe, LPSTR pRoot, LONG lVersion, BOOL bChecksum, BOOL fDeep)
  51. {
  52. DWORD dwAttrib;
  53. LPSTR file;
  54. char buffer[MAX_PATH];
  55. /* check whether this is a directory or a file */
  56. dwAttrib = GetFileAttributes(pRoot);
  57. if (dwAttrib == -1) {
  58. /* file does not exist or is not visible */
  59. if (GetLastError() == ERROR_INVALID_PASSWORD) {
  60. dprintf1(("password error\n"));
  61. Log_Write(hlogErrors, "password error on %s", pRoot);
  62. if (!ss_sendnewresp( hpipe, lVersion, SSRESP_BADPASS
  63. , 0, 0, 0, 0, NULL)) {
  64. return(FALSE);
  65. }
  66. } else {
  67. dprintf1(("file access error %d\n", GetLastError()));
  68. Log_Write(hlogErrors, "file error %d for %s", GetLastError(), pRoot);
  69. if (!ss_sendnewresp( hpipe, lVersion, SSRESP_ERROR
  70. , GetLastError(), 0, 0, 0, pRoot)) {
  71. return(FALSE);
  72. }
  73. if (!ss_sendnewresp( hpipe, lVersion, SSRESP_END
  74. , 0, 0, 0, 0, NULL)) {
  75. return(FALSE);
  76. }
  77. }
  78. return TRUE;
  79. }
  80. if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) {
  81. /* it is a directory - read all entries and
  82. * then process the entries */
  83. /*
  84. * create a "." directory and scan that
  85. */
  86. if (!ss_sendnewresp( hpipe, lVersion, SSRESP_DIR
  87. , 0 , 0, 0, 0, ".")) {
  88. return(FALSE);
  89. }
  90. if (!ss_processdir(hpipe, lVersion, pRoot, ".", bChecksum, fDeep) ) {
  91. return(FALSE);
  92. }
  93. } else {
  94. /* pRoot is a file. we should just return the
  95. * checksum and name, and then end.
  96. *
  97. * note that we should send a filename relative
  98. * to pRoot for this file. Since pRoot is this file,
  99. * it is not clear what we should send as the file name.
  100. * in this case we split off the last component of the
  101. * file name and return that
  102. */
  103. if ( (file = strrchr(pRoot, '\\')) == NULL) {
  104. /* there are no slashes in pRoot - so
  105. * there is only one component: use that
  106. */
  107. file = pRoot;
  108. } else {
  109. /* we found a / - skip it so we point at the
  110. * final elem
  111. */
  112. file++;
  113. }
  114. /*
  115. * make a copy of the filename, prepended with .\ so that
  116. * it matches the normal format
  117. */
  118. lstrcpy(buffer, ".\\");
  119. lstrcat(buffer, file);
  120. if (!ss_processfile(hpipe, lVersion, pRoot, buffer, bChecksum) ) {
  121. return(FALSE);
  122. }
  123. }
  124. return(ss_sendnewresp( hpipe, lVersion, SSRESP_END
  125. , 0, 0, 0, 0, NULL));
  126. } /* ss_scan */
  127. /* module-internal functions --------------------------------------------*/
  128. /* read all entries in a directory, and create a sorted list of files
  129. * in that directory, and a sorted list of subdirs.
  130. *
  131. * for each file found, call ss_process_file to checksum and report on
  132. * the file.
  133. * for each subdir, report the name of the new dir and then
  134. * recursively call this function to scan it.
  135. *
  136. * We have two names for the dir- the absolute name (which we use to
  137. * scan it) and the name relative to the pRoot starting point - which
  138. * pass on to the client
  139. *
  140. * return TRUE if all ok, or FALSE if the connection has been lost
  141. */
  142. BOOL
  143. ss_processdir( HANDLE hpipe,
  144. long lVersion,
  145. LPSTR pAbsName, /* absolute name of dir (to open) */
  146. LPSTR pRelName, /* relative name of dir (to report) */
  147. BOOL bChecksum, /* TRUE iff checksums are wanted */
  148. BOOL fDeep /* TRUE iff subdirs to be included */
  149. )
  150. {
  151. PFNAMELIST pfiles = NULL;
  152. PFNAMELIST pdirs = NULL;
  153. PFNAMELIST pnext;
  154. HANDLE hFind;
  155. WIN32_FIND_DATA finddata;
  156. BOOL bMore;
  157. char szNewAbs[MAX_PATH], szNewRel[MAX_PATH];
  158. /* initiate a search of the directory - append
  159. * *.* to the directory name
  160. */
  161. lstrcpy(szNewAbs, pAbsName);
  162. lstrcat(szNewAbs, "\\*.*");
  163. hFind = FindFirstFile(szNewAbs, &finddata);
  164. if (hFind == INVALID_HANDLE_VALUE) {
  165. bMore = FALSE;
  166. } else {
  167. bMore = TRUE;
  168. }
  169. /* loop reading all entries in the directory */
  170. while (bMore) {
  171. /* was it a directory or a file ? */
  172. if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  173. /* ignore . and .. */
  174. if ((strcmp(finddata.cFileName, ".") != 0) &&
  175. (strcmp(finddata.cFileName, "..") != 0)) {
  176. /* insert in sorted list of dir names */
  177. pdirs = ss_addtolist(pdirs, finddata.cFileName);
  178. }
  179. } else {
  180. /* insert in sorted list of file names */
  181. pfiles = ss_addtolist(pfiles, finddata.cFileName);
  182. }
  183. /* get next entry in directory if there are any */
  184. bMore = FindNextFile(hFind, &finddata);
  185. }
  186. FindClose(hFind);
  187. /* we have now built the sorted lists.
  188. * go through the file list first and process each entry */
  189. for (pnext = pfiles; pnext != NULL; ) {
  190. /* build a new abs and relative name for this file */
  191. lstrcpy(szNewAbs, pAbsName);
  192. lstrcat(szNewAbs, "\\");
  193. lstrcat(szNewAbs, pnext->szFile);
  194. lstrcpy(szNewRel, pRelName);
  195. lstrcat(szNewRel, "\\");
  196. lstrcat(szNewRel, pnext->szFile);
  197. /* checksum the file and send response */
  198. if (!ss_processfile(hpipe, lVersion, szNewAbs, szNewRel, bChecksum)) {
  199. return(FALSE);
  200. }
  201. /* free up the list entry */
  202. pfiles = pnext->next;
  203. LocalUnlock(LocalHandle( (PSTR) pnext));
  204. LocalFree(LocalHandle( (PSTR) pnext));
  205. pnext = pfiles;
  206. }
  207. if (!fDeep) return TRUE;
  208. /* loop through the subdirs and recursively scan those */
  209. for (pnext = pdirs; pnext != NULL; ) {
  210. /* build a new abs and relative name for this dir */
  211. lstrcpy(szNewAbs, pAbsName);
  212. lstrcat(szNewAbs, "\\");
  213. lstrcat(szNewAbs, pnext->szFile);
  214. lstrcpy(szNewRel, pRelName);
  215. lstrcat(szNewRel, "\\");
  216. lstrcat(szNewRel, pnext->szFile);
  217. /* send the name of the new dir to the client */
  218. if (!ss_sendnewresp( hpipe, lVersion, SSRESP_DIR
  219. , 0, 0, 0, 0, szNewRel)) {
  220. return(FALSE);
  221. }
  222. if (!ss_processdir(hpipe, lVersion, szNewAbs, szNewRel, bChecksum, TRUE) ) {
  223. return(FALSE);
  224. }
  225. /* free up the list entry */
  226. pdirs = pnext->next;
  227. LocalUnlock(LocalHandle( (PSTR) pnext));
  228. LocalFree(LocalHandle( (PSTR) pnext));
  229. pnext = pdirs;
  230. }
  231. return(TRUE);
  232. } /* ss_processdir */
  233. /* checksum a file and send the response to the client.
  234. *
  235. * return FALSE if the connection failed, TRUE otherwise
  236. */
  237. BOOL
  238. ss_processfile( HANDLE hpipe,
  239. long lVersion,
  240. LPSTR pAbsName, /* absolute name of file (to open) */
  241. LPSTR pRelName, /* relative name (to report) */
  242. BOOL bChecksum
  243. )
  244. {
  245. HANDLE hfile; /* file handle from CreateFile() */
  246. DWORD sum, size;
  247. FILETIME ft;
  248. hfile = CreateFile(pAbsName, GENERIC_READ, FILE_SHARE_READ,
  249. NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  250. if (hfile == INVALID_HANDLE_VALUE) {
  251. /* We can't read the file, but we can still probably report some
  252. properties because FindFirst / FindNext must have found it
  253. */
  254. WIN32_FIND_DATA finddata;
  255. HANDLE hFind;
  256. hFind = FindFirstFile(pAbsName, &finddata);
  257. if (hFind!=INVALID_HANDLE_VALUE){
  258. FindClose(hFind);
  259. Log_Write(hlogErrors, "Cannot read file %s", pAbsName);
  260. /* report that we could not read the file */
  261. return(ss_sendnewresp( hpipe, lVersion, SSRESP_CANTOPEN
  262. , finddata.nFileSizeLow, 0
  263. , finddata.ftLastWriteTime.dwLowDateTime
  264. , finddata.ftLastWriteTime.dwHighDateTime
  265. , pRelName));
  266. } else {
  267. /* report that this file is cracked */
  268. Log_Write(hlogErrors, "Cannot find file %s", pAbsName);
  269. return(ss_sendnewresp( hpipe, lVersion, SSRESP_ERROR
  270. , GetLastError(), 0, 0, 0, pRelName));
  271. }
  272. } else {
  273. size = GetFileSize(hfile, NULL);
  274. if (!GetFileTime(hfile, NULL, NULL, &ft)) {
  275. ft.dwLowDateTime = 0;
  276. ft.dwHighDateTime = 0;
  277. }
  278. CloseHandle(hfile);
  279. if (bChecksum) {
  280. LONG err;
  281. sum = checksum_file(pAbsName, &err);
  282. if (err!=0) {
  283. return(ss_sendnewresp( hpipe, lVersion, SSRESP_ERROR
  284. , GetLastError(), 0, 0, 0, pRelName));
  285. }
  286. }
  287. else sum = 0; /* no checksum wanted */
  288. return (ss_sendnewresp( hpipe, lVersion, SSRESP_FILE
  289. , size, sum
  290. , ft.dwLowDateTime, ft.dwHighDateTime
  291. , pRelName));
  292. }
  293. }/* ss_processfile */
  294. /* add a file or directory into a sorted single-linked list. alloc memory for
  295. * the new element from LocalAlloc
  296. *
  297. * we sort using utils_CompPath for a case-insensitive canonical sort,
  298. * but to match what goes on in the client world, we actually lower-case
  299. * everything first!
  300. *
  301. * return the new head of the list;
  302. */
  303. PFNAMELIST
  304. ss_addtolist(PFNAMELIST head, PSTR name)
  305. {
  306. PFNAMELIST pnew, prev, pnext;
  307. /* alloc and fill a new entry */
  308. pnew = LocalLock(LocalAlloc(LHND, sizeof (FNAMELIST)));
  309. lstrcpy(pnew->szFile, name);
  310. /* always lower-case the names, or the comparison (utils_comppath)
  311. * will fail. even if we don't do the compare this time, this name
  312. * will be what we are compared against next time round...
  313. */
  314. AnsiLowerBuff(pnew->szFile, strlen(pnew->szFile));
  315. /* is the list empty ? */
  316. if (head == NULL) {
  317. /* yes, so return new head */
  318. return(pnew);
  319. }
  320. /* find place in list */
  321. prev = NULL;
  322. pnext = head;
  323. while ((pnext) && (utils_CompPath(pnext->szFile, pnew->szFile) <= 0)) {
  324. prev = pnext;
  325. pnext = pnext->next;
  326. }
  327. /* place found: we come between *prev and *pnext */
  328. pnew->next = pnext;
  329. if (prev == NULL) {
  330. /* we are new head of list */
  331. return(pnew);
  332. } else {
  333. prev->next = pnew;
  334. /* head of list still the same */
  335. return(head);
  336. }
  337. }
  338. /* UNC handling
  339. *
  340. * client can pass us a SSREQ_UNC: this contains both a password and a server
  341. * name (in the form \\server\share). We make a connection to it here and
  342. * remember the connection so that we can remove it (in ss_cleanconnections)
  343. * when the client session terminates.
  344. *
  345. * We are passed the head of a FNAMELIST in which we should store the connect
  346. * name for later cleanup. We return the new head of this list.
  347. *
  348. * the client will send this request if a unc-style named scan fails
  349. * with the SSRESP_BADPASS error.
  350. */
  351. PFNAMELIST
  352. ss_handleUNC( HANDLE hpipe, long lVersion
  353. , LPSTR password, LPSTR server, PFNAMELIST connects)
  354. {
  355. NETRESOURCE resource;
  356. int errorcode;
  357. resource.lpRemoteName = server;
  358. resource.lpLocalName = NULL;
  359. resource.dwType = RESOURCETYPE_DISK;
  360. resource.lpProvider = NULL;
  361. errorcode = (int)WNetAddConnection2(&resource, password, NULL, 0);
  362. if (errorcode == NO_ERROR) {
  363. /* remember the connection name */
  364. connects = ss_addtolist(connects, server);
  365. /* report success */
  366. ss_sendnewresp( hpipe, lVersion, SSRESP_END
  367. , 0, 0, 0, 0, NULL);
  368. } else {
  369. Log_Write(hlogErrors, "Connect error %d for server %s", GetLastError(), server);
  370. dprintf1(("connect error %d for server %s\n", GetLastError(), server));
  371. /* report error */
  372. ss_sendnewresp( hpipe, lVersion, SSRESP_ERROR
  373. , 0, 0, 0, 0, NULL);
  374. }
  375. return(connects);
  376. } /* ss_handleUNC */
  377. /* disconnect from all the sessions that this client asked us to make */
  378. void
  379. ss_cleanconnections(PFNAMELIST connects)
  380. {
  381. PFNAMELIST server, next;
  382. for (server = connects; server != NULL; ) {
  383. WNetCancelConnection2(server->szFile, 0, 0);
  384. /* free block of memory */
  385. next = server->next;
  386. LocalUnlock(LocalHandle( (PSTR) server));
  387. LocalFree(LocalHandle( (PSTR) server));
  388. server = next;
  389. }
  390. connects = NULL;
  391. } /* ss_cleanconnections */