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