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