Windows: shell extension is multithreaded
[openafs.git] / src / WINNT / client_exp / gui2fs.cpp
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 "stdafx.h"
11 #include <winsock2.h>
12 #include <ws2tcpip.h>
13
14 extern "C" {
15 #include <afsconfig.h>
16 #include <afs/param.h>
17 #include <roken.h>
18 #include <afs/stds.h>
19 }
20
21 #include <errno.h>
22 #include <time.h>
23
24 #include "gui2fs.h"
25 #include "msgs.h"
26 #include "results_dlg.h"
27 #include "volume_inf.h"
28 #include "mount_points_dlg.h"
29 #include "symlinks_dlg.h"
30 #include "hourglass.h"
31 #include "down_servers_dlg.h"
32
33 extern "C" {
34 #include <rx/rx_globals.h>
35 #include "fs_utils.h"
36 #include "fs_acl.h"
37 #include <afs/afsint.h>
38 #include <afs/afs_consts.h>
39 #include <afs/cellconfig.h>
40 #include <afs/vldbint.h>
41 #include <afs/volser.h>
42 #include <afs/auth.h>
43 #include <WINNT\afsreg.h>
44 #include <cm.h>
45 #include <cm_nls.h>
46 #include <osi.h>
47 #include <cm_user.h>
48 #include <cm_scache.h>
49 #include <cm_ioctl.h>
50 }
51
52 #define STRSAFE_NO_DEPRECATE
53 #include <strsafe.h>
54
55 /*
56  * the NO_CALLER symbol is used to document functions
57  * that are present in this file but have no caller
58  */
59 #define NO_CALLER
60
61
62 #define PCCHAR(str)             ((char *)(const char *)(str))
63 #define VL_NOENT                (363524L)
64
65 #define MAXNAME 100
66 #define MAXINSIZE 1300    /* pioctl complains if data is larger than this */
67 #define VMSGSIZE 128      /* size of msg buf in volume hdr */
68
69 #define MAXCELLCHARS            64
70 #define MAXHOSTCHARS            64
71 #define MAXHOSTSPERCELL         8
72
73 static struct ubik_client *uclient;
74 static int rxInitDone = 0;
75 static char pn[] = "fs";
76
77 // #define      LOGGING_ON              // Enable this to log certain pioctl calls
78
79 #ifdef  LOGGING_ON
80 static char *szLogFileName = "afsguilog.txt";
81 #endif
82
83 #ifdef UNICODE
84 class CStringUtf8 : public CStringA
85 {
86 public:
87     CStringUtf8(const CStringW& csw) : CStringA()
88     {
89         SetString(csw);
90     }
91
92     CStringUtf8(const char * cp) : CStringA(cp) {}
93
94     CStringUtf8() :CStringA() {}
95
96     void SetString(const CStringW& csw)
97     {
98         char buffer[1024];
99         int rv;
100
101         rv = WideCharToMultiByte(CP_UTF8, 0, csw, -1,
102                                  buffer, sizeof(buffer),
103                                  NULL, FALSE);
104
105         if (rv != 0) {
106             CStringA::SetString(buffer);
107         } else {
108             if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
109                 int cb_req;
110
111                 cb_req = WideCharToMultiByte(CP_UTF8, 0, csw, -1, NULL, 0, NULL, FALSE);
112                 if (cb_req != 0) {
113                     cb_req ++;
114
115                     WideCharToMultiByte(CP_UTF8, 0, csw, -1, CStringA::GetBuffer(cb_req), cb_req, NULL, FALSE);
116                     CStringA::ReleaseBuffer();
117                 }
118             } else {
119 #ifdef DEBUG
120                 DebugBreak();
121 #endif
122             }
123         }
124     }
125
126     static CString _Utf8ToCString(const char * ustr)
127     {
128         CString cs;
129         int cch;
130
131         cch = MultiByteToWideChar(CP_UTF8, 0, ustr, -1, NULL, 0);
132         if (cch == 0) {
133             cs.Empty();
134             return cs;
135         }
136
137         cch++;
138         cch = MultiByteToWideChar(CP_UTF8, 0, ustr, -1, cs.GetBuffer(cch), cch);
139         cs.ReleaseBuffer();
140
141         return cs;
142     }
143 };
144
145 long
146 pioctl_T(const CString& path, long opcode, struct ViceIoctl * blob, int follow)
147 {
148     CStringUtf8 upath(path);
149
150     return pioctl_utf8(PCCHAR(upath), opcode, blob, follow);
151 }
152
153 #define Utf8ToCString(cs) CStringUtf8::_Utf8ToCString(cs)
154 #else
155 #define pioctl_T(path, op, vblob, follow) pioctl(PCCHAR(path), op, vblob, follow)
156 #define Utf8ToCString(cs) (cs)
157 #endif
158
159 static int NO_CALLER
160 VLDBInit(int noAuthFlag, struct afsconf_cell *info)
161 {
162     afs_int32 code;
163
164     code = ugen_ClientInit(noAuthFlag, (char *)AFSDIR_CLIENT_ETC_DIRPATH,
165                            info->name, 0, &uclient,
166                            NULL, pn, rxkad_clear,
167                            VLDB_MAXSERVERS, AFSCONF_VLDBSERVICE, 50,
168                            0, 0, USER_SERVICE_ID);
169     rxInitDone = 1;
170     return code;
171 }
172
173 static FILE *
174 OpenFile(char *file, char *rwp)
175 {
176     char wdir[256];
177     long code;
178     long tlen;
179     FILE *fp;
180
181     code = GetWindowsDirectoryA(wdir, sizeof(wdir));
182     if (code == 0 || code > sizeof(wdir))
183         return FALSE;
184
185     /* add trailing backslash, if required */
186     tlen = strlen(wdir);
187     if (wdir[tlen - 1] != '\\')
188         strcat(wdir, "\\");
189
190     strcat(wdir, file);
191
192     fp = fopen(wdir, rwp);
193
194     return fp;
195 }
196
197 CString StripPath(CString& strPath)
198 {
199     int nIndex = strPath.ReverseFind('\\');
200
201     CString strFile = strPath.Mid(nIndex + 1);
202     if (strFile.IsEmpty())
203         return strPath;
204
205     return strFile;
206 }
207
208 CStringArray&
209 StripPath(CStringArray& files)
210 {
211     for (int i = 0; i < files.GetSize(); i++)
212         files[i] = StripPath(files[i]);
213
214     return files;
215 }
216
217 void
218 Flush(const CStringArray& files)
219 {
220     LONG code;
221     struct ViceIoctl blob;
222     int error = 0;
223
224     HOURGLASS hourglass;
225
226     for (int i = 0; i < files.GetSize(); i++) {
227         blob.in_size = blob.out_size = 0;
228
229         code = pioctl_T(files[i], VIOCFLUSH, &blob, 0);
230         if (code) {
231             error = 1;
232             if (errno == EMFILE)
233                 ShowMessageBox(IDS_FLUSH_FAILED, MB_ICONERROR, IDS_FLUSH_FAILED, files[i]);
234             else
235                 ShowMessageBox(IDS_FLUSH_ERROR, MB_ICONERROR, IDS_FLUSH_ERROR, files[i], strerror(errno));
236         }
237     }
238
239     if (!error)
240         ShowMessageBox(IDS_FLUSH_OK, MB_ICONINFORMATION, IDS_FLUSH_OK);
241 }
242
243 void
244 FlushVolume(const CStringArray& files)
245 {
246     LONG code;
247     struct ViceIoctl blob;
248     int error = 0;
249
250     HOURGLASS hourglass;
251
252     for (int i = 0; i < files.GetSize(); i++) {
253         blob.in_size = blob.out_size = 0;
254
255         code = pioctl_T(files[i], VIOC_FLUSHVOLUME, &blob, 0);
256         if (code) {
257             error = 1;
258             ShowMessageBox(IDS_FLUSH_VOLUME_ERROR, MB_ICONERROR, IDS_FLUSH_VOLUME_ERROR, files[i], strerror(errno));
259         }
260     }
261
262     if (!code)
263         ShowMessageBox(IDS_FLUSH_VOLUME_OK, MB_ICONINFORMATION, IDS_FLUSH_VOLUME_OK);
264 }
265
266 void NO_CALLER
267 WhichCell(CStringArray& files)
268 {
269     LONG code;
270     struct ViceIoctl blob;
271     int error;
272     CString str;
273     CString str2;
274
275     CStringArray results;
276
277     error = 0;
278
279     HOURGLASS hourglass;
280
281     for (int i = 0; i < files.GetSize(); i++) {
282         char space[AFS_PIOCTL_MAXSIZE];
283
284         blob.in_size = 0;
285         blob.out_size = AFS_PIOCTL_MAXSIZE;
286         blob.out = space;
287
288         code = pioctl_T(files[i], VIOC_FILE_CELL_NAME, &blob, 1);
289         if (code) {
290             if (code == ENOENT) {
291                 LoadString (str, IDS_CANT_GET_CELL);
292                 results.Add(str);
293             } else
294                 results.Add(GetAfsError(errno));
295         } else {
296             space[AFS_PIOCTL_MAXSIZE - 1] = '\0';
297             results.Add(Utf8ToCString(space));
298         }
299     }
300
301     LoadString (str, IDS_SHOW_CELL);
302     LoadString (str2, IDS_SHOW_CELL_COLUMN);
303     CResultsDlg dlg(SHOW_CELL_HELP_ID);
304     dlg.SetContents(str, str2, StripPath(files), results);
305     dlg.DoModal();
306 }
307
308 void NO_CALLER
309 WSCellCmd(void)
310 {
311     char space[AFS_PIOCTL_MAXSIZE];
312     LONG code;
313     struct ViceIoctl blob;
314
315     HOURGLASS hourglass;
316
317     blob.in_size = 0;
318     blob.in = (char *) 0;
319     blob.out_size = AFS_PIOCTL_MAXSIZE;
320     blob.out = space;
321
322     code = pioctl((char *) 0, VIOC_GET_WS_CELL, &blob, 1);
323
324     /*
325      * Cell name is left in 'space' as side effect.
326      * At present no callers of this function.
327      */
328 }
329
330 BOOL NO_CALLER
331 CheckVolumes(void)
332 {
333     LONG code;
334     struct ViceIoctl blob;
335
336     blob.in_size = 0;
337     blob.out_size = 0;
338     code = pioctl(0, VIOCCKBACK, &blob, 1);
339     if (code) {
340         ShowMessageBox(IDS_CHECK_VOLUMES_ERROR, MB_ICONERROR, IDS_CHECK_VOLUMES_ERROR, GetAfsError(errno, CString()));
341         return FALSE;
342     }
343
344     ShowMessageBox(IDS_CHECK_VOLUMES_OK, MB_OK|MB_ICONINFORMATION, IDS_CHECK_VOLUMES_OK);
345
346     return TRUE;
347 }
348
349 void NO_CALLER
350 SetCacheSizeCmd(LONG nNewCacheSize)
351 {
352     LONG code;
353     struct ViceIoctl blob;
354
355     HOURGLASS hourglass;
356
357     blob.in = (char *) &nNewCacheSize;
358     blob.in_size = sizeof(LONG);
359     blob.out_size = 0;
360
361     code = pioctl(0, VIOCSETCACHESIZE, &blob, 1);
362
363     /* error handling? */
364 }
365
366 void NO_CALLER
367 WhereIs(CStringArray& files)
368 {
369     LONG code;
370     struct ViceIoctl blob;
371     CStringArray servers;
372     CStringArray resultFiles;
373     CString str;
374     CString str2;
375
376     HOURGLASS hourglass;
377
378     for (int i = 0; i < files.GetSize(); i++) {
379         char space[AFS_PIOCTL_MAXSIZE];
380
381         blob.out_size = AFS_PIOCTL_MAXSIZE;
382         blob.in_size = 0;
383         blob.out = space;
384         memset(space, 0, sizeof(space));
385
386         code = pioctl_T(files[i], VIOCWHEREIS, &blob, 1);
387         if (code) {
388             resultFiles.Add(StripPath(files[i]));
389             servers.Add(GetAfsError(errno));
390             continue;
391         }
392
393         LONG *hosts = (LONG *)space;
394         BOOL bFirst = TRUE;
395         str = "";
396
397         for (int j = 0; j < AFS_MAXHOSTS; j++) {
398             if (hosts[j] == 0)
399                 break;
400             char *hostName = hostutil_GetNameByINet(hosts[j]);
401             if (bFirst) {
402                 resultFiles.Add(StripPath(files[i]));
403                 bFirst = FALSE;
404             } else
405                 resultFiles.Add(" ");
406             servers.Add(hostName);
407         }
408     }
409
410     LoadString (str, IDS_SHOW_FS);
411     LoadString (str2, IDS_SHOW_FS_COLUMN);
412     CResultsDlg dlg(SHOW_FILE_SERVERS_HELP_ID);
413     dlg.SetContents(str, str2, resultFiles, servers);
414     dlg.DoModal();
415 }
416
417 static int
418 CMtoUNIXerror(int cm_code)
419 {
420     switch (cm_code) {
421     case CM_ERROR_TIMEDOUT:
422         return ETIMEDOUT;
423     case CM_ERROR_NOACCESS:
424         return EACCES;
425     case CM_ERROR_NOSUCHFILE:
426         return ENOENT;
427     case CM_ERROR_INVAL:
428         return EINVAL;
429     case CM_ERROR_BADFD:
430         return EBADF;
431     case CM_ERROR_EXISTS:
432         return EEXIST;
433     case CM_ERROR_CROSSDEVLINK:
434         return EXDEV;
435     case CM_ERROR_NOTDIR:
436         return ENOTDIR;
437     case CM_ERROR_ISDIR:
438         return EISDIR;
439     case CM_ERROR_READONLY:
440         return EROFS;
441     case CM_ERROR_WOULDBLOCK:
442         return EWOULDBLOCK;
443     case CM_ERROR_NOSUCHCELL:
444         return ESRCH;           /* hack */
445     case CM_ERROR_NOSUCHVOLUME:
446         return EPIPE;           /* hack */
447     case CM_ERROR_NOMORETOKENS:
448         return EDOM;            /* hack */
449     case CM_ERROR_TOOMANYBUFS:
450         return EFBIG;           /* hack */
451     default:
452         if (cm_code > 0 && cm_code < EILSEQ)
453             return cm_code;
454         else
455             return ENOTTY;
456     }
457 }
458
459 CString
460 GetAfsError(int code, const TCHAR *filename)
461 {
462     CString strMsg;
463
464     code = CMtoUNIXerror(code);
465
466     if (code == EINVAL) {
467         if (filename)
468             strMsg.Format(_T("Invalid argument; it is possible that the file is not in AFS"));
469         else
470             strMsg.Format(_T("Invalid argument"));
471     } else if (code == ENOENT) {
472         if (filename)
473             strMsg.Format(_T("The file does not exist"));
474         else
475             strMsg.Format(_T("No such file returned"));
476     } else if (code == EROFS)  {
477         strMsg.Format(_T("You can not change a backup or readonly volume"));
478     } else if (code == EACCES || code == EPERM) {
479         strMsg.Format(_T("You do not have the required rights to do this operation"));
480     } else if (code == ENODEV) {
481         strMsg.Format(_T("AFS service may not have started"));
482     } else if (code == ESRCH) {
483         strMsg.Format(_T("Cell name not recognized"));
484     } else if (code == ETIMEDOUT) {
485         strMsg.Format(_T("Connection timed out"));
486     } else if (code == EPIPE) {
487         strMsg.Format(_T("Volume name or ID not recognized"));
488     } else {
489         strMsg.Format(_T("Error 0x%x occurred"), code);
490     }
491
492     return strMsg;
493 }
494
495
496 static int
497 foldcmp (char *a, char *b)
498 {
499     char t, u;
500     while (1) {
501         t = *a++;
502         u = *b++;
503         if (t >= 'A' && t <= 'Z') t += 0x20;
504         if (u >= 'A' && u <= 'Z') u += 0x20;
505         if (t != u) return 1;
506         if (t == 0) return 0;
507     }
508 }
509
510 CString
511 GetRightsString(LONG arights, int dfs)
512 {
513     CString str;
514
515     if (!dfs) {
516         if (arights & PRSFS_READ) str += _T("r");
517         if (arights & PRSFS_LOOKUP) str += _T("l");
518         if (arights & PRSFS_INSERT) str += _T("i");
519         if (arights & PRSFS_DELETE) str += _T("d");
520         if (arights & PRSFS_WRITE) str += _T("w");
521         if (arights & PRSFS_LOCK) str += _T("k");
522         if (arights & PRSFS_ADMINISTER) str += _T("a");
523     } else {
524         ASSERT(FALSE);
525 /*
526                 if (arights & DFS_READ) str += _T("r"); else str += _T("-");
527                 if (arights & DFS_WRITE) str += _T("w"); else printf(_T("-"));
528                 if (arights & DFS_EXECUTE) str += _T("x"); else printf(_T("-"));
529                 if (arights & DFS_CONTROL) str += _T("c"); else printf(_T("-"));
530                 if (arights & DFS_INSERT) str += _T("i"); else printf(_T("-"));
531                 if (arights & DFS_DELETE) str += _T("d"); else printf(_T("-"));
532                 if (arights & (DFS_USRALL)) str += _T("+");
533 */
534     }
535
536     return str;
537 }
538
539 struct Acl *
540 EmptyAcl(const CString& strCellName)
541 {
542     struct Acl *tp;
543     CStringUtf8 ustrCell(strCellName);
544
545     tp = (struct Acl *)malloc(sizeof (struct Acl));
546     tp->nplus = tp->nminus = 0;
547     tp->pluslist = tp->minuslist = 0;
548     tp->dfs = 0;
549     StringCbCopyA(tp->cell, sizeof(tp->cell), ustrCell);
550
551     return tp;
552 }
553
554 void
555 CleanACL(CStringArray& names)
556 {
557     LONG code;
558     struct Acl *ta;
559     struct ViceIoctl blob;
560     int changes;
561
562     ShowMessageBox(IDS_CLEANACL_MSG, MB_OK|MB_ICONINFORMATION, IDS_CLEANACL_MSG);
563
564     HOURGLASS hourglass;
565
566     for (int i = 0; i < names.GetSize(); i++) {
567         char space[AFS_PIOCTL_MAXSIZE];
568
569         blob.out_size = AFS_PIOCTL_MAXSIZE;
570         blob.in_size = 0;
571         blob.out = space;
572
573         code = pioctl_T(names[i], VIOCGETAL, &blob, 1);
574         if (code) {
575             ShowMessageBox(IDS_CLEANACL_ERROR, MB_ICONERROR, 0, names[i], GetAfsError(errno));
576             continue;
577         }
578
579         ta = ParseAcl(space, AFS_PIOCTL_MAXSIZE);
580         if (ta == NULL) {
581             ShowMessageBox(IDS_INVALID_ACL_DATA, MB_ICONERROR, IDS_INVALID_ACL_DATA);
582             continue;
583         }
584         if (ta->dfs) {
585             ShowMessageBox(IDS_CLEANACL_NOT_SUPPORTED, MB_ICONERROR, IDS_CLEANACL_NOT_SUPPORTED, names[i]);
586             continue;
587         }
588
589         changes = CleanAcl(ta, NULL);
590         if (!changes)
591             continue;
592
593         /* now set the acl */
594         blob.in = AclToString(ta);
595         blob.in_size = strlen((char *)blob.in) + 1;
596         blob.out_size = 0;
597
598         code = pioctl_T(names[i], VIOCSETAL, &blob, 1);
599         if (code) {
600             if (errno == EINVAL) {
601                 ShowMessageBox(IDS_CLEANACL_INVALID_ARG, MB_ICONERROR, IDS_CLEANACL_INVALID_ARG, names[i]);
602                 continue;
603             }
604             else {
605                 ShowMessageBox(IDS_CLEANACL_ERROR, MB_ICONERROR, 0, names[i], GetAfsError(errno));
606                 continue;
607             }
608         }
609     }
610 }
611
612 // Derived from fs.c's ListAclCmd
613 BOOL
614 GetRights(const CString& strDir, CStringArray& strNormal, CStringArray& strNegative)
615 {
616     LONG code;
617     struct Acl *ta;
618     struct ViceIoctl blob;
619     struct AclEntry *te;
620     int idf = 0; //getidf(as, parm_listacl_id);
621     char space[AFS_PIOCTL_MAXSIZE];
622     HOURGLASS hourglass;
623
624     blob.out_size = AFS_PIOCTL_MAXSIZE;
625     blob.in_size = idf;
626     blob.in = blob.out = space;
627
628     code = pioctl_T(strDir, VIOCGETAL, &blob, 1);
629     if (code) {
630         ShowMessageBox(IDS_GETRIGHTS_ERROR, MB_ICONERROR, IDS_GETRIGHTS_ERROR, strDir, GetAfsError(errno));
631         return FALSE;
632     }
633
634     ta = ParseAcl(space, AFS_PIOCTL_MAXSIZE);
635     if (ta == NULL) {
636         ShowMessageBox(IDS_INVALID_ACL_DATA, MB_ICONERROR, IDS_INVALID_ACL_DATA);
637         return FALSE;
638     }
639     if (ta->dfs) {
640         ShowMessageBox(IDS_DFSACL_ERROR, MB_ICONERROR, IDS_DFSACL_ERROR);
641         return FALSE;
642     }
643
644 //      if (ta->dfs)
645 //              printf("  Default cell = %s\n", ta->cell);
646
647     CString strRight;
648
649     if (ta->nplus > 0) {
650         for (te = ta->pluslist; te; te = te->next) {
651             strNormal.Add(te->name);
652             strNormal.Add(GetRightsString(te->rights, ta->dfs));
653         }
654     }
655
656     if (ta->nminus > 0) {
657         for (te = ta->minuslist; te; te = te->next) {
658             strNegative.Add(te->name);
659             strNegative.Add(GetRightsString(te->rights, ta->dfs));
660         }
661     }
662
663     return TRUE;
664 }
665
666 struct AclEntry *
667 FindList(struct AclEntry *pCurEntry, const char *entryName)
668 {
669     while (pCurEntry) {
670         if (!foldcmp(pCurEntry->name, PCCHAR(entryName)))
671             return pCurEntry;
672         pCurEntry = pCurEntry->next;
673     }
674
675     return 0;
676 }
677
678 void
679 ChangeList(struct Acl *pAcl, BYTE bNormalRights, const CString & entryName, LONG nEntryRights)
680 {
681     ASSERT(pAcl);
682     ASSERT(entryName);
683
684     struct AclEntry *pEntry;
685     CStringUtf8 uEntryName(entryName);
686
687     HOURGLASS hourglass;
688
689     pEntry = (bNormalRights ? pAcl->pluslist : pAcl->minuslist);
690     pEntry = FindList(pEntry, uEntryName);
691
692     /* Found the item already in the list. */
693     if (pEntry) {
694         pEntry->rights = nEntryRights;
695         if (bNormalRights)
696             pAcl->nplus -= PruneList(&pAcl->pluslist, pAcl->dfs);
697         else
698             pAcl->nminus -= PruneList(&pAcl->minuslist, pAcl->dfs);
699         return;
700     }
701
702     /* Otherwise we make a new item and plug in the new data. */
703     pEntry = (struct AclEntry *) malloc(sizeof (struct AclEntry));
704     ASSERT(pEntry);
705
706     strcpy(pEntry->name, uEntryName);
707     pEntry->rights = nEntryRights;
708
709     if (bNormalRights) {
710         pEntry->next = pAcl->pluslist;
711         pAcl->pluslist = pEntry;
712         pAcl->nplus++;
713         if (nEntryRights == 0 || nEntryRights == -1)
714             pAcl->nplus -= PruneList(&pAcl->pluslist, pAcl->dfs);
715     }
716     else {
717         pEntry->next = pAcl->minuslist;
718         pAcl->minuslist = pEntry;
719         pAcl->nminus++;
720         if (nEntryRights == 0)
721             pAcl->nminus -= PruneList(&pAcl->minuslist, pAcl->dfs);
722     }
723 }
724
725 static LONG
726 Convert(const CString& strRights, int dfs, enum rtype *rtypep)
727 {
728     int i, len;
729     LONG mode;
730
731     *rtypep = add;      /* add rights, by default */
732
733     if (strRights == _T("read"))
734         return PRSFS_READ | PRSFS_LOOKUP;
735     if (strRights == _T("write"))
736         return PRSFS_READ | PRSFS_LOOKUP | PRSFS_INSERT | PRSFS_DELETE | PRSFS_WRITE | PRSFS_LOCK;
737     if (strRights == _T("mail"))
738         return PRSFS_INSERT | PRSFS_LOCK | PRSFS_LOOKUP;
739     if (strRights == _T("all"))
740         return PRSFS_READ | PRSFS_LOOKUP | PRSFS_INSERT | PRSFS_DELETE | PRSFS_WRITE | PRSFS_LOCK | PRSFS_ADMINISTER;
741
742     if (strRights == _T("none")) {
743         *rtypep = destroy; /* Remove entire entry */
744         return 0;
745     }
746
747     len = strRights.GetLength();
748     mode = 0;
749
750     for (i = 0; i < len; i++) {
751         TCHAR c = strRights[i];
752         if (c == _T('r')) mode |= PRSFS_READ;
753         else if (c == _T('l')) mode |= PRSFS_LOOKUP;
754         else if (c == _T('i')) mode |= PRSFS_INSERT;
755         else if (c == _T('d')) mode |= PRSFS_DELETE;
756         else if (c == _T('w')) mode |= PRSFS_WRITE;
757         else if (c == _T('k')) mode |= PRSFS_LOCK;
758         else if (c == _T('a')) mode |= PRSFS_ADMINISTER;
759         else {
760             fprintf(stderr, "illegal rights character '%c'.\n", c);
761             exit(1);
762         }
763     }
764     return mode;
765 }
766
767 BOOL
768 SaveACL(const CString& strCellName, const CString& strDir, const CStringArray& normal, const CStringArray& negative)
769 {
770     LONG code;
771     struct ViceIoctl blob;
772     struct Acl *pAcl;
773     LONG rights;
774     enum rtype rtype;
775
776     HOURGLASS hourglass;
777
778     // Create a new ACL
779     pAcl = EmptyAcl(strCellName);
780
781     // Set its normal rights
782     int i;
783     for (i = 0; i < normal.GetSize(); i += 2) {
784         rights = Convert(normal[i + 1], 0, &rtype);
785         ChangeList(pAcl, TRUE, normal[i], rights);
786     }
787
788     // Set its negative rights
789     for (i = 0; i < negative.GetSize(); i += 2) {
790         rights = Convert(negative[i + 1], 0, &rtype);
791         ChangeList(pAcl, FALSE, negative[i], rights);
792     }
793
794     // Write the ACL
795     blob.in = AclToString(pAcl);
796     blob.out_size = 0;
797     blob.in_size = 1 + strlen((const char *)blob.in);
798
799     code = pioctl_T(strDir, VIOCSETAL, &blob, 1);
800     if (code) {
801         if (errno == EINVAL)
802             ShowMessageBox(IDS_SAVE_ACL_EINVAL_ERROR, MB_ICONERROR, IDS_SAVE_ACL_EINVAL_ERROR, strDir);
803         else
804             ShowMessageBox(IDS_SAVE_ACL_ERROR, MB_ICONERROR, IDS_SAVE_ACL_ERROR, strDir, GetAfsError(errno, strDir));
805     }
806
807     ZapAcl(pAcl);
808
809     return (code == 0);
810 }
811
812 BOOL
813 CopyACL(const CString& strToDir, const CStringArray& normal, const CStringArray& negative, BOOL bClear)
814 {
815     LONG code;
816     struct ViceIoctl blob;
817     struct Acl *pToAcl;
818     int idf = 0; // getidf(as, parm_copyacl_id);
819     char space[AFS_PIOCTL_MAXSIZE];
820     HOURGLASS hourglass;
821
822     // Get ACL to copy to
823     blob.out_size = AFS_PIOCTL_MAXSIZE;
824     blob.in_size = idf;
825     blob.in = blob.out = space;
826
827     code = pioctl_T(strToDir, VIOCGETAL, &blob, 1);
828     if (code) {
829         ShowMessageBox(IDS_ACL_READ_ERROR, MB_ICONERROR, IDS_ACL_READ_ERROR, strToDir, GetAfsError(errno, strToDir));
830         return FALSE;
831     }
832
833     if (bClear)
834         pToAcl = EmptyAcl(space);
835     else
836         pToAcl = ParseAcl(space, AFS_PIOCTL_MAXSIZE);
837
838     if (pToAcl == NULL) {
839         ShowMessageBox(IDS_INVALID_ACL_DATA, MB_ICONERROR, IDS_INVALID_ACL_DATA);
840         return FALSE;
841     }
842
843     CleanAcl(pToAcl, NULL);
844
845     if (pToAcl->dfs) {
846         ShowMessageBox(IDS_NO_DFS_COPY_ACL, MB_ICONERROR, IDS_NO_DFS_COPY_ACL, strToDir);
847         ZapAcl(pToAcl);
848         return FALSE;
849     }
850
851     enum rtype rtype;
852
853     // Set normal rights
854     int i;
855     for (i = 0; i < normal.GetSize(); i += 2) {
856         LONG rights = Convert(normal[i + 1], 0, &rtype);
857         ChangeList(pToAcl, TRUE, normal[i], rights);
858     }
859
860     // Set negative rights
861     for (i = 0; i < negative.GetSize(); i += 2) {
862         LONG rights = Convert(negative[i + 1], 0, &rtype);
863         ChangeList(pToAcl, FALSE, normal[i], rights);
864     }
865
866     // Save modified ACL
867     blob.in = AclToString(pToAcl);
868     blob.out_size = 0;
869     blob.in_size = 1 + strlen((char *)blob.in);
870
871     code = pioctl_T(strToDir, VIOCSETAL, &blob, 1);
872     if (code) {
873         ZapAcl(pToAcl);
874         if (errno == EINVAL)
875             ShowMessageBox(IDS_COPY_ACL_EINVAL_ERROR, MB_ICONERROR, IDS_COPY_ACL_EINVAL_ERROR, strToDir);
876         else
877             ShowMessageBox(IDS_COPY_ACL_ERROR, MB_ICONERROR, IDS_COPY_ACL_ERROR, strToDir, GetAfsError(errno, strToDir));
878         return FALSE;
879     }
880
881     ZapAcl(pToAcl);
882
883     ShowMessageBox(IDS_COPY_ACL_OK, MB_OK|MB_ICONINFORMATION, IDS_COPY_ACL_OK);
884
885     return TRUE;
886 }
887
888 CString
889 ParseMountPoint(const CString strFile, CString strMountPoint)
890 {
891     CString strType;
892     CString strVolume;
893     CString strCell;
894     CString strMountPointInfo;
895
896     if (strMountPoint[0] == '#')
897         strType = "Regular";
898     else if (strMountPoint[0] == '%')
899         strType = "Read/Write";
900
901     int nColon = strMountPoint.Find(':');
902     if (nColon >= 0) {
903         strCell = strMountPoint.Mid(1, nColon - 1);
904         strVolume = strMountPoint.Mid(nColon + 1);
905     } else
906         strVolume = strMountPoint.Mid(1);
907
908     strMountPointInfo = strFile + _T("\t") + strVolume + _T("\t") + strCell + _T("\t") + strType;
909
910     return strMountPointInfo;
911 }
912
913 CString
914 ParseSymlink(const CString strFile, CString strSymlink)
915 {
916     CString strSymlinkInfo;
917
918     strSymlinkInfo = strFile + _T("\t") + strSymlink;
919
920     return strSymlinkInfo;
921 }
922
923 BOOL
924 IsPathInAfs(const CString & strPath)
925 {
926     struct ViceIoctl blob;
927     cm_ioctlQueryOptions_t options;
928     cm_fid_t fid;
929     int code;
930
931     HOURGLASS hourglass;
932
933     memset(&options, 0, sizeof(options));
934     options.size = sizeof(options);
935     options.field_flags |= CM_IOCTL_QOPTS_FIELD_LITERAL;
936     options.literal = 1;
937     blob.in_size = options.size;    /* no variable length data */
938     blob.in = &options;
939     blob.out_size = sizeof(cm_fid_t);
940     blob.out = (char *) &fid;
941
942     code = pioctl_T(strPath, VIOCGETFID, &blob, 1);
943     if (code) {
944         if ((errno == EINVAL) || (errno == ENOENT))
945         return FALSE;
946     }
947     return TRUE;
948 }
949
950 static int
951 IsFreelanceRoot(const CString& apath)
952 {
953     struct ViceIoctl blob;
954     afs_int32 code;
955     char space[AFS_PIOCTL_MAXSIZE];
956
957     blob.in_size = 0;
958     blob.out_size = AFS_PIOCTL_MAXSIZE;
959     blob.out = space;
960
961     code = pioctl_T(apath, VIOC_FILE_CELL_NAME, &blob, 1);
962     if (code == 0)
963         return !strcmp("Freelance.Local.Root",space);
964     return 1;   /* assume it is because it is more restrictive that way */
965 }
966
967 static const char *
968 NetbiosName(void)
969 {
970     static char buffer[1024] = "AFS";
971     HKEY  parmKey;
972     DWORD code;
973     DWORD dummyLen;
974     DWORD enabled = 0;
975
976     code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
977                          0, (IsWow64()?KEY_WOW64_64KEY:0)|KEY_QUERY_VALUE, &parmKey);
978     if (code == ERROR_SUCCESS) {
979         dummyLen = sizeof(buffer);
980         code = RegQueryValueExA(parmKey, "NetbiosName", NULL, NULL,
981                                (LPBYTE)buffer, &dummyLen);
982         RegCloseKey (parmKey);
983     } else {
984         strcpy(buffer, "AFS");
985     }
986     return buffer;
987 }
988
989 static void
990 FixNetbiosPath(CString& path)
991 {
992     if (!IsPathInAfs(path)) {
993         CString nbroot;
994         const char * nbname = NetbiosName();
995
996 #ifdef UNICODE
997         nbroot.Format(_T("\\\\%S\\"), nbname);
998 #else
999         nbroot.Format(_T("\\\\%s\\"), nbname);
1000 #endif
1001
1002         if (nbroot.CompareNoCase(path) == 0) {
1003             path.Append(_T("all\\"));
1004         }
1005     }
1006 }
1007
1008 #define AFSCLIENT_ADMIN_GROUPNAME "AFS Client Admins"
1009
1010 static BOOL
1011 IsAdmin (void)
1012 {
1013     static BOOL fAdmin = FALSE;
1014     static BOOL fTested = FALSE;
1015
1016     if (!fTested)
1017     {
1018         /* Obtain the SID for the AFS client admin group.  If the group does
1019          * not exist, then assume we have AFS client admin privileges.
1020          */
1021         PSID psidAdmin = NULL;
1022         DWORD dwSize, dwSize2;
1023         TCHAR pszAdminGroup[ MAX_COMPUTERNAME_LENGTH + sizeof(AFSCLIENT_ADMIN_GROUPNAME) + 2 ];
1024         TCHAR *pszRefDomain = NULL;
1025         SID_NAME_USE snu = SidTypeGroup;
1026
1027         dwSize = sizeof(pszAdminGroup);
1028
1029         if (!GetComputerName(pszAdminGroup, &dwSize)) {
1030             /* Can't get computer name.  We return false in this case.
1031                Retain fAdmin and fTested. This shouldn't happen.*/
1032             return FALSE;
1033         }
1034
1035         dwSize = 0;
1036         dwSize2 = 0;
1037
1038         lstrcat(pszAdminGroup, _T("\\"));
1039         lstrcat(pszAdminGroup, _T(AFSCLIENT_ADMIN_GROUPNAME));
1040
1041         LookupAccountName(NULL, pszAdminGroup, NULL, &dwSize, NULL, &dwSize2, &snu);
1042         /* that should always fail. */
1043
1044         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
1045             /* if we can't find the group, then we allow the operation */
1046             fAdmin = TRUE;
1047             return TRUE;
1048         }
1049
1050         if (dwSize == 0 || dwSize2 == 0) {
1051             /* Paranoia */
1052             fAdmin = TRUE;
1053             return TRUE;
1054         }
1055
1056         psidAdmin = (PSID)malloc(dwSize); memset(psidAdmin,0,dwSize);
1057         pszRefDomain = (TCHAR *)malloc(dwSize2);
1058
1059         if (!LookupAccountName(NULL, pszAdminGroup, psidAdmin, &dwSize, pszRefDomain, &dwSize2, &snu)) {
1060             /* We can't lookup the group now even though we looked it up earlier.
1061                Could this happen? */
1062             fAdmin = TRUE;
1063         } else {
1064             /* Then open our current ProcessToken */
1065             HANDLE hToken;
1066
1067             if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken))
1068             {
1069
1070                 if (!CheckTokenMembership(hToken, psidAdmin, &fAdmin)) {
1071                     /* We'll have to allocate a chunk of memory to store the list of
1072                      * groups to which this user belongs; find out how much memory
1073                      * we'll need.
1074                      */
1075                     DWORD dwSize = 0;
1076                     PTOKEN_GROUPS pGroups;
1077
1078                     GetTokenInformation (hToken, TokenGroups, NULL, dwSize, &dwSize);
1079
1080                     pGroups = (PTOKEN_GROUPS)malloc(dwSize);
1081
1082                     /* Allocate that buffer, and read in the list of groups. */
1083                     if (GetTokenInformation (hToken, TokenGroups, pGroups, dwSize, &dwSize))
1084                     {
1085                         /* Look through the list of group SIDs and see if any of them
1086                          * matches the AFS Client Admin group SID.
1087                          */
1088                         size_t iGroup = 0;
1089                         for (; (!fAdmin) && (iGroup < pGroups->GroupCount); ++iGroup)
1090                         {
1091                             if (EqualSid (psidAdmin, pGroups->Groups[ iGroup ].Sid)) {
1092                                 fAdmin = TRUE;
1093                             }
1094                         }
1095                     }
1096
1097                     if (pGroups)
1098                         free(pGroups);
1099                 }
1100
1101                 /* if do not have permission because we were not explicitly listed
1102                  * in the Admin Client Group let's see if we are the SYSTEM account
1103                  */
1104                 if (!fAdmin) {
1105                     PTOKEN_USER pTokenUser;
1106                     SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
1107                     PSID pSidLocalSystem = 0;
1108                     DWORD gle;
1109
1110                     GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize);
1111
1112                     pTokenUser = (PTOKEN_USER)malloc(dwSize);
1113
1114                     if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize))
1115                         gle = GetLastError();
1116
1117                     if (AllocateAndInitializeSid( &SIDAuth, 1,
1118                                                   SECURITY_LOCAL_SYSTEM_RID,
1119                                                   0, 0, 0, 0, 0, 0, 0,
1120                                                   &pSidLocalSystem))
1121                     {
1122                         if (EqualSid(pTokenUser->User.Sid, pSidLocalSystem)) {
1123                             fAdmin = TRUE;
1124                         }
1125
1126                         FreeSid(pSidLocalSystem);
1127                     }
1128
1129                     if ( pTokenUser )
1130                         free(pTokenUser);
1131                 }
1132             }
1133         }
1134
1135         free(psidAdmin);
1136         free(pszRefDomain);
1137
1138         fTested = TRUE;
1139     }
1140
1141     return fAdmin;
1142 }
1143
1144 CString
1145 Parent(const CString& path)
1146 {
1147     int last_slash = path.ReverseFind(_T('\\'));
1148
1149     if (last_slash != -1) {
1150         CString ret = path.Left(last_slash + 1);
1151         return ret;
1152     } else {
1153         if (path.GetLength() >= 2 && path[1] == _T(':')) {
1154             CString ret = path.Left(2);
1155             ret.AppendChar(_T('.'));
1156             return ret;
1157         } else {
1158             CString ret = _T(".");
1159             return ret;
1160         }
1161     }
1162     }
1163
1164 CString
1165 LastComponent(const CString& path)
1166 {
1167     int last_slash = path.ReverseFind(_T('\\'));
1168
1169     if (last_slash != -1) {
1170         CString ret = path.Mid(last_slash + 1);
1171         return ret;
1172     } else {
1173         if (path.GetLength() >= 2 && path[1] == _T(':')) {
1174             CString ret = path.Mid(2);
1175             return ret;
1176         } else {
1177             CString ret = path;
1178             return ret;
1179         }
1180     }
1181 }
1182
1183 static CString
1184 GetCell(const CString & path)
1185 {
1186     char cellname[MAXCELLCHARS];
1187     afs_int32 code;
1188     struct ViceIoctl blob;
1189
1190     blob.in_size = 0;
1191     blob.out_size = sizeof(cellname);
1192     blob.out = cellname;
1193
1194     code = pioctl_T(path, VIOC_FILE_CELL_NAME, &blob, 1);
1195     if (code) {
1196         CString s;
1197         s.Empty();
1198
1199         return s;
1200     } else {
1201         return Utf8ToCString(cellname);
1202     }
1203 }
1204
1205
1206 BOOL
1207 ListMount(CStringArray& files)
1208 {
1209     LONG code;
1210     struct ViceIoctl blob;
1211     int error;
1212
1213     CString parent_dir;         /* Parent directory of true name */
1214     CStringUtf8 last_component;         /* Last component of true name */
1215
1216     CStringArray mountPoints;
1217
1218     HOURGLASS hourglass;
1219
1220     error = 0;
1221
1222     for (int i = 0; i < files.GetSize(); i++) {
1223         int last_slash = files[i].ReverseFind(_T('\\'));
1224         char space[AFS_PIOCTL_MAXSIZE];
1225
1226         if (last_slash != -1) {
1227             last_component.SetString( files[i].Mid(last_slash + 1) );
1228             parent_dir.SetString( files[i].Left(last_slash + 1) );
1229             FixNetbiosPath(parent_dir);
1230         } else {
1231             // The path is of the form "C:foo" or just "foo".  If
1232             // there is a drive, then use the current directory of
1233             // that drive.  Otherwise we just use '.'.
1234
1235             if (files[i].GetLength() >= 2 && files[i][1] == _T(':')) {
1236                 parent_dir.Format(_T("%c:."), files[i][0]);
1237                 last_component.SetString( files[i].Mid(2) );
1238             } else {
1239                 parent_dir.SetString( _T("."));
1240                 last_component.SetString( files[i] );
1241             }
1242         }
1243
1244         blob.in_size = last_component.GetLength() + 1;
1245         blob.in = last_component.GetBuffer();
1246         blob.out_size = AFS_PIOCTL_MAXSIZE;
1247         blob.out = space;
1248         memset(space, 0, AFS_PIOCTL_MAXSIZE);
1249
1250         code = pioctl_T(parent_dir, VIOC_AFS_STAT_MT_PT, &blob, 1);
1251
1252         last_component.ReleaseBuffer();
1253
1254         if (code == 0) {
1255             int nPos;
1256             space[AFS_PIOCTL_MAXSIZE - 1] = '\0';
1257             nPos = strlen(space) - 1;
1258             if (space[nPos] == '.')
1259                 space[nPos] = 0;
1260             mountPoints.Add(ParseMountPoint(StripPath(files[i]), Utf8ToCString(space)));
1261         } else {
1262             error = 1;
1263             if (errno == EINVAL)
1264                 mountPoints.Add(GetMessageString(IDS_NOT_MOUNT_POINT_ERROR, StripPath(files[i])));
1265             else
1266                 mountPoints.Add(GetMessageString(IDS_LIST_MOUNT_POINT_ERROR, GetAfsError(errno, StripPath(files[i]))));
1267         }
1268     }
1269
1270     CMountPointsDlg dlg;
1271     dlg.SetMountPoints(mountPoints);
1272     dlg.DoModal();
1273
1274     return !error;
1275 }
1276
1277 BOOL
1278 MakeMount(const CString& strDir,
1279           const CString& strVolName,
1280           const CString& strInCellName,
1281           BOOL bRW)
1282 {
1283     LONG code;
1284     struct ViceIoctl blob;
1285     HOURGLASS hourglass;
1286
1287     ASSERT(strVolName.GetLength() < 64);
1288
1289     CString strParent = Parent(strDir);
1290
1291     FixNetbiosPath(strParent);
1292     if (!IsPathInAfs(strParent)) {
1293             ShowMessageBox(IDS_MAKE_MP_NOT_AFS_ERROR, MB_ICONERROR, IDS_MAKE_MP_NOT_AFS_ERROR);
1294             return FALSE;
1295         }
1296
1297     CString strPath = strParent + LastComponent(strDir);
1298
1299     if ( IsFreelanceRoot(strParent) && !IsAdmin() ) {
1300         ShowMessageBox(IDS_NOT_AFS_CLIENT_ADMIN_ERROR, MB_ICONERROR,
1301                        IDS_NOT_AFS_CLIENT_ADMIN_ERROR);
1302             return FALSE;
1303         }
1304
1305     CString strMount;
1306
1307     strMount.Format(_T("%c%s%s%s."),
1308                     ((bRW)?_T('%'):_T('#')),
1309                     strInCellName,
1310                     ((strInCellName.IsEmpty())?_T(""):_T(":")),
1311                     strVolName);
1312
1313     CStringUtf8 ustrMount(strMount);
1314
1315     blob.out_size = 0;
1316     blob.in_size = ustrMount.GetLength() + 1;
1317     blob.in = ustrMount.GetBuffer();
1318     blob.out = NULL;
1319
1320     code = pioctl_T(strPath, VIOC_AFS_CREATE_MT_PT, &blob, 0);
1321
1322     ustrMount.ReleaseBuffer();
1323
1324     if (code) {
1325         ShowMessageBox(IDS_MOUNT_POINT_ERROR, MB_ICONERROR, IDS_MOUNT_POINT_ERROR, GetAfsError(errno, strDir));
1326         return FALSE;
1327     }
1328
1329     return TRUE;
1330 }
1331
1332 BOOL
1333 RemoveSymlink(const CString& strName)
1334 {
1335     BOOL error = FALSE;
1336     INT code=0;
1337     struct ViceIoctl blob;
1338     char lsbuffer[1024];
1339
1340     HOURGLASS hourglass;
1341
1342     CString strParent = Parent(strName);
1343     CStringUtf8 ustrLast(LastComponent(strName));
1344     FixNetbiosPath(strParent);
1345
1346     if ( IsFreelanceRoot(strParent) && !IsAdmin() ) {
1347         ShowMessageBox(IDS_NOT_AFS_CLIENT_ADMIN_ERROR, MB_ICONERROR, IDS_NOT_AFS_CLIENT_ADMIN_ERROR);
1348         return FALSE;
1349     }
1350
1351     blob.in_size = ustrLast.GetLength() + 1;
1352     blob.in = ustrLast.GetBuffer();
1353     blob.out = lsbuffer;
1354     blob.out_size = sizeof(lsbuffer);
1355     code = pioctl_T(strParent, VIOC_LISTSYMLINK, &blob, 0);
1356     ustrLast.ReleaseBuffer();
1357     if (code)
1358         return FALSE;
1359     blob.out_size = 0;
1360     blob.in_size = ustrLast.GetLength() + 1;
1361     blob.in = ustrLast.GetBuffer();
1362
1363     code = pioctl_T(strParent, VIOC_DELSYMLINK, &blob, 0);
1364
1365     ustrLast.ReleaseBuffer();
1366
1367     return (code == 0);
1368 }
1369
1370 BOOL
1371 IsSymlink(const CString& strName)
1372 {
1373     struct ViceIoctl blob;
1374     int code;
1375     char space[AFS_PIOCTL_MAXSIZE];
1376     HOURGLASS hourglass;
1377
1378     CStringUtf8 ustrLast(LastComponent(strName));
1379     CString strParent = Parent(strName);
1380
1381     FixNetbiosPath(strParent);
1382
1383     blob.in_size = ustrLast.GetLength() + 1;
1384     blob.in = ustrLast.GetBuffer();
1385     blob.out_size = AFS_PIOCTL_MAXSIZE;
1386     blob.out = space;
1387     memset(space, 0, AFS_PIOCTL_MAXSIZE);
1388
1389     code = pioctl_T(strParent, VIOC_LISTSYMLINK, &blob, 1);
1390
1391     ustrLast.ReleaseBuffer();
1392
1393     return (code==0);
1394 }
1395
1396
1397 BOOL
1398 IsMountPoint(const CString& path)
1399 {
1400     LONG code = 0;
1401     struct ViceIoctl blob;
1402     char lsbuffer[1024];
1403
1404     HOURGLASS hourglass;
1405
1406     CString parent = Parent(path);
1407     FixNetbiosPath(parent);
1408
1409     CStringUtf8 mountpoint(LastComponent(path));
1410
1411     blob.in_size = mountpoint.GetLength() + 1;
1412     blob.in = mountpoint.GetBuffer();
1413     blob.out = lsbuffer;
1414     blob.out_size = sizeof(lsbuffer);
1415
1416     code = pioctl_T(parent, VIOC_AFS_STAT_MT_PT, &blob, 0);
1417
1418     mountpoint.ReleaseBuffer();
1419
1420     return (code==0);
1421 }
1422
1423
1424 /*
1425  * Delete AFS mount points.  Variables are used as follows:
1426  *       tbuffer: Set to point to the null-terminated directory name of the mount point
1427  *          (or ``.'' if none is provided)
1428  *      tp: Set to point to the actual name of the mount point to nuke.
1429  */
1430 BOOL
1431 RemoveMount(CStringArray& files)
1432 {
1433     LONG code = 0;
1434     struct ViceIoctl blob;
1435     BOOL error = FALSE;
1436     CStringArray results;
1437     CString str;
1438     CString str2;
1439
1440     HOURGLASS hourglass;
1441
1442     for (int i = 0; i < files.GetSize(); i++) {
1443         if (!IsMountPoint(files[i])) {
1444             error = TRUE;
1445             if (errno == EINVAL)
1446                 results.Add(GetMessageString(IDS_NOT_MOUNT_POINT_ERROR, StripPath(files[i])));
1447             else
1448                 results.Add(GetMessageString(IDS_ERROR, GetAfsError(errno, StripPath(files[i]))));
1449             continue;   // don't bother trying
1450         }
1451
1452         CString parent = Parent(files[i]);
1453         CStringUtf8 mountpoint(LastComponent(files[i]));
1454         FixNetbiosPath(parent);
1455
1456         if ( IsFreelanceRoot(parent) && !IsAdmin() ) {
1457             results.Add(GetMessageString(IDS_NOT_AFS_CLIENT_ADMIN_ERROR, StripPath(files[i])));
1458             error = TRUE;
1459             continue;   /* skip */
1460         }
1461
1462         blob.out_size = 0;
1463         blob.in_size = mountpoint.GetLength() + 1;
1464         blob.in = mountpoint.GetBuffer();
1465
1466         code = pioctl_T(parent, VIOC_AFS_DELETE_MT_PT, &blob, 0);
1467
1468         mountpoint.ReleaseBuffer();
1469
1470         if (code) {
1471             error = TRUE;
1472             results.Add(GetMessageString(IDS_ERROR, GetAfsError(errno, StripPath(files[i]))));
1473         } else
1474             results.Add(GetMessageString(IDS_DELETED));
1475     }
1476
1477     LoadString (str, IDS_REMOVE_MP);
1478     LoadString (str2, IDS_REMOVE_MP_COLUMN);
1479     CResultsDlg dlg(REMOVE_MOUNT_POINTS_HELP_ID);
1480     dlg.SetContents(str, str2, StripPath(files), results);
1481     dlg.DoModal();
1482
1483     return !error;
1484 }
1485
1486 BOOL
1487 GetVolumeInfo(CString strFile, CVolInfo& volInfo)
1488 {
1489     LONG code;
1490     struct ViceIoctl blob;
1491     struct VolumeStatus *status;
1492     char *name;
1493     char space[AFS_PIOCTL_MAXSIZE];
1494     HOURGLASS hourglass;
1495
1496     volInfo.m_strFilePath = strFile;
1497     volInfo.m_strFileName = StripPath(strFile);
1498
1499     /*
1500         volInfo.m_strName = "VolumeName";
1501         volInfo.m_nID = 10;
1502         volInfo.m_nQuota = 20 * 1024 * 1024;
1503         volInfo.m_nNewQuota = volInfo.m_nQuota;
1504         volInfo.m_nUsed = volInfo.m_nQuota / 2;
1505         volInfo.m_nPartSize = 50 * 1024 * 1024;
1506         volInfo.m_nPartFree = 30 * 1024 * 1024;
1507         volInfo.m_nDup = -1;
1508         return TRUE;
1509      */
1510
1511     blob.out_size = AFS_PIOCTL_MAXSIZE;
1512     blob.in_size = 0;
1513     blob.out = space;
1514
1515     code = pioctl_T(strFile, VIOCGETVOLSTAT, &blob, 1);
1516     if (code || blob.out_size < sizeof(*status)) {
1517         volInfo.m_strErrorMsg = GetAfsError(errno, strFile);
1518         return FALSE;
1519     }
1520
1521     status = (VolumeStatus *)space;
1522     name = (char *)status + sizeof(*status);
1523
1524     volInfo.m_strName = Utf8ToCString(name);
1525     volInfo.m_nID = status->Vid;
1526     volInfo.m_nQuota = status->MaxQuota;
1527     volInfo.m_nNewQuota = status->MaxQuota;
1528     volInfo.m_nUsed = status->BlocksInUse;
1529     volInfo.m_nPartSize = status->PartMaxBlocks;
1530     volInfo.m_nPartFree = status->PartBlocksAvail;
1531     volInfo.m_nDup = -1;
1532
1533     return TRUE;
1534 }
1535
1536 BOOL
1537 SetVolInfo(CVolInfo& volInfo)
1538 {
1539     LONG code;
1540     struct ViceIoctl blob;
1541     struct VolumeStatus *status;
1542     char *input;
1543     char space[AFS_PIOCTL_MAXSIZE];
1544     HOURGLASS hourglass;
1545
1546     blob.out_size = AFS_PIOCTL_MAXSIZE;
1547     blob.in_size = sizeof(*status) + 3; /* for the three terminating nulls */
1548     blob.out = space;
1549     blob.in = space;
1550
1551     status = (VolumeStatus *)space;
1552     status->MinQuota = -1;
1553     status->MaxQuota = volInfo.m_nNewQuota;
1554
1555     input = (char *)status + sizeof(*status);
1556     *(input++) = '\0';  /* never set name: this call doesn't change vldb */
1557     *(input++) = '\0';  // No offmsg
1558     *(input++) = '\0';  // No motd
1559
1560 #ifdef LOGGING_ON
1561     FILE *fp = OpenFile(szLogFileName, "a");
1562     if (fp) {
1563         fprintf(fp, "\nSetVolInfo() pioctl parms:\n");
1564         fprintf(fp, "\tpathp = %s\n\topcode = VIOCSETVOLSTAT (%d)\n\tblobp = %ld\n", PCCHAR(volInfo.m_strFilePath), VIOCSETVOLSTAT, &blob);
1565         fprintf(fp, "\t\tblobp.in = %ld (VolumeStatus *status)\n\t\tblobp.in_size = %ld\n\t\tblobp.out = %ld ((VolumeStatus *status))\n\t\tblobp.out_size = %ld\n", blob.in, blob.in_size, blob.out, blob.out_size);
1566         fprintf(fp, "\t\t\tstatus->MinQuota = %ld\n", status->MinQuota);
1567         fprintf(fp, "\t\t\tstatus->MaxQuota = %ld\n", status->MaxQuota);
1568         fprintf(fp, "\t\t\tOther status fields aren't set\n");
1569         fprintf(fp, "\t\t\t3 nulls follow the VolumeStatus structure.\n");
1570         fprintf(fp, "\tfollow = 1\n");
1571         fclose(fp);
1572     }
1573 #endif
1574
1575     code = pioctl_T(volInfo.m_strFilePath, VIOCSETVOLSTAT, &blob, 1);
1576     if (code || blob.out_size < sizeof(*status)) {
1577         ShowMessageBox(IDS_SET_QUOTA_ERROR, MB_ICONERROR, IDS_SET_QUOTA_ERROR, GetAfsError(errno, volInfo.m_strName));
1578         return FALSE;
1579     }
1580
1581     return TRUE;
1582 }
1583
1584 void
1585 GetCellName(const CString& cellNamep, struct afsconf_cell *infop)
1586 {
1587     CStringUtf8 uCellName(cellNamep);
1588
1589     StringCbCopyA(infop->name, sizeof(infop->name), uCellName);
1590 }
1591
1592 BOOL NO_CALLER
1593 CheckServers(const CString& strCellName, WHICH_CELLS nCellsToCheck, BOOL bFast)
1594 {
1595     LONG code;
1596     struct ViceIoctl blob;
1597     LONG j;
1598     LONG temp = 0;
1599     struct afsconf_cell info;
1600     struct chservinfo checkserv;
1601     char space[AFS_PIOCTL_MAXSIZE];
1602     HOURGLASS hourglass;
1603
1604     memset(&checkserv, 0, sizeof(struct chservinfo));
1605     blob.in_size = sizeof(struct chservinfo);
1606     blob.in = (caddr_t)&checkserv;
1607
1608     blob.out_size = AFS_PIOCTL_MAXSIZE;
1609     blob.out = space;
1610     memset(space, 0, sizeof(afs_int32));        /* so we assure zero when nothing is copied back */
1611
1612     if (nCellsToCheck == SPECIFIC_CELL) {
1613         temp = 2;
1614         GetCellName(strCellName, &info);
1615         strcpy(checkserv.tbuffer,info.name);
1616         checkserv.tsize = strlen(info.name) + 1;
1617     } else {
1618         if (nCellsToCheck != ALL_CELLS)
1619             temp = 2;
1620         strcpy(checkserv.tbuffer, "\0");
1621         checkserv.tsize = 0;
1622     }
1623     if (bFast)
1624         temp |= 1;      /* set fast flag */
1625
1626     checkserv.magic = 0x12345678;       /* XXX */
1627     checkserv.tflags = temp;
1628     checkserv.tinterval = -1;   /* don't change current interval */
1629
1630     code = pioctl_utf8(0, VIOCCKSERV, &blob, 1);
1631     if (code) {
1632         ShowMessageBox(IDS_CHECK_SERVERS_ERROR, MB_ICONERROR, IDS_CHECK_SERVERS_ERROR, GetAfsError(errno, CString()));
1633         return FALSE;
1634     }
1635
1636     memcpy(&temp, space, sizeof(LONG));
1637
1638     if (temp == 0) {
1639         ShowMessageBox(IDS_ALL_SERVERS_RUNNING, MB_OK|MB_ICONINFORMATION, IDS_ALL_SERVERS_RUNNING);
1640         return TRUE;
1641     }
1642
1643     CStringArray servers;
1644     for (j = 0; j < AFS_MAXHOSTS; j++) {
1645         memcpy(&temp, space + j * sizeof(LONG), sizeof(LONG));
1646         if (temp == 0)
1647             break;
1648
1649         char *name = hostutil_GetNameByINet(temp);
1650         servers.Add(name);
1651     }
1652
1653     CDownServersDlg dlg;
1654     dlg.SetServerNames(servers);
1655     dlg.DoModal();
1656
1657     return TRUE;
1658 }
1659
1660 BOOL
1661 GetTokenInfo(CStringArray& tokenInfo)
1662 {
1663     int cellNum;
1664     int rc;
1665     time_t current_time;
1666     time_t tokenExpireTime;
1667     char *expireString;
1668     char userName[100];
1669 //      char s[100];
1670     struct ktc_principal serviceName, clientName;
1671     struct ktc_token token;
1672
1673     CString strTokenInfo;
1674     CString strUserName;
1675     CString strCellName;
1676     CString strExpir;
1677
1678 //      tokenInfo.Add("");
1679 //      return TRUE;
1680
1681
1682     HOURGLASS hourglass;
1683
1684 //      printf("\nTokens held by the Cache Manager:\n\n");
1685     cellNum = 0;
1686     current_time = time(0);
1687
1688     while (1) {
1689         rc = ktc_ListTokens(cellNum, &cellNum, &serviceName);
1690         if (rc == KTC_NOENT) {
1691             /* end of list */
1692 //          printf("   --End of list --\n");
1693             break;
1694         }
1695         else if (rc == KTC_NOCM) {
1696             ShowMessageBox(IDS_GET_TOKENS_NO_AFS_SERVICE);
1697 //          printf("AFS service may not have started\n");
1698             break;
1699         }
1700         else if (rc) {
1701             ShowMessageBox(IDS_GET_TOKENS_UNEXPECTED_ERROR, MB_ICONERROR, IDS_GET_TOKENS_UNEXPECTED_ERROR, rc);
1702             return FALSE;
1703 //          printf("Unexpected error, code %d\n", rc);
1704 //          exit(1);
1705         }
1706         else {
1707             rc = ktc_GetToken(&serviceName, &token, sizeof(token), &clientName);
1708             if (rc) {
1709                 ShowMessageBox(IDS_GET_TOKENS_UNEXPECTED_ERROR2, MB_ICONERROR, IDS_GET_TOKENS_UNEXPECTED_ERROR2,
1710                                 serviceName.name, serviceName.instance, serviceName.cell, rc);
1711                 continue;
1712             }
1713
1714             tokenExpireTime = token.endTime;
1715
1716             strcpy(userName, clientName.name);
1717             if (clientName.instance[0] != 0) {
1718                 strcat(userName, ".");
1719                 strcat(userName, clientName.instance);
1720             }
1721
1722             BOOL bShowName = FALSE;
1723
1724             if (userName[0] == '\0')
1725                 ; //printf("Tokens");
1726 // AFS ID is not returned at this time.
1727 //          else if (strncmp(userName, "AFS ID", 6) == 0)
1728 //              printf("User's (%s) tokens", userName);
1729 //              sscanf(userName, "(AFS ID %s)", szAfsID);
1730             else if (strncmp(userName, "Unix UID", 8) == 0)
1731                 ; //printf("Tokens");
1732             else
1733                 strUserName = userName;
1734 //              printf("User %s's tokens", userName);
1735
1736 //              printf(" for %s%s%s@%s ", serviceName.name, serviceName.instance[0] ? "." : "", serviceName.instance, serviceName.cell);
1737             strCellName = serviceName.cell;
1738
1739             if (tokenExpireTime <= current_time)
1740                 strExpir = "[>> Expired <<]";
1741 //              printf("[>> Expired <<]\n");
1742             else {
1743                 expireString = ctime(&tokenExpireTime);
1744                 expireString += 4;       /* Skip day of week */
1745                 expireString[12] = '\0'; /* Omit secs & year */
1746 //              printf("[Expires %s]\n", expireString);
1747 #ifdef UNICODE
1748                 strExpir.Format(_T("%S"), expireString);
1749 #else
1750                 strExpir.Format(_T("%s"), expireString);
1751 #endif
1752             }
1753
1754             strTokenInfo = strUserName + "\t" + strCellName + "\t" + strExpir + "\t" + strCellName;
1755             tokenInfo.Add(strTokenInfo);
1756         }
1757     }
1758
1759 //      printf("Press <Enter> or <Return> when finished: ");
1760 //      gets(s);
1761     return TRUE;
1762 }
1763
1764 UINT
1765 MakeSymbolicLink(const CString& strName, const CString& strTarget)
1766 {
1767     struct ViceIoctl blob;
1768     UINT code;
1769     HOURGLASS hourglass;
1770
1771     CString strParent = Parent(strName);
1772     FixNetbiosPath(strParent);
1773
1774     if ( IsFreelanceRoot(strParent) && !IsAdmin() ) {
1775         ShowMessageBox(IDS_NOT_AFS_CLIENT_ADMIN_ERROR, MB_ICONERROR, IDS_NOT_AFS_CLIENT_ADMIN_ERROR);
1776         return FALSE;
1777     }
1778
1779     CStringUtf8 ustrTarget(strTarget);
1780
1781     blob.in_size = ustrTarget.GetLength() + 1;
1782     blob.in = ustrTarget.GetBuffer();
1783     blob.out_size = 0;
1784     blob.out = NULL;
1785
1786     code = pioctl_T(strName, VIOC_SYMLINK, &blob, 0);
1787
1788     ustrTarget.ReleaseBuffer();
1789
1790     if (code != 0)
1791         return code;
1792     return 0;
1793 }
1794
1795 void NO_CALLER
1796 ListSymbolicLinkPath(const char *strName,char *strPath,UINT nlenPath)
1797 {
1798     ASSERT(nlenPath<MAX_PATH);
1799     struct ViceIoctl blob;
1800     char orig_name[MAX_PATH+1];         /*Original name, may be modified*/
1801     char true_name[MAX_PATH+1];         /*``True'' dirname (e.g., symlink target)*/
1802     char parent_dir[MAX_PATH+1];                /*Parent directory of true name*/
1803     char *last_component;       /*Last component of true name*/
1804     UINT code;
1805     char space[AFS_PIOCTL_MAXSIZE];
1806     HOURGLASS hourglass;
1807
1808     strcpy(orig_name, strName);
1809     strcpy(true_name, orig_name);
1810     /*
1811      * Find rightmost slash, if any.
1812      */
1813     last_component = (char *) strrchr(true_name, '\\');
1814     if (!last_component)
1815         last_component = (char *) strrchr(true_name, '/');
1816     if (last_component) {
1817         /*
1818          * Found it.  Designate everything before it as the parent directory,
1819          * everything after it as the final component.
1820          */
1821         strncpy(parent_dir, true_name, last_component - true_name + 1);
1822         parent_dir[last_component - true_name + 1] = 0;
1823         last_component++;   /*Skip the slash*/
1824
1825         if (!IsPathInAfs(parent_dir)) {
1826             const char * nbname = NetbiosName();
1827             int len = strlen(nbname);
1828
1829             if (parent_dir[0] == '\\' && parent_dir[1] == '\\' &&
1830                  parent_dir[len+2] == '\\' &&
1831                  parent_dir[len+3] == '\0' &&
1832                  !strnicmp(nbname,&parent_dir[2],len))
1833             {
1834                 sprintf(parent_dir,"\\\\%s\\all\\", nbname);
1835             }
1836         }
1837     }
1838     else {
1839         /*
1840          * No slash appears in the given file name.  Set parent_dir to the current
1841          * directory, and the last component as the given name.
1842          */
1843         fs_ExtractDriveLetter(true_name, parent_dir);
1844         strcat(parent_dir, ".");
1845         last_component = true_name;
1846         fs_StripDriveLetter(true_name, true_name, sizeof(true_name));
1847     }
1848     blob.in = last_component;
1849     blob.in_size = strlen(last_component)+1;
1850     blob.out_size = AFS_PIOCTL_MAXSIZE;
1851     blob.out = space;
1852     memset(space, 0, AFS_PIOCTL_MAXSIZE);
1853     if ((code = pioctl(parent_dir, VIOC_LISTSYMLINK, &blob, 1)))
1854         strcpy(space,"???");
1855     ASSERT(strlen(space)<MAX_PATH);
1856     strncpy(strPath,space,nlenPath);
1857 }
1858
1859 BOOL NO_CALLER
1860 ListSymlink(CStringArray& files)
1861 {
1862     LONG code;
1863     struct ViceIoctl blob;
1864     int error;
1865     CStringArray symlinks;
1866     char space[AFS_PIOCTL_MAXSIZE];
1867     HOURGLASS hourglass;
1868
1869     error = 0;
1870
1871     for (int i = 0; i < files.GetSize(); i++) {
1872
1873         CString strParent = Parent(files[i]);
1874         CStringUtf8 ustrLast(LastComponent(files[i]));
1875
1876         FixNetbiosPath(strParent);
1877
1878         blob.in_size = ustrLast.GetLength() + 1;
1879         blob.in = ustrLast.GetBuffer();
1880         blob.out_size = AFS_PIOCTL_MAXSIZE;
1881         blob.out = space;
1882         memset(space, 0, AFS_PIOCTL_MAXSIZE);
1883
1884         code = pioctl_T(strParent, VIOC_LISTSYMLINK, &blob, 1);
1885
1886         ustrLast.ReleaseBuffer();
1887
1888         if (code == 0) {
1889             CString syml;
1890             int len;
1891
1892             space[AFS_PIOCTL_MAXSIZE - 1] = '\0';
1893             syml = Utf8ToCString(space);
1894             len = syml.GetLength();
1895
1896             if (len > 0) {
1897                 if (syml[len - 1] == _T('.'))
1898                     syml.Truncate(len - 1);
1899             }
1900
1901             symlinks.Add(ParseSymlink(StripPath(files[i]), syml));
1902
1903         } else {
1904             error = 1;
1905             if (errno == EINVAL)
1906                 symlinks.Add(GetMessageString(IDS_NOT_SYMLINK_ERROR, StripPath(files[i])));
1907             else
1908                 symlinks.Add(GetMessageString(IDS_LIST_MOUNT_POINT_ERROR, GetAfsError(errno, StripPath(files[i]))));
1909         }
1910     }
1911
1912     CSymlinksDlg dlg;
1913     dlg.SetSymlinks(symlinks);
1914     dlg.DoModal();
1915
1916     return !error;
1917 }
1918