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