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