Windows: Explorer shell GetUnixModeBits()
[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         nbroot.Format(_T("\\\\%s\\"), nbname);
1031
1032         if (nbroot.CompareNoCase(path) == 0) {
1033             path.Append(_T("all\\"));
1034         }
1035     }
1036 }
1037
1038 #define AFSCLIENT_ADMIN_GROUPNAME "AFS Client Admins"
1039
1040 static BOOL
1041 IsAdmin (void)
1042 {
1043     static BOOL fAdmin = FALSE;
1044     static BOOL fTested = FALSE;
1045
1046     if (!fTested)
1047     {
1048         /* Obtain the SID for the AFS client admin group.  If the group does
1049          * not exist, then assume we have AFS client admin privileges.
1050          */
1051         PSID psidAdmin = NULL;
1052         DWORD dwSize, dwSize2;
1053         TCHAR pszAdminGroup[ MAX_COMPUTERNAME_LENGTH + sizeof(AFSCLIENT_ADMIN_GROUPNAME) + 2 ];
1054         TCHAR *pszRefDomain = NULL;
1055         SID_NAME_USE snu = SidTypeGroup;
1056
1057         dwSize = sizeof(pszAdminGroup);
1058
1059         if (!GetComputerName(pszAdminGroup, &dwSize)) {
1060             /* Can't get computer name.  We return false in this case.
1061                Retain fAdmin and fTested. This shouldn't happen.*/
1062             return FALSE;
1063         }
1064
1065         dwSize = 0;
1066         dwSize2 = 0;
1067
1068         lstrcat(pszAdminGroup, _T("\\"));
1069         lstrcat(pszAdminGroup, _T(AFSCLIENT_ADMIN_GROUPNAME));
1070
1071         LookupAccountName(NULL, pszAdminGroup, NULL, &dwSize, NULL, &dwSize2, &snu);
1072         /* that should always fail. */
1073
1074         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
1075             /* if we can't find the group, then we allow the operation */
1076             fAdmin = TRUE;
1077             return TRUE;
1078         }
1079
1080         if (dwSize == 0 || dwSize2 == 0) {
1081             /* Paranoia */
1082             fAdmin = TRUE;
1083             return TRUE;
1084         }
1085
1086         psidAdmin = (PSID)malloc(dwSize); memset(psidAdmin,0,dwSize);
1087         pszRefDomain = (TCHAR *)malloc(dwSize2);
1088
1089         if (!LookupAccountName(NULL, pszAdminGroup, psidAdmin, &dwSize, pszRefDomain, &dwSize2, &snu)) {
1090             /* We can't lookup the group now even though we looked it up earlier.
1091                Could this happen? */
1092             fAdmin = TRUE;
1093         } else {
1094             /* Then open our current ProcessToken */
1095             HANDLE hToken;
1096
1097             if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken))
1098             {
1099
1100                 if (!CheckTokenMembership(hToken, psidAdmin, &fAdmin)) {
1101                     /* We'll have to allocate a chunk of memory to store the list of
1102                      * groups to which this user belongs; find out how much memory
1103                      * we'll need.
1104                      */
1105                     DWORD dwSize = 0;
1106                     PTOKEN_GROUPS pGroups;
1107
1108                     GetTokenInformation (hToken, TokenGroups, NULL, dwSize, &dwSize);
1109
1110                     pGroups = (PTOKEN_GROUPS)malloc(dwSize);
1111
1112                     /* Allocate that buffer, and read in the list of groups. */
1113                     if (GetTokenInformation (hToken, TokenGroups, pGroups, dwSize, &dwSize))
1114                     {
1115                         /* Look through the list of group SIDs and see if any of them
1116                          * matches the AFS Client Admin group SID.
1117                          */
1118                         size_t iGroup = 0;
1119                         for (; (!fAdmin) && (iGroup < pGroups->GroupCount); ++iGroup)
1120                         {
1121                             if (EqualSid (psidAdmin, pGroups->Groups[ iGroup ].Sid)) {
1122                                 fAdmin = TRUE;
1123                             }
1124                         }
1125                     }
1126
1127                     if (pGroups)
1128                         free(pGroups);
1129                 }
1130
1131                 /* if do not have permission because we were not explicitly listed
1132                  * in the Admin Client Group let's see if we are the SYSTEM account
1133                  */
1134                 if (!fAdmin) {
1135                     PTOKEN_USER pTokenUser;
1136                     SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
1137                     PSID pSidLocalSystem = 0;
1138                     DWORD gle;
1139
1140                     GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize);
1141
1142                     pTokenUser = (PTOKEN_USER)malloc(dwSize);
1143
1144                     if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize))
1145                         gle = GetLastError();
1146
1147                     if (AllocateAndInitializeSid( &SIDAuth, 1,
1148                                                   SECURITY_LOCAL_SYSTEM_RID,
1149                                                   0, 0, 0, 0, 0, 0, 0,
1150                                                   &pSidLocalSystem))
1151                     {
1152                         if (EqualSid(pTokenUser->User.Sid, pSidLocalSystem)) {
1153                             fAdmin = TRUE;
1154                         }
1155
1156                         FreeSid(pSidLocalSystem);
1157                     }
1158
1159                     if ( pTokenUser )
1160                         free(pTokenUser);
1161                 }
1162             }
1163         }
1164
1165         free(psidAdmin);
1166         free(pszRefDomain);
1167
1168         fTested = TRUE;
1169     }
1170
1171     return fAdmin;
1172 }
1173
1174 CString
1175 Parent(const CString& path)
1176 {
1177     int last_slash = path.ReverseFind(_T('\\'));
1178
1179     if (last_slash != -1) {
1180         CString ret = path.Left(last_slash + 1);
1181         return ret;
1182     } else {
1183         if (path.GetLength() >= 2 && path[1] == _T(':')) {
1184             CString ret = path.Left(2);
1185             ret.AppendChar(_T('.'));
1186             return ret;
1187         } else {
1188             CString ret = _T(".");
1189             return ret;
1190         }
1191     }
1192     }
1193
1194 CString
1195 LastComponent(const CString& path)
1196 {
1197     int last_slash = path.ReverseFind(_T('\\'));
1198
1199     if (last_slash != -1) {
1200         CString ret = path.Mid(last_slash + 1);
1201         return ret;
1202     } else {
1203         if (path.GetLength() >= 2 && path[1] == _T(':')) {
1204             CString ret = path.Mid(2);
1205             return ret;
1206         } else {
1207             CString ret = path;
1208             return ret;
1209         }
1210     }
1211 }
1212
1213 static CString
1214 GetCell(const CString & path, BOOL bFollow = TRUE);
1215
1216 static CString
1217 GetCell(const CString & path, BOOL bFollow)
1218 {
1219     char cellname[MAXCELLCHARS];
1220     afs_int32 code;
1221     struct ViceIoctl blob;
1222
1223     blob.in_size = 0;
1224     blob.out_size = sizeof(cellname);
1225     blob.out = cellname;
1226
1227     code = pioctl_T(path, VIOC_FILE_CELL_NAME, &blob, 1);
1228     if (code) {
1229         CString s;
1230         s.Empty();
1231
1232         return s;
1233     } else {
1234         return Utf8ToCString(cellname);
1235     }
1236 }
1237
1238
1239 BOOL
1240 ListMount(CStringArray& files)
1241 {
1242     LONG code;
1243     struct ViceIoctl blob;
1244     int error;
1245
1246     CString parent_dir;         /* Parent directory of true name */
1247     CStringUtf8 last_component;         /* Last component of true name */
1248
1249     CStringArray mountPoints;
1250
1251     HOURGLASS hourglass;
1252
1253     error = 0;
1254
1255     for (int i = 0; i < files.GetSize(); i++) {
1256         int last_slash = files[i].ReverseFind(_T('\\'));
1257         char space[AFS_PIOCTL_MAXSIZE];
1258
1259         if (last_slash != -1) {
1260             last_component.SetString( files[i].Mid(last_slash + 1) );
1261             parent_dir.SetString( files[i].Left(last_slash + 1) );
1262             FixNetbiosPath(parent_dir);
1263         } else {
1264             // The path is of the form "C:foo" or just "foo".  If
1265             // there is a drive, then use the current directory of
1266             // that drive.  Otherwise we just use '.'.
1267
1268             if (files[i].GetLength() >= 2 && files[i][1] == _T(':')) {
1269                 parent_dir.Format(_T("%c:."), files[i][0]);
1270                 last_component.SetString( files[i].Mid(2) );
1271             } else {
1272                 parent_dir.SetString( _T("."));
1273                 last_component.SetString( files[i] );
1274             }
1275         }
1276
1277         blob.in_size = last_component.GetLength() + 1;
1278         blob.in = last_component.GetBuffer();
1279         blob.out_size = AFS_PIOCTL_MAXSIZE;
1280         blob.out = space;
1281         memset(space, 0, AFS_PIOCTL_MAXSIZE);
1282
1283         code = pioctl_T(parent_dir, VIOC_AFS_STAT_MT_PT, &blob, 1);
1284
1285         last_component.ReleaseBuffer();
1286
1287         if (code == 0) {
1288             int nPos;
1289             space[AFS_PIOCTL_MAXSIZE - 1] = '\0';
1290             nPos = strlen(space) - 1;
1291             if (space[nPos] == '.')
1292                 space[nPos] = 0;
1293             mountPoints.Add(ParseMountPoint(StripPath(files[i]), Utf8ToCString(space)));
1294         } else {
1295             error = 1;
1296             if (errno == EINVAL)
1297                 mountPoints.Add(GetMessageString(IDS_NOT_MOUNT_POINT_ERROR, StripPath(files[i])));
1298             else
1299                 mountPoints.Add(GetMessageString(IDS_LIST_MOUNT_POINT_ERROR, GetAfsError(errno, StripPath(files[i]))));
1300         }
1301     }
1302
1303     CMountPointsDlg dlg;
1304     dlg.SetMountPoints(mountPoints);
1305     dlg.DoModal();
1306
1307     return !error;
1308 }
1309
1310 BOOL
1311 MakeMount(const CString& strDir,
1312           const CString& strVolName,
1313           const CString& strInCellName,
1314           BOOL bRW)
1315 {
1316     LONG code;
1317     struct ViceIoctl blob;
1318     HOURGLASS hourglass;
1319
1320     ASSERT(strVolName.GetLength() < 64);
1321
1322     CString strParent = Parent(strDir);
1323
1324     FixNetbiosPath(strParent);
1325     if (!IsPathInAfs(strParent)) {
1326             ShowMessageBox(IDS_MAKE_MP_NOT_AFS_ERROR, MB_ICONERROR, IDS_MAKE_MP_NOT_AFS_ERROR);
1327             return FALSE;
1328         }
1329
1330     CString strPath = strParent + LastComponent(strDir);
1331
1332     if ( IsFreelanceRoot(strParent) && !IsAdmin() ) {
1333         ShowMessageBox(IDS_NOT_AFS_CLIENT_ADMIN_ERROR, MB_ICONERROR,
1334                        IDS_NOT_AFS_CLIENT_ADMIN_ERROR);
1335             return FALSE;
1336         }
1337
1338     CString strMount;
1339
1340     strMount.Format(_T("%c%s%s%s."),
1341                     ((bRW)?_T('%'):_T('#')),
1342                     strInCellName,
1343                     ((strInCellName.IsEmpty())?_T(""):_T(":")),
1344                     strVolName);
1345
1346     CStringUtf8 ustrMount(strMount);
1347
1348     blob.out_size = 0;
1349     blob.in_size = ustrMount.GetLength() + 1;
1350     blob.in = ustrMount.GetBuffer();
1351     blob.out = NULL;
1352
1353     code = pioctl_T(strPath, VIOC_AFS_CREATE_MT_PT, &blob, 0);
1354
1355     ustrMount.ReleaseBuffer();
1356
1357     if (code) {
1358         ShowMessageBox(IDS_MOUNT_POINT_ERROR, MB_ICONERROR, IDS_MOUNT_POINT_ERROR, GetAfsError(errno, strDir));
1359         return FALSE;
1360     }
1361
1362     return TRUE;
1363 }
1364
1365 BOOL
1366 RemoveSymlink(const CString& strName)
1367 {
1368     BOOL error = FALSE;
1369     INT code=0;
1370     struct ViceIoctl blob;
1371     char lsbuffer[1024];
1372
1373     HOURGLASS hourglass;
1374
1375     CString strParent = Parent(strName);
1376     CStringUtf8 ustrLast(LastComponent(strName));
1377     FixNetbiosPath(strParent);
1378
1379     if ( IsFreelanceRoot(strParent) && !IsAdmin() ) {
1380         ShowMessageBox(IDS_NOT_AFS_CLIENT_ADMIN_ERROR, MB_ICONERROR, IDS_NOT_AFS_CLIENT_ADMIN_ERROR);
1381         return FALSE;
1382     }
1383
1384     blob.in_size = ustrLast.GetLength() + 1;
1385     blob.in = ustrLast.GetBuffer();
1386     blob.out = lsbuffer;
1387     blob.out_size = sizeof(lsbuffer);
1388     code = pioctl_T(strParent, VIOC_LISTSYMLINK, &blob, 0);
1389     ustrLast.ReleaseBuffer();
1390     if (code)
1391         return FALSE;
1392     blob.out_size = 0;
1393     blob.in_size = ustrLast.GetLength() + 1;
1394     blob.in = ustrLast.GetBuffer();
1395
1396     code = pioctl_T(strParent, VIOC_DELSYMLINK, &blob, 0);
1397
1398     ustrLast.ReleaseBuffer();
1399
1400     return (code == 0);
1401 }
1402
1403 BOOL
1404 IsSymlink(const CString& strName)
1405 {
1406     struct ViceIoctl blob;
1407     int code;
1408     char space[AFS_PIOCTL_MAXSIZE];
1409     HOURGLASS hourglass;
1410
1411     CStringUtf8 ustrLast(LastComponent(strName));
1412     CString strParent = Parent(strName);
1413
1414     FixNetbiosPath(strParent);
1415
1416     blob.in_size = ustrLast.GetLength() + 1;
1417     blob.in = ustrLast.GetBuffer();
1418     blob.out_size = AFS_PIOCTL_MAXSIZE;
1419     blob.out = space;
1420     memset(space, 0, AFS_PIOCTL_MAXSIZE);
1421
1422     code = pioctl_T(strParent, VIOC_LISTSYMLINK, &blob, 1);
1423
1424     ustrLast.ReleaseBuffer();
1425
1426     return (code==0);
1427 }
1428
1429
1430 BOOL
1431 IsMountPoint(const CString& path)
1432 {
1433     LONG code = 0;
1434     struct ViceIoctl blob;
1435     char lsbuffer[1024];
1436
1437     HOURGLASS hourglass;
1438
1439     CString parent = Parent(path);
1440     FixNetbiosPath(parent);
1441
1442     CStringUtf8 mountpoint(LastComponent(path));
1443
1444     blob.in_size = mountpoint.GetLength() + 1;
1445     blob.in = mountpoint.GetBuffer();
1446     blob.out = lsbuffer;
1447     blob.out_size = sizeof(lsbuffer);
1448
1449     code = pioctl_T(parent, VIOC_AFS_STAT_MT_PT, &blob, 0);
1450
1451     mountpoint.ReleaseBuffer();
1452
1453     return (code==0);
1454 }
1455
1456
1457 /*
1458  * Delete AFS mount points.  Variables are used as follows:
1459  *       tbuffer: Set to point to the null-terminated directory name of the mount point
1460  *          (or ``.'' if none is provided)
1461  *      tp: Set to point to the actual name of the mount point to nuke.
1462  */
1463 BOOL
1464 RemoveMount(CStringArray& files)
1465 {
1466     LONG code = 0;
1467     struct ViceIoctl blob;
1468     BOOL error = FALSE;
1469     CStringArray results;
1470     CString str;
1471     CString str2;
1472
1473     HOURGLASS hourglass;
1474
1475     for (int i = 0; i < files.GetSize(); i++) {
1476         if (!IsMountPoint(files[i])) {
1477             error = TRUE;
1478             if (errno == EINVAL)
1479                 results.Add(GetMessageString(IDS_NOT_MOUNT_POINT_ERROR, StripPath(files[i])));
1480             else
1481                 results.Add(GetMessageString(IDS_ERROR, GetAfsError(errno, StripPath(files[i]))));
1482             continue;   // don't bother trying
1483         }
1484
1485         CString parent = Parent(files[i]);
1486         CStringUtf8 mountpoint(LastComponent(files[i]));
1487         FixNetbiosPath(parent);
1488
1489         if ( IsFreelanceRoot(parent) && !IsAdmin() ) {
1490             results.Add(GetMessageString(IDS_NOT_AFS_CLIENT_ADMIN_ERROR, StripPath(files[i])));
1491             error = TRUE;
1492             continue;   /* skip */
1493         }
1494
1495         blob.out_size = 0;
1496         blob.in_size = mountpoint.GetLength() + 1;
1497         blob.in = mountpoint.GetBuffer();
1498
1499         code = pioctl_T(parent, VIOC_AFS_DELETE_MT_PT, &blob, 0);
1500
1501         mountpoint.ReleaseBuffer();
1502
1503         if (code) {
1504             error = TRUE;
1505             results.Add(GetMessageString(IDS_ERROR, GetAfsError(errno, StripPath(files[i]))));
1506         } else
1507             results.Add(GetMessageString(IDS_DELETED));
1508     }
1509
1510     LoadString (str, IDS_REMOVE_MP);
1511     LoadString (str2, IDS_REMOVE_MP_COLUMN);
1512     CResultsDlg dlg(REMOVE_MOUNT_POINTS_HELP_ID);
1513     dlg.SetContents(str, str2, StripPath(files), results);
1514     dlg.DoModal();
1515
1516     return !error;
1517 }
1518
1519 BOOL
1520 GetVolumeInfo(CString strFile, CVolInfo& volInfo, BOOL bFollow)
1521 {
1522     LONG code;
1523     struct ViceIoctl blob;
1524     struct VolumeStatus *status;
1525     char *name;
1526     char space[AFS_PIOCTL_MAXSIZE];
1527     HOURGLASS hourglass;
1528     CString strTarget = bFollow ? strFile : Parent(strFile);
1529
1530     volInfo.m_strFilePath = strTarget;
1531     volInfo.m_strFileName = StripPath(strTarget);
1532
1533     /*
1534         volInfo.m_strName = "VolumeName";
1535         volInfo.m_nID = 10;
1536         volInfo.m_nQuota = 20 * 1024 * 1024;
1537         volInfo.m_nNewQuota = volInfo.m_nQuota;
1538         volInfo.m_nUsed = volInfo.m_nQuota / 2;
1539         volInfo.m_nPartSize = 50 * 1024 * 1024;
1540         volInfo.m_nPartFree = 30 * 1024 * 1024;
1541         volInfo.m_nDup = -1;
1542         return TRUE;
1543      */
1544
1545     blob.out_size = AFS_PIOCTL_MAXSIZE;
1546     blob.in_size = 0;
1547     blob.out = space;
1548
1549     code = pioctl_T(strTarget, VIOCGETVOLSTAT, &blob, 1);
1550     if (code || blob.out_size < sizeof(*status)) {
1551         volInfo.m_strErrorMsg = GetAfsError(errno, strTarget);
1552         return FALSE;
1553     }
1554
1555     status = (VolumeStatus *)space;
1556     name = (char *)status + sizeof(*status);
1557
1558     volInfo.m_strName = Utf8ToCString(name);
1559     volInfo.m_nID = status->Vid;
1560     volInfo.m_nQuota = status->MaxQuota;
1561     volInfo.m_nNewQuota = status->MaxQuota;
1562     volInfo.m_nUsed = status->BlocksInUse;
1563     volInfo.m_nPartSize = status->PartMaxBlocks;
1564     volInfo.m_nPartFree = status->PartBlocksAvail;
1565     volInfo.m_nDup = -1;
1566
1567     errno = 0;
1568     code = pioctl_T(strTarget, VIOC_PATH_AVAILABILITY, &blob, 1);
1569     switch (errno) {
1570     case 0:
1571         volInfo.m_strAvail =_T("Online");
1572         break;
1573     case ENXIO:
1574         volInfo.m_strAvail = _T("Offline");
1575         break;
1576     case ENOSYS:
1577         volInfo.m_strAvail = _T("Unreachable");
1578         break;
1579     case EBUSY:
1580         volInfo.m_strAvail = _T("Busy");
1581         break;
1582     default:
1583         volInfo.m_strAvail = _T("Unknown");
1584     }
1585
1586     return TRUE;
1587 }
1588
1589 BOOL
1590 SetVolInfo(CVolInfo& volInfo)
1591 {
1592     LONG code;
1593     struct ViceIoctl blob;
1594     struct VolumeStatus *status;
1595     char *input;
1596     char space[AFS_PIOCTL_MAXSIZE];
1597     HOURGLASS hourglass;
1598
1599     blob.out_size = AFS_PIOCTL_MAXSIZE;
1600     blob.in_size = sizeof(*status) + 3; /* for the three terminating nulls */
1601     blob.out = space;
1602     blob.in = space;
1603
1604     status = (VolumeStatus *)space;
1605     status->MinQuota = -1;
1606     status->MaxQuota = volInfo.m_nNewQuota;
1607
1608     input = (char *)status + sizeof(*status);
1609     *(input++) = '\0';  /* never set name: this call doesn't change vldb */
1610     *(input++) = '\0';  // No offmsg
1611     *(input++) = '\0';  // No motd
1612
1613 #ifdef LOGGING_ON
1614     FILE *fp = OpenFile(szLogFileName, "a");
1615     if (fp) {
1616         fprintf(fp, "\nSetVolInfo() pioctl parms:\n");
1617         fprintf(fp, "\tpathp = %s\n\topcode = VIOCSETVOLSTAT (%d)\n\tblobp = %ld\n", PCCHAR(volInfo.m_strFilePath), VIOCSETVOLSTAT, &blob);
1618         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);
1619         fprintf(fp, "\t\t\tstatus->MinQuota = %ld\n", status->MinQuota);
1620         fprintf(fp, "\t\t\tstatus->MaxQuota = %ld\n", status->MaxQuota);
1621         fprintf(fp, "\t\t\tOther status fields aren't set\n");
1622         fprintf(fp, "\t\t\t3 nulls follow the VolumeStatus structure.\n");
1623         fprintf(fp, "\tfollow = 1\n");
1624         fclose(fp);
1625     }
1626 #endif
1627
1628     code = pioctl_T(volInfo.m_strFilePath, VIOCSETVOLSTAT, &blob, 1);
1629     if (code || blob.out_size < sizeof(*status)) {
1630         ShowMessageBox(IDS_SET_QUOTA_ERROR, MB_ICONERROR, IDS_SET_QUOTA_ERROR, GetAfsError(errno, volInfo.m_strName));
1631         return FALSE;
1632     }
1633
1634     return TRUE;
1635 }
1636
1637 void
1638 GetCellName(const CString& cellNamep, struct afsconf_cell *infop)
1639 {
1640     CStringUtf8 uCellName(cellNamep);
1641
1642     StringCbCopyA(infop->name, sizeof(infop->name), uCellName);
1643 }
1644
1645 BOOL NO_CALLER
1646 CheckServers(const CString& strCellName, WHICH_CELLS nCellsToCheck, BOOL bFast)
1647 {
1648     LONG code;
1649     struct ViceIoctl blob;
1650     LONG j;
1651     LONG temp = 0;
1652     struct afsconf_cell info;
1653     struct chservinfo checkserv;
1654     char space[AFS_PIOCTL_MAXSIZE];
1655     HOURGLASS hourglass;
1656
1657     memset(&checkserv, 0, sizeof(struct chservinfo));
1658     blob.in_size = sizeof(struct chservinfo);
1659     blob.in = (caddr_t)&checkserv;
1660
1661     blob.out_size = AFS_PIOCTL_MAXSIZE;
1662     blob.out = space;
1663     memset(space, 0, sizeof(afs_int32));        /* so we assure zero when nothing is copied back */
1664
1665     if (nCellsToCheck == SPECIFIC_CELL) {
1666         temp = 2;
1667         GetCellName(strCellName, &info);
1668         strcpy(checkserv.tbuffer,info.name);
1669         checkserv.tsize = strlen(info.name) + 1;
1670     } else {
1671         if (nCellsToCheck != ALL_CELLS)
1672             temp = 2;
1673         strcpy(checkserv.tbuffer, "\0");
1674         checkserv.tsize = 0;
1675     }
1676     if (bFast)
1677         temp |= 1;      /* set fast flag */
1678
1679     checkserv.magic = 0x12345678;       /* XXX */
1680     checkserv.tflags = temp;
1681     checkserv.tinterval = -1;   /* don't change current interval */
1682
1683     code = pioctl_utf8(0, VIOCCKSERV, &blob, 1);
1684     if (code) {
1685         ShowMessageBox(IDS_CHECK_SERVERS_ERROR, MB_ICONERROR, IDS_CHECK_SERVERS_ERROR, GetAfsError(errno, CString()));
1686         return FALSE;
1687     }
1688
1689     memcpy(&temp, space, sizeof(LONG));
1690
1691     if (temp == 0) {
1692         ShowMessageBox(IDS_ALL_SERVERS_RUNNING, MB_OK|MB_ICONINFORMATION, IDS_ALL_SERVERS_RUNNING);
1693         return TRUE;
1694     }
1695
1696     CStringArray servers;
1697     for (j = 0; j < AFS_MAXHOSTS; j++) {
1698         memcpy(&temp, space + j * sizeof(LONG), sizeof(LONG));
1699         if (temp == 0)
1700             break;
1701
1702         char *name = hostutil_GetNameByINet(temp);
1703         servers.Add(name);
1704     }
1705
1706     CDownServersDlg dlg;
1707     dlg.SetServerNames(servers);
1708     dlg.DoModal();
1709
1710     return TRUE;
1711 }
1712
1713 BOOL
1714 GetTokenInfo(CStringArray& tokenInfo)
1715 {
1716     int cellNum;
1717     int rc;
1718     time_t current_time;
1719     time_t tokenExpireTime;
1720     char *expireString;
1721     char userName[100];
1722 //      char s[100];
1723     struct ktc_principal serviceName, clientName;
1724     struct ktc_token token;
1725
1726     CString strTokenInfo;
1727     CString strUserName;
1728     CString strCellName;
1729     CString strExpir;
1730
1731 //      tokenInfo.Add("");
1732 //      return TRUE;
1733
1734
1735     HOURGLASS hourglass;
1736
1737 //      printf("\nTokens held by the Cache Manager:\n\n");
1738     cellNum = 0;
1739     current_time = time(0);
1740
1741     while (1) {
1742         rc = ktc_ListTokens(cellNum, &cellNum, &serviceName);
1743         if (rc == KTC_NOENT) {
1744             /* end of list */
1745 //          printf("   --End of list --\n");
1746             break;
1747         }
1748         else if (rc == KTC_NOCM) {
1749             ShowMessageBox(IDS_GET_TOKENS_NO_AFS_SERVICE);
1750 //          printf("AFS service may not have started\n");
1751             break;
1752         }
1753         else if (rc) {
1754             ShowMessageBox(IDS_GET_TOKENS_UNEXPECTED_ERROR, MB_ICONERROR, IDS_GET_TOKENS_UNEXPECTED_ERROR, rc);
1755             return FALSE;
1756 //          printf("Unexpected error, code %d\n", rc);
1757 //          exit(1);
1758         }
1759         else {
1760             rc = ktc_GetToken(&serviceName, &token, sizeof(token), &clientName);
1761             if (rc) {
1762                 ShowMessageBox(IDS_GET_TOKENS_UNEXPECTED_ERROR2, MB_ICONERROR, IDS_GET_TOKENS_UNEXPECTED_ERROR2,
1763                                 serviceName.name, serviceName.instance, serviceName.cell, rc);
1764                 continue;
1765             }
1766
1767             tokenExpireTime = token.endTime;
1768
1769             strcpy(userName, clientName.name);
1770             if (clientName.instance[0] != 0) {
1771                 strcat(userName, ".");
1772                 strcat(userName, clientName.instance);
1773             }
1774
1775             BOOL bShowName = FALSE;
1776
1777             if (userName[0] == '\0')
1778                 ; //printf("Tokens");
1779 // AFS ID is not returned at this time.
1780 //          else if (strncmp(userName, "AFS ID", 6) == 0)
1781 //              printf("User's (%s) tokens", userName);
1782 //              sscanf(userName, "(AFS ID %s)", szAfsID);
1783             else if (strncmp(userName, "Unix UID", 8) == 0)
1784                 ; //printf("Tokens");
1785             else
1786                 strUserName = userName;
1787 //              printf("User %s's tokens", userName);
1788
1789 //              printf(" for %s%s%s@%s ", serviceName.name, serviceName.instance[0] ? "." : "", serviceName.instance, serviceName.cell);
1790             strCellName = serviceName.cell;
1791
1792             if (tokenExpireTime <= current_time)
1793                 strExpir = "[>> Expired <<]";
1794 //              printf("[>> Expired <<]\n");
1795             else {
1796                 expireString = ctime(&tokenExpireTime);
1797                 expireString += 4;       /* Skip day of week */
1798                 expireString[12] = '\0'; /* Omit secs & year */
1799 //              printf("[Expires %s]\n", expireString);
1800 #ifdef UNICODE
1801                 strExpir.Format(_T("%S"), expireString);
1802 #else
1803                 strExpir.Format(_T("%s"), expireString);
1804 #endif
1805             }
1806
1807             strTokenInfo = strUserName + "\t" + strCellName + "\t" + strExpir + "\t" + strCellName;
1808             tokenInfo.Add(strTokenInfo);
1809         }
1810     }
1811
1812 //      printf("Press <Enter> or <Return> when finished: ");
1813 //      gets(s);
1814     return TRUE;
1815 }
1816
1817 UINT
1818 MakeSymbolicLink(const CString& strName, const CString& strTarget)
1819 {
1820     struct ViceIoctl blob;
1821     UINT code;
1822     HOURGLASS hourglass;
1823
1824     CString strParent = Parent(strName);
1825     FixNetbiosPath(strParent);
1826
1827     if ( IsFreelanceRoot(strParent) && !IsAdmin() ) {
1828         ShowMessageBox(IDS_NOT_AFS_CLIENT_ADMIN_ERROR, MB_ICONERROR, IDS_NOT_AFS_CLIENT_ADMIN_ERROR);
1829         return FALSE;
1830     }
1831
1832     CStringUtf8 ustrTarget(strTarget);
1833
1834     blob.in_size = ustrTarget.GetLength() + 1;
1835     blob.in = ustrTarget.GetBuffer();
1836     blob.out_size = 0;
1837     blob.out = NULL;
1838
1839     code = pioctl_T(strName, VIOC_SYMLINK, &blob, 0);
1840
1841     ustrTarget.ReleaseBuffer();
1842
1843     if (code != 0)
1844         return code;
1845     return 0;
1846 }
1847
1848 void NO_CALLER
1849 ListSymbolicLinkPath(const char *strName,char *strPath,UINT nlenPath)
1850 {
1851     ASSERT(nlenPath<MAX_PATH);
1852     struct ViceIoctl blob;
1853     char orig_name[MAX_PATH+1];         /*Original name, may be modified*/
1854     char true_name[MAX_PATH+1];         /*``True'' dirname (e.g., symlink target)*/
1855     char parent_dir[MAX_PATH+1];                /*Parent directory of true name*/
1856     char *last_component;       /*Last component of true name*/
1857     UINT code;
1858     char space[AFS_PIOCTL_MAXSIZE];
1859     HOURGLASS hourglass;
1860
1861     strcpy(orig_name, strName);
1862     strcpy(true_name, orig_name);
1863     /*
1864      * Find rightmost slash, if any.
1865      */
1866     last_component = (char *) strrchr(true_name, '\\');
1867     if (!last_component)
1868         last_component = (char *) strrchr(true_name, '/');
1869     if (last_component) {
1870         /*
1871          * Found it.  Designate everything before it as the parent directory,
1872          * everything after it as the final component.
1873          */
1874         strncpy(parent_dir, true_name, last_component - true_name + 1);
1875         parent_dir[last_component - true_name + 1] = 0;
1876         last_component++;   /*Skip the slash*/
1877
1878         if (!IsPathInAfs(parent_dir)) {
1879             const char * nbname = NetbiosName();
1880             int len = strlen(nbname);
1881
1882             if (parent_dir[0] == '\\' && parent_dir[1] == '\\' &&
1883                  parent_dir[len+2] == '\\' &&
1884                  parent_dir[len+3] == '\0' &&
1885                  !strnicmp(nbname,&parent_dir[2],len))
1886             {
1887                 sprintf(parent_dir,"\\\\%s\\all\\", nbname);
1888             }
1889         }
1890     }
1891     else {
1892         /*
1893          * No slash appears in the given file name.  Set parent_dir to the current
1894          * directory, and the last component as the given name.
1895          */
1896         fs_ExtractDriveLetter(true_name, parent_dir);
1897         strcat(parent_dir, ".");
1898         last_component = true_name;
1899         fs_StripDriveLetter(true_name, true_name, sizeof(true_name));
1900     }
1901     blob.in = last_component;
1902     blob.in_size = strlen(last_component)+1;
1903     blob.out_size = AFS_PIOCTL_MAXSIZE;
1904     blob.out = space;
1905     memset(space, 0, AFS_PIOCTL_MAXSIZE);
1906     if ((code = pioctl(parent_dir, VIOC_LISTSYMLINK, &blob, 1)))
1907         strcpy(space,"???");
1908     ASSERT(strlen(space)<MAX_PATH);
1909     strncpy(strPath,space,nlenPath);
1910 }
1911
1912 BOOL NO_CALLER
1913 ListSymlink(CStringArray& files)
1914 {
1915     LONG code;
1916     struct ViceIoctl blob;
1917     int error;
1918     CStringArray symlinks;
1919     char space[AFS_PIOCTL_MAXSIZE];
1920     HOURGLASS hourglass;
1921
1922     error = 0;
1923
1924     for (int i = 0; i < files.GetSize(); i++) {
1925
1926         CString strParent = Parent(files[i]);
1927         CStringUtf8 ustrLast(LastComponent(files[i]));
1928
1929         FixNetbiosPath(strParent);
1930
1931         blob.in_size = ustrLast.GetLength() + 1;
1932         blob.in = ustrLast.GetBuffer();
1933         blob.out_size = AFS_PIOCTL_MAXSIZE;
1934         blob.out = space;
1935         memset(space, 0, AFS_PIOCTL_MAXSIZE);
1936
1937         code = pioctl_T(strParent, VIOC_LISTSYMLINK, &blob, 1);
1938
1939         ustrLast.ReleaseBuffer();
1940
1941         if (code == 0) {
1942             CString syml;
1943             int len;
1944
1945             space[AFS_PIOCTL_MAXSIZE - 1] = '\0';
1946             syml = Utf8ToCString(space);
1947             len = syml.GetLength();
1948
1949             if (len > 0) {
1950                 if (syml[len - 1] == _T('.'))
1951                     syml.Truncate(len - 1);
1952             }
1953
1954             symlinks.Add(ParseSymlink(StripPath(files[i]), syml));
1955
1956         } else {
1957             error = 1;
1958             if (errno == EINVAL)
1959                 symlinks.Add(GetMessageString(IDS_NOT_SYMLINK_ERROR, StripPath(files[i])));
1960             else
1961                 symlinks.Add(GetMessageString(IDS_LIST_MOUNT_POINT_ERROR, GetAfsError(errno, StripPath(files[i]))));
1962         }
1963     }
1964
1965     CSymlinksDlg dlg;
1966     dlg.SetSymlinks(symlinks);
1967     dlg.DoModal();
1968
1969     return !error;
1970 }
1971
1972 CString
1973 GetCellName( const CString& strPath, BOOL bFollow )
1974 {
1975     return GetCell(bFollow ? strPath : Parent(strPath));
1976 }
1977
1978 CString
1979 GetServer( const CString& strPath, BOOL bFollow )
1980 {
1981     LONG code;
1982     struct ViceIoctl blob;
1983     CString server;
1984     char space[AFS_PIOCTL_MAXSIZE];
1985
1986     blob.out_size = AFS_PIOCTL_MAXSIZE;
1987     blob.in_size = 0;
1988     blob.out = space;
1989     memset(space, 0, sizeof(space));
1990
1991     code = pioctl_T(bFollow ? strPath : Parent(strPath), VIOCWHEREIS, &blob, 1);
1992     if (code) {
1993         server=GetAfsError(errno);
1994     } else {
1995         LONG *hosts = (LONG *)space;
1996
1997         for (int j = 0; j < AFS_MAXHOSTS; j++) {
1998             if (hosts[j] == 0)
1999                 break;
2000             char *hostName = hostutil_GetNameByINet(hosts[j]);
2001             server=hostName;
2002         }
2003     }
2004
2005     return server;
2006 }
2007
2008 void
2009 GetServers( const CString& strPath, CStringArray& servers, BOOL bFollow )
2010 {
2011     LONG code;
2012     struct ViceIoctl blob;
2013     char space[AFS_PIOCTL_MAXSIZE];
2014
2015     blob.out_size = AFS_PIOCTL_MAXSIZE;
2016     blob.in_size = 0;
2017     blob.out = space;
2018     memset(space, 0, sizeof(space));
2019
2020     code = pioctl_T(bFollow ? strPath : Parent(strPath), VIOCWHEREIS, &blob, 1);
2021     if (code) {
2022         servers.Add(GetAfsError(errno));
2023     } else {
2024         LONG *hosts = (LONG *)space;
2025
2026         for (int j = 0; j < AFS_MAXHOSTS; j++) {
2027             if (hosts[j] == 0)
2028                 break;
2029             char *hostName = hostutil_GetNameByINet(hosts[j]);
2030             servers.Add(hostName);
2031         }
2032     }
2033 }
2034
2035 CString
2036 GetOwner( const CString& strPath, BOOL bFollow )
2037 {
2038     LONG code;
2039     struct ViceIoctl blob;
2040     afs_int32 owner[2];
2041     CString ret = TEXT("(unknown)");
2042
2043     blob.in_size = 0;
2044     blob.out_size = 2 * sizeof(afs_int32);
2045     blob.out = (char *) &owner;
2046
2047     code = pioctl_T(bFollow ? strPath : Parent(strPath), VIOCGETOWNER, &blob, 1);
2048     if (code == 0 && blob.out_size == 2 * sizeof(afs_int32)) {
2049         char oname[PR_MAXNAMELEN] = "(unknown)";
2050         char confDir[257];
2051         CStringUtf8 cell;
2052
2053         cell = GetCell(strPath, bFollow);
2054
2055         /* Go to the PRDB and see if this all number username is valid */
2056         cm_GetConfigDir(confDir, sizeof(confDir));
2057
2058         pr_Initialize(1, confDir, PCCHAR(cell));
2059         pr_SIdToName(owner[0], oname);
2060
2061         ret.Empty();
2062         ret.Format(TEXT("%s [%d]"), (LPCTSTR)CString(oname), owner[0]);
2063     }
2064
2065     return ret;
2066 }
2067
2068 CString
2069 GetGroup( const CString& strPath, BOOL bFollow )
2070 {
2071     LONG code;
2072     struct ViceIoctl blob;
2073     afs_int32 owner[2];
2074     CString ret = TEXT("(unknown)");
2075
2076     blob.in_size = 0;
2077     blob.out_size = 2 * sizeof(afs_int32);
2078     blob.out = (char *) &owner;
2079
2080     code = pioctl_T(bFollow ? strPath : Parent(strPath), VIOCGETOWNER, &blob, 1);
2081     if (code == 0 && blob.out_size == 2 * sizeof(afs_int32)) {
2082         char gname[PR_MAXNAMELEN] = "(unknown)";
2083         char confDir[257];
2084         CStringUtf8 cell;
2085
2086         cell = GetCell(strPath, bFollow);
2087
2088         /* Go to the PRDB and see if this all number username is valid */
2089         cm_GetConfigDir(confDir, sizeof(confDir));
2090
2091         pr_Initialize(1, confDir, PCCHAR(cell));
2092         pr_SIdToName(owner[1], gname);
2093
2094         ret.Empty();
2095         ret.Format(TEXT("%s [%d]"), (LPCTSTR)CString(gname), owner[1]);
2096     }
2097
2098     return ret;
2099 }
2100
2101 BOOL
2102 GetUnixModeBits( const CString& strPath, CString& user, CString& group, CString& other, CString& suid )
2103 {
2104     // the strings must match the unix ls command output: "rwx" means read/write/execute permissions
2105
2106     LONG code;
2107     struct ViceIoctl blob;
2108     afs_uint32 unixModeBits;
2109     CString ret = TEXT("(unknown)");
2110
2111     user.Empty();
2112     group.Empty();
2113     other.Empty();
2114     suid.Empty();
2115
2116     blob.in_size = 0;
2117     blob.out_size = sizeof(afs_int32);
2118     blob.out = (char *) &unixModeBits;
2119
2120     code = pioctl_T(strPath, VIOC_GETUNIXMODE, &blob, 1);
2121     if (code == 0 && blob.out_size == sizeof(afs_uint32)) {
2122         if (unixModeBits & S_IRUSR)
2123             user += _T("r");
2124         if (unixModeBits & S_IWUSR)
2125             user += _T("w");
2126         if (unixModeBits & S_IXUSR)
2127             user += _T("x");
2128
2129         if (unixModeBits & S_IRGRP)
2130             group += _T("r");
2131         if (unixModeBits & S_IWGRP)
2132             group += _T("w");
2133         if (unixModeBits & S_IXGRP)
2134             group += _T("x");
2135
2136         if (unixModeBits & S_IROTH)
2137             other += _T("r");
2138         if (unixModeBits & S_IWOTH)
2139             other += _T("w");
2140         if (unixModeBits & S_IXOTH)
2141             other += _T("x");
2142
2143         if (unixModeBits & S_ISUID)
2144             suid += _T("s");
2145         if (unixModeBits & S_ISGID)
2146             suid += _T("g");
2147         if (unixModeBits & S_ISVTX)
2148             suid += _T("v");
2149
2150         return TRUE;
2151     }
2152
2153     return FALSE;
2154 }
2155
2156 void
2157 SetUnixModeBits( const CStringArray& files, const CString& user, const CString& group, const CString& other, const CString& suid )
2158 {
2159     // set the unix file attributes for all paths in 'files'.
2160     LONG code;
2161     struct ViceIoctl blob;
2162     struct {
2163         cm_ioctlQueryOptions_t options;
2164         afs_uint32 unixModeBits;
2165     } inData;
2166
2167     memset(&inData, 0, sizeof(inData));
2168     inData.options.size = sizeof(inData.options);
2169     inData.options.field_flags = 0;
2170     inData.options.literal = 0;            /* always applying to target */
2171     blob.in_size = inData.options.size;    /* no variable length data */
2172     blob.in = &inData;
2173     blob.out = NULL;
2174     blob.out_size = 0;
2175     inData.unixModeBits = 0;
2176
2177     if (user.Find(_T("r")) != -1)
2178         inData.unixModeBits |= S_IRUSR;
2179     if (user.Find(_T("w")) != -1)
2180         inData.unixModeBits |= S_IWUSR;
2181     if (user.Find(_T("x")) != -1)
2182         inData.unixModeBits |= S_IXUSR;
2183
2184     if (group.Find(_T("r")) != -1)
2185         inData.unixModeBits |= S_IRGRP;
2186     if (group.Find(_T("w")) != -1)
2187         inData.unixModeBits |= S_IWGRP;
2188     if (group.Find(_T("x")) != -1)
2189         inData.unixModeBits |= S_IXGRP;
2190
2191     if (other.Find(_T("r")) != -1)
2192         inData.unixModeBits |= S_IROTH;
2193     if (other.Find(_T("w")) != -1)
2194         inData.unixModeBits |= S_IWOTH;
2195     if (other.Find(_T("x")) != -1)
2196         inData.unixModeBits |= S_IXOTH;
2197
2198     if (suid.Find(_T("s")) != -1)
2199         inData.unixModeBits |= S_ISUID;
2200     if (suid.Find(_T("g")) != -1)
2201         inData.unixModeBits |= S_ISGID;
2202     if (suid.Find(_T("v")) != -1)
2203         inData.unixModeBits |= S_ISVTX;
2204
2205     for (int i = 0; i < files.GetSize(); i++)
2206         code = pioctl_T(files[i], VIOC_SETUNIXMODE, &blob, 1);
2207 }
2208
2209 CString GetMountpoint( const CString& strPath )
2210 {
2211     LONG code;
2212     struct ViceIoctl blob;
2213     char space[AFS_PIOCTL_MAXSIZE];
2214     CString parent_dir;         /* Parent directory of true name */
2215     CStringUtf8 last_component;         /* Last component of true name */
2216
2217     CString mountPoint;
2218
2219
2220     int last_slash = strPath.ReverseFind(_T('\\'));
2221
2222     if (last_slash != -1) {
2223         last_component.SetString( strPath.Mid(last_slash + 1) );
2224         parent_dir.SetString( strPath.Left(last_slash + 1) );
2225         FixNetbiosPath(parent_dir);
2226     } else {
2227         // The path is of the form "C:foo" or just "foo".  If
2228         // there is a drive, then use the current directory of
2229         // that drive.  Otherwise we just use '.'.
2230
2231         if (strPath.GetLength() >= 2 && strPath[1] == _T(':')) {
2232             parent_dir.Format(_T("%c:."), strPath[0]);
2233             last_component.SetString( strPath.Mid(2) );
2234         } else {
2235             parent_dir.SetString( _T("."));
2236             last_component.SetString( strPath );
2237         }
2238     }
2239
2240     blob.in_size = last_component.GetLength() + 1;
2241     blob.in = last_component.GetBuffer();
2242     blob.out_size = AFS_PIOCTL_MAXSIZE;
2243     blob.out = space;
2244     memset(space, 0, AFS_PIOCTL_MAXSIZE);
2245
2246     code = pioctl_T(parent_dir, VIOC_AFS_STAT_MT_PT, &blob, 1);
2247
2248     last_component.ReleaseBuffer();
2249
2250     if (code == 0) {
2251         int nPos;
2252         space[AFS_PIOCTL_MAXSIZE - 1] = '\0';
2253         nPos = strlen(space) - 1;
2254         if (space[nPos] == '.')
2255             space[nPos] = 0;
2256         mountPoint = ParseMountPoint(StripPath(strPath), Utf8ToCString(space));
2257     } else {
2258         if (errno == EINVAL)
2259             mountPoint = GetMessageString(IDS_NOT_MOUNT_POINT_ERROR, StripPath(strPath));
2260         else
2261             mountPoint = GetMessageString(IDS_LIST_MOUNT_POINT_ERROR, GetAfsError(errno, StripPath(strPath)));
2262     }
2263
2264     return mountPoint;
2265 }
2266
2267 CString GetSymlink( const CString& strPath )
2268 {
2269     LONG code;
2270     struct ViceIoctl blob;
2271     CString symlink;
2272     char space[AFS_PIOCTL_MAXSIZE];
2273
2274     CString strParent = Parent(strPath);
2275     CStringUtf8 ustrLast(LastComponent(strPath));
2276
2277     FixNetbiosPath(strParent);
2278
2279     blob.in_size = ustrLast.GetLength() + 1;
2280     blob.in = ustrLast.GetBuffer();
2281     blob.out_size = AFS_PIOCTL_MAXSIZE;
2282     blob.out = space;
2283     memset(space, 0, AFS_PIOCTL_MAXSIZE);
2284
2285     code = pioctl_T(strParent, VIOC_LISTSYMLINK, &blob, 1);
2286
2287     ustrLast.ReleaseBuffer();
2288
2289     if (code == 0) {
2290         CString syml;
2291         int len;
2292
2293         space[AFS_PIOCTL_MAXSIZE - 1] = '\0';
2294         syml = Utf8ToCString(space);
2295         len = syml.GetLength();
2296
2297         if (len > 0) {
2298             if (syml[len - 1] == _T('.'))
2299                 syml.Truncate(len - 1);
2300         }
2301
2302         symlink = ParseSymlink(StripPath(strPath), syml);
2303
2304     } else {
2305         if (errno == EINVAL)
2306             symlink = GetMessageString(IDS_NOT_SYMLINK_ERROR, StripPath(strPath));
2307         else
2308             symlink = GetMessageString(IDS_LIST_MOUNT_POINT_ERROR, GetAfsError(errno, StripPath(strPath)));
2309     }
2310
2311
2312     return symlink;
2313 }