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