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