Initial IBM OpenAFS 1.0 tree
[openafs.git] / src / WINNT / afs_setup_utils / forceremove.c
1 /* Copyright (C) 1999 Transarc Corporation - All rights reserved.
2  *
3  */
4
5 #include <afs/param.h>
6 #include <afs/stds.h>
7
8 #include <windows.h>
9 #include <stdlib.h>
10 #include <stddef.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <ctype.h>
14
15 #include <WINNT/afsreg.h>
16
17 #include "forceremove.h"
18 #include "sutil.h"
19
20
21 /* Functions to forcibly remove AFS software without using InstallShield. */
22
23 #define CLIENT34_FME_VALUE  "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\File Manager\\AddOns\\AFS Client FME"
24
25 #define MS_UNINSTALL_KEY  "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
26
27
28 static DWORD
29 ClientSoftwareGet(DWORD *version, char *dir, DWORD dirSize);
30
31 static DWORD
32 ClientServiceDelete(void);
33
34 static DWORD
35 FileForceRemove(const char *filePath);
36
37 static DWORD
38 DirectoryForceRemove(const char *dir);
39
40 static DWORD
41 FolderLocateInTree(const char *dir, const char *folderName, char *buf);
42
43 static DWORD
44 Client34ZapUninstallKeys(void);
45
46
47
48 /* ------------------ exported functions ---------------------- */
49
50
51 /*
52  * Client34Eradicate() -- remove AFS 3.4a client, if extant.
53  *
54  *     If keepConfig is TRUE then the following are not removed:
55  *
56  *         a) file %WINDIR%\afsdcell.ini - CellServDB
57  *         b) file %WINDIR%\afsdsbmt.ini - submount settings
58  *         c) file %WINDIR%\afsd.ini - client parameter settings
59  */
60 DWORD Client34Eradicate(BOOL keepConfig)
61 {
62     DWORD rc = ERROR_SUCCESS;
63     DWORD status, version;
64     BOOL installPathValid = FALSE;
65     char installPath[MAX_PATH];
66     char winPath[MAX_PATH];
67     char sysPath[MAX_PATH];
68     char filePath[MAX_PATH];
69
70     /* check client version and fetch install directory */
71
72     status = ClientSoftwareGet(&version, installPath, MAX_PATH);
73
74     if (status == ERROR_SUCCESS) {
75         if (version == 34) {
76             /* 3.4 client to eradicate */
77             installPathValid = TRUE;
78         } else {
79             /* 3.5 or later client installed */
80             return ERROR_SUCCESS;
81         }
82     }
83
84     /* If no client info found then assume partial unconfigure and keep
85      * trying.  Save errors as proceed but keep going to maximize removal.
86      */
87
88
89     /* stop and delete client service */
90
91     status = ClientServiceDelete();
92     if (status != ERROR_SUCCESS) {
93         rc = status;
94     }
95
96     /* remove client files */
97
98     status = GetWindowsDirectory(winPath, MAX_PATH);
99     if (status == 0 || status > MAX_PATH) {
100         /* this should never happen */
101         winPath[0] = '\0';
102     }
103
104     status = GetSystemDirectory(sysPath, MAX_PATH);
105     if (status == 0 || status > MAX_PATH) {
106         /* this should never happen */
107         sysPath[0] = '\0';
108     }
109
110     if (installPathValid) {
111         status = DirectoryForceRemove(installPath);
112         if (status != ERROR_SUCCESS && status != ERROR_PATH_NOT_FOUND) {
113             rc = status;
114         }
115     }
116
117     sprintf(filePath, "%s\\%s", winPath, "afsd.log");
118     status = FileForceRemove(filePath);
119     if (status != ERROR_SUCCESS && status != ERROR_FILE_NOT_FOUND) {
120         rc = status;
121     }
122
123     sprintf(filePath, "%s\\%s", winPath, "afsd_init.log");
124     status = FileForceRemove(filePath);
125     if (status != ERROR_SUCCESS && status != ERROR_FILE_NOT_FOUND) {
126         rc = status;
127     }
128
129     sprintf(filePath, "%s\\%s", sysPath, "afs_cpa.cpl");
130     status = FileForceRemove(filePath);
131     if (status != ERROR_SUCCESS && status != ERROR_FILE_NOT_FOUND) {
132         rc = status;
133     }
134
135     sprintf(filePath, "%s\\%s", sysPath, "afs_shl_ext.dll");
136     status = FileForceRemove(filePath);
137     if (status != ERROR_SUCCESS && status != ERROR_FILE_NOT_FOUND) {
138         rc = status;
139     }
140
141     if (!keepConfig) {
142         sprintf(filePath, "%s\\%s", winPath, "afsdcell.ini");
143         status = FileForceRemove(filePath);
144         if (status != ERROR_SUCCESS && status != ERROR_FILE_NOT_FOUND) {
145             rc = status;
146         }
147
148         sprintf(filePath, "%s\\%s", winPath, "afsdsbmt.ini");
149         status = FileForceRemove(filePath);
150         if (status != ERROR_SUCCESS && status != ERROR_FILE_NOT_FOUND) {
151             rc = status;
152         }
153
154         sprintf(filePath, "%s\\%s", winPath, "afsd.ini");
155         status = FileForceRemove(filePath);
156         if (status != ERROR_SUCCESS && status != ERROR_FILE_NOT_FOUND) {
157             rc = status;
158         }
159     }
160
161     do { /* locate all start menu entries (common or for a user) */
162         status = FolderLocateInTree(winPath, "Transarc AFS Client", filePath);
163         if (status == ERROR_SUCCESS) {
164             status = DirectoryForceRemove(filePath);
165         }
166     } while (status == ERROR_SUCCESS);
167     if (status != ERROR_FILE_NOT_FOUND) {
168         rc = status;
169     }
170
171     /* update relevant Microsoft registry entries */
172
173     status = RegDeleteEntryAlt(CLIENT34_FME_VALUE, REGENTRY_VALUE);
174     if (status != ERROR_SUCCESS && status != ERROR_FILE_NOT_FOUND) {
175         rc = status;
176     }
177
178     status = Client34ZapUninstallKeys();
179     if (status != ERROR_SUCCESS) {
180         rc = status;
181     }
182
183     if (!RemoveFromProviderOrder(AFSREG_CLT_SVC_NAME)) {
184         /* function does not supply an error value; make one up */
185         rc = ERROR_FILE_NOT_FOUND;
186     }
187
188     if (installPathValid) {
189         status = GetShortPathName(installPath, filePath, MAX_PATH);
190         if (status == 0 || status > MAX_PATH) {
191             strcpy(filePath, installPath);
192         }
193         strcat(filePath, "\\Program");
194         if (!RemoveFromSystemPath(filePath)) {
195             /* function does not supply an error value; make one up */
196             rc = ERROR_FILE_NOT_FOUND;
197         }
198     }
199
200     /* remove client registry entries */
201
202     status = RegDeleteEntryAlt(AFSREG_CLT_SVC_KEY, REGENTRY_KEY);
203     if (status != ERROR_SUCCESS && status != ERROR_FILE_NOT_FOUND) {
204         rc = status;
205     }
206
207     status = RegDeleteEntryAlt(AFSREG_CLT_SW_KEY, REGENTRY_KEY);
208     if (status != ERROR_SUCCESS && status != ERROR_FILE_NOT_FOUND) {
209         rc = status;
210     }
211     return rc;
212 }
213
214
215
216 /* ------------------ utility functions ---------------------- */
217
218
219 /*
220  * ClientSoftwareGet() -- fetch client software installation information.
221  */
222 static DWORD
223 ClientSoftwareGet(DWORD *version, char *dir, DWORD dirSize)
224 {
225     HKEY key;
226     DWORD rc = ERROR_SUCCESS;
227
228     /* get client version and install directory */
229
230     rc = RegOpenKeyAlt(AFSREG_NULL_KEY, AFSREG_CLT_SW_VERSION_KEY,
231                        KEY_READ, 0, &key, NULL);
232     if (rc == ERROR_SUCCESS) {
233         DWORD major, minor, dataSize;
234
235         dataSize = sizeof(DWORD);
236         rc = RegQueryValueEx(key, AFSREG_CLT_SW_VERSION_MAJOR_VALUE,
237                              NULL, NULL, (void*)&major, &dataSize);
238         if (rc == ERROR_SUCCESS) {
239             dataSize = sizeof(DWORD);
240             rc = RegQueryValueEx(key, AFSREG_CLT_SW_VERSION_MINOR_VALUE,
241                                  NULL, NULL, (void*)&minor, &dataSize);
242             if (rc == ERROR_SUCCESS) {
243                 dataSize = dirSize;
244                 rc = RegQueryValueEx(key, AFSREG_CLT_SW_VERSION_DIR_VALUE,
245                                      NULL, NULL, dir, &dataSize);
246             }
247         }
248         (void)RegCloseKey(key);
249
250         if (rc == ERROR_SUCCESS) {
251             *version = (major * 10) + minor;
252         }
253     }
254     return rc;
255 }
256
257
258 /*
259  * ClientServiceDelete() -- stop and delete the client service.
260  */
261 static DWORD
262 ClientServiceDelete(void)
263 {
264     SC_HANDLE scmHandle, svcHandle;
265     DWORD rc = ERROR_SUCCESS;
266
267     if ((scmHandle = OpenSCManager(NULL,
268                                    NULL, SC_MANAGER_ALL_ACCESS)) == NULL ||
269         (svcHandle = OpenService(scmHandle,
270                                  AFSREG_CLT_SVC_NAME,
271                                  SERVICE_ALL_ACCESS)) == NULL) {
272         /* can't connect to SCM or can't open service */
273         DWORD status = GetLastError();
274
275         if (status != ERROR_SERVICE_DOES_NOT_EXIST) {
276             rc = status;
277         }
278
279         if (scmHandle != NULL) {
280             CloseServiceHandle(scmHandle);
281         }
282
283     } else {
284         SERVICE_STATUS svcStatus;
285
286         if (!ControlService(svcHandle, SERVICE_CONTROL_STOP, &svcStatus)) {
287             /* service stop failed */
288             DWORD status = GetLastError();
289
290             if (status != ERROR_SERVICE_NOT_ACTIVE) {
291                 rc = status;
292             }
293         }
294
295         if (rc == ERROR_SUCCESS) {
296             if (!DeleteService(svcHandle)) {
297                 /* service delete failed */
298                 DWORD status = GetLastError();
299
300                 if (status != ERROR_SERVICE_MARKED_FOR_DELETE) {
301                     rc = status;
302                 }
303             }
304         }
305
306         CloseServiceHandle(svcHandle);
307         CloseServiceHandle(scmHandle);
308
309         if (rc == ERROR_SUCCESS) {
310             /* let client state settle; not mandatory so don't do query */
311             Sleep(2000);
312         }
313     }
314     return rc;
315 }
316
317
318 /*
319  * DirectoryForceRemove() -- forcibly, and recursively, remove a directory
320  *     and its contents; this may require moving in-use files to a temp
321  *     directory and marking them for delete on reboot.
322  */
323 static DWORD
324 DirectoryForceRemove(const char *dir)
325 {
326     DWORD rc = ERROR_SUCCESS;
327     HANDLE enumHandle;
328     WIN32_FIND_DATA enumResult;
329     char filePath[MAX_PATH];
330
331     /* enumerate directory and delete contents */
332
333     sprintf(filePath, "%s\\*.*", dir);
334
335     enumHandle = FindFirstFile(filePath, &enumResult);
336
337     if (enumHandle == INVALID_HANDLE_VALUE) {
338         DWORD status = GetLastError();
339
340         if (status != ERROR_NO_MORE_FILES) {
341             /* failure other than contents already deleted */
342             rc = status;
343         }
344
345     } else {
346         while (1) {
347             DWORD status;
348
349             sprintf(filePath, "%s\\%s", dir, enumResult.cFileName);
350
351             if (enumResult.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
352                 if (strcmp(enumResult.cFileName, ".") == 0 ||
353                     strcmp(enumResult.cFileName, "..") == 0) {
354                     /* ignore these special directories */
355                     status = ERROR_SUCCESS;
356                 } else {
357                     status = DirectoryForceRemove(filePath);
358                 }
359             } else {
360                 status = FileForceRemove(filePath);
361             }
362
363             if (status != ERROR_SUCCESS && status != ERROR_FILE_NOT_FOUND) {
364                 /* save error but keep on truckin' */
365                 rc = status;
366             }
367
368             if (!FindNextFile(enumHandle, &enumResult)) {
369                 status = GetLastError();
370
371                 if (status != ERROR_NO_MORE_FILES) {
372                     rc = status;
373                 }
374                 break;
375             }
376         }
377         FindClose(enumHandle);
378     }
379
380     if (rc == ERROR_SUCCESS) {
381         if (!RemoveDirectory(dir)) {
382             rc = GetLastError();
383         }
384     }
385     return rc;
386 }
387
388
389 /*
390  * FileForceRemove() -- forcibly remove a file; if the file is in-use then
391  *     move the file to a temp directory and mark it for delete on reboot.
392  */
393 static DWORD
394 FileForceRemove(const char *filePath)
395 {
396     DWORD rc = ERROR_SUCCESS;
397
398     if (!DeleteFile(filePath)) {
399         rc = GetLastError();
400
401         if (rc != ERROR_FILE_NOT_FOUND) {
402             /* couldn't just delete; probably in use; try to move */
403             char filePathFull[MAX_PATH];
404             char *dummy;
405             DWORD status;
406
407             rc = ERROR_SUCCESS;
408
409             status = GetFullPathName(filePath, MAX_PATH, filePathFull, &dummy);
410             if (status == 0 || status > MAX_PATH) {
411                 if (status == 0) {
412                     rc = GetLastError();
413                 } else {
414                     rc = ERROR_INVALID_PARAMETER;
415                 }
416             }
417
418             if (rc == ERROR_SUCCESS) {
419                 char tempDir[MAX_PATH];
420                 char tempPath[MAX_PATH];
421
422                 status = GetTempPath(MAX_PATH, tempDir);
423                 if ((status == 0 || status > MAX_PATH) ||
424                     (_strnicmp(tempDir, filePathFull, 3))) {
425                     /* failed getting temp dir, or temp dir is on different
426                      * drive than file (so can't do a true move to there).
427                      */
428                     sprintf(tempDir, "%c:\\", filePathFull[0]);
429                 }
430
431                 if (!GetTempFileName(tempDir, "AFS", 0, tempPath)) {
432                     rc = GetLastError();
433                 } else {
434                     if (MoveFileEx(filePathFull, tempPath,
435                                    MOVEFILE_REPLACE_EXISTING)) {
436                         (void)SetFileAttributes(tempPath,
437                                                 FILE_ATTRIBUTE_NORMAL);
438                         (void)MoveFileEx(tempPath, NULL,
439                                          MOVEFILE_DELAY_UNTIL_REBOOT);
440                     } else {
441                         rc = GetLastError();
442                     }
443                 }
444             }
445         }
446     }
447     return rc;
448 }
449
450
451 /*
452  * FolderLocateInTree() -- find an instance of named directory in specified
453  *     tree; folderName is presumed to be a directory name only (i.e., not
454  *     a path); buf is presumed to be at least MAX_PATH characters.
455  */
456 static DWORD
457 FolderLocateInTree(const char *dir, const char *folderName, char *buf)
458 {
459     DWORD rc = ERROR_SUCCESS;
460     HANDLE enumHandle;
461     WIN32_FIND_DATA enumResult;
462     char filePath[MAX_PATH];
463
464     /* enumerate directory recursively looking for folder */
465
466     sprintf(filePath, "%s\\*.*", dir);
467
468     enumHandle = FindFirstFile(filePath, &enumResult);
469
470     if (enumHandle == INVALID_HANDLE_VALUE) {
471         DWORD status = GetLastError();
472
473         if (status == ERROR_NO_MORE_FILES) {
474             rc = ERROR_FILE_NOT_FOUND;
475         } else {
476             rc = status;
477         }
478
479     } else {
480         while (1) {
481             DWORD status = ERROR_FILE_NOT_FOUND;
482
483             if (enumResult.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
484                 if (_stricmp(enumResult.cFileName, folderName) == 0) {
485                     /* is folder that we're looking for */
486                     sprintf(buf, "%s\\%s", dir, enumResult.cFileName);
487                     status = ERROR_SUCCESS;
488
489                 } else if (strcmp(enumResult.cFileName, ".") != 0 &&
490                            strcmp(enumResult.cFileName, "..") != 0) {
491                     /* is not folder that we're looking for; search it */
492                     sprintf(filePath, "%s\\%s", dir, enumResult.cFileName);
493                     status = FolderLocateInTree(filePath, folderName, buf);
494                 }
495             }
496
497             if (status != ERROR_FILE_NOT_FOUND) {
498                 /* found folder or encountered an error; quit */
499                 rc = status;
500                 break;
501             } else {
502                 /* folder not found; keep looking */
503                 if (!FindNextFile(enumHandle, &enumResult)) {
504                     status = GetLastError();
505
506                     if (status == ERROR_NO_MORE_FILES) {
507                         rc = ERROR_FILE_NOT_FOUND;
508                     } else {
509                         rc = status;
510                     }
511                     break;
512                 }
513             }
514         }
515         FindClose(enumHandle);
516     }
517     return rc;
518 }
519
520
521 /*
522  * Client34ZapUninstallKeys() -- delete all of the client uninstall keys
523  */
524 static DWORD
525 Client34ZapUninstallKeys(void)
526 {
527     DWORD rc = ERROR_SUCCESS;
528     HKEY key;
529
530     /* enumerate all uninstall registry keys looking for client's */
531
532     rc = RegOpenKeyAlt(AFSREG_NULL_KEY,
533                        MS_UNINSTALL_KEY, KEY_ALL_ACCESS, 0, &key, NULL);
534     if (rc == ERROR_SUCCESS) {
535         char *keyEnum;
536
537         rc = RegEnumKeyAlt(key, &keyEnum);
538         if (rc == ERROR_SUCCESS && keyEnum != NULL) {
539             char *keyEnumName;
540
541             for (keyEnumName = keyEnum;
542                  *keyEnumName != '\0';
543                  keyEnumName += strlen(keyEnumName) + 1) {
544                 if (_stricmp(keyEnumName, "AFSDeinstKey") == 0 ||
545                     _strnicmp(keyEnumName, "AFSV34", 6) == 0) {
546                     /* found an AFS uninstall key */
547                     DWORD status = RegDeleteKeyAlt(key, keyEnumName);
548                     if (status != ERROR_SUCCESS) {
549                         rc = status;
550                     }
551                 }
552             }
553             free(keyEnum);
554         }
555         (void) RegCloseKey(key);
556     }
557     return rc;
558 }