Windows: remove trailing whitespace
[openafs.git] / src / WINNT / client_exp / gui2fs.cpp
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include "stdafx.h"
11 #include <winsock2.h>
12 #include <ws2tcpip.h>
13
14 extern "C" {
15 #include <afsconfig.h>
16 #include <afs/param.h>
17 #include <roken.h>
18 #include <afs/stds.h>
19 }
20
21 #include <errno.h>
22 #include <time.h>
23
24 #include "gui2fs.h"
25 #include "msgs.h"
26 #include "results_dlg.h"
27 #include "volume_inf.h"
28 #include "mount_points_dlg.h"
29 #include "symlinks_dlg.h"
30 #include "hourglass.h"
31 #include "down_servers_dlg.h"
32
33 extern "C" {
34 #include <rx/rx_globals.h>
35 #include "fs.h"
36 #include "fs_utils.h"
37 #include <afs/afsint.h>
38 #include <afs/afs_consts.h>
39 #include <afs/cellconfig.h>
40 #include <afs/vldbint.h>
41 #include <afs/volser.h>
42 #include <afs/auth.h>
43 #include <WINNT\afsreg.h>
44 #include <cm.h>
45 #include <cm_nls.h>
46 #include <osi.h>
47 #include <cm_user.h>
48 #include <cm_scache.h>
49 #include <cm_ioctl.h>
50 }
51
52 #define STRSAFE_NO_DEPRECATE
53 #include <strsafe.h>
54
55 #define PCCHAR(str)             ((char *)(const char *)(str))
56 #define VL_NOENT                (363524L)
57
58 #define MAXNAME 100
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[AFS_PIOCTL_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 = AFS_PIOCTL_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[AFS_PIOCTL_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 = AFS_PIOCTL_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 = AFS_PIOCTL_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 < AFS_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[AFS_PIOCTL_MAXSIZE];
612     char tstring[AFS_PIOCTL_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 = AFS_PIOCTL_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 = AFS_PIOCTL_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 = AFS_PIOCTL_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     memset(&options, 0, sizeof(options));
1170     options.size = sizeof(options);
1171     options.field_flags |= CM_IOCTL_QOPTS_FIELD_LITERAL;
1172     options.literal = 1;
1173     blob.in_size = options.size;    /* no variable length data */
1174     blob.in = &options;
1175     blob.out_size = sizeof(cm_fid_t);
1176     blob.out = (char *) &fid;
1177
1178     code = pioctl_T(strPath, VIOCGETFID, &blob, 1);
1179     if (code) {
1180         if ((errno == EINVAL) || (errno == ENOENT))
1181         return FALSE;
1182     }
1183     return TRUE;
1184 }
1185
1186 static int
1187 IsFreelanceRoot(const CString& apath)
1188 {
1189     struct ViceIoctl blob;
1190     afs_int32 code;
1191
1192     blob.in_size = 0;
1193     blob.out_size = AFS_PIOCTL_MAXSIZE;
1194     blob.out = space;
1195
1196     code = pioctl_T(apath, VIOC_FILE_CELL_NAME, &blob, 1);
1197     if (code == 0)
1198         return !strcmp("Freelance.Local.Root",space);
1199     return 1;   /* assume it is because it is more restrictive that way */
1200 }
1201
1202 static const char * NetbiosName(void)
1203 {
1204     static char buffer[1024] = "AFS";
1205     HKEY  parmKey;
1206     DWORD code;
1207     DWORD dummyLen;
1208     DWORD enabled = 0;
1209
1210     code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
1211                          0, (IsWow64()?KEY_WOW64_64KEY:0)|KEY_QUERY_VALUE, &parmKey);
1212     if (code == ERROR_SUCCESS) {
1213         dummyLen = sizeof(buffer);
1214         code = RegQueryValueExA(parmKey, "NetbiosName", NULL, NULL,
1215                                (LPBYTE)buffer, &dummyLen);
1216         RegCloseKey (parmKey);
1217     } else {
1218         strcpy(buffer, "AFS");
1219     }
1220     return buffer;
1221 }
1222
1223 static void FixNetbiosPath(CString& path)
1224 {
1225     if (!IsPathInAfs(path)) {
1226         CString nbroot;
1227         const char * nbname = NetbiosName();
1228
1229 #ifdef UNICODE
1230         nbroot.Format(_T("\\\\%S\\"), nbname);
1231 #else
1232         nbroot.Format(_T("\\\\%s\\"), nbname);
1233 #endif
1234
1235         if (nbroot.CompareNoCase(path) == 0) {
1236             path.Append(_T("all\\"));
1237         }
1238     }
1239 }
1240
1241 #define AFSCLIENT_ADMIN_GROUPNAME "AFS Client Admins"
1242
1243 static BOOL IsAdmin (void)
1244 {
1245     static BOOL fAdmin = FALSE;
1246     static BOOL fTested = FALSE;
1247
1248     if (!fTested)
1249     {
1250         /* Obtain the SID for the AFS client admin group.  If the group does
1251          * not exist, then assume we have AFS client admin privileges.
1252          */
1253         PSID psidAdmin = NULL;
1254         DWORD dwSize, dwSize2;
1255         TCHAR pszAdminGroup[ MAX_COMPUTERNAME_LENGTH + sizeof(AFSCLIENT_ADMIN_GROUPNAME) + 2 ];
1256         TCHAR *pszRefDomain = NULL;
1257         SID_NAME_USE snu = SidTypeGroup;
1258
1259         dwSize = sizeof(pszAdminGroup);
1260
1261         if (!GetComputerName(pszAdminGroup, &dwSize)) {
1262             /* Can't get computer name.  We return false in this case.
1263                Retain fAdmin and fTested. This shouldn't happen.*/
1264             return FALSE;
1265         }
1266
1267         dwSize = 0;
1268         dwSize2 = 0;
1269
1270         lstrcat(pszAdminGroup, _T("\\"));
1271         lstrcat(pszAdminGroup, _T(AFSCLIENT_ADMIN_GROUPNAME));
1272
1273         LookupAccountName(NULL, pszAdminGroup, NULL, &dwSize, NULL, &dwSize2, &snu);
1274         /* that should always fail. */
1275
1276         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
1277             /* if we can't find the group, then we allow the operation */
1278             fAdmin = TRUE;
1279             return TRUE;
1280         }
1281
1282         if (dwSize == 0 || dwSize2 == 0) {
1283             /* Paranoia */
1284             fAdmin = TRUE;
1285             return TRUE;
1286         }
1287
1288         psidAdmin = (PSID)malloc(dwSize); memset(psidAdmin,0,dwSize);
1289         pszRefDomain = (TCHAR *)malloc(dwSize2);
1290
1291         if (!LookupAccountName(NULL, pszAdminGroup, psidAdmin, &dwSize, pszRefDomain, &dwSize2, &snu)) {
1292             /* We can't lookup the group now even though we looked it up earlier.
1293                Could this happen? */
1294             fAdmin = TRUE;
1295         } else {
1296             /* Then open our current ProcessToken */
1297             HANDLE hToken;
1298
1299             if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken))
1300             {
1301
1302                 if (!CheckTokenMembership(hToken, psidAdmin, &fAdmin)) {
1303                     /* We'll have to allocate a chunk of memory to store the list of
1304                      * groups to which this user belongs; find out how much memory
1305                      * we'll need.
1306                      */
1307                     DWORD dwSize = 0;
1308                     PTOKEN_GROUPS pGroups;
1309
1310                     GetTokenInformation (hToken, TokenGroups, NULL, dwSize, &dwSize);
1311
1312                     pGroups = (PTOKEN_GROUPS)malloc(dwSize);
1313
1314                     /* Allocate that buffer, and read in the list of groups. */
1315                     if (GetTokenInformation (hToken, TokenGroups, pGroups, dwSize, &dwSize))
1316                     {
1317                         /* Look through the list of group SIDs and see if any of them
1318                          * matches the AFS Client Admin group SID.
1319                          */
1320                         size_t iGroup = 0;
1321                         for (; (!fAdmin) && (iGroup < pGroups->GroupCount); ++iGroup)
1322                         {
1323                             if (EqualSid (psidAdmin, pGroups->Groups[ iGroup ].Sid)) {
1324                                 fAdmin = TRUE;
1325                             }
1326                         }
1327                     }
1328
1329                     if (pGroups)
1330                         free(pGroups);
1331                 }
1332
1333                 /* if do not have permission because we were not explicitly listed
1334                  * in the Admin Client Group let's see if we are the SYSTEM account
1335                  */
1336                 if (!fAdmin) {
1337                     PTOKEN_USER pTokenUser;
1338                     SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
1339                     PSID pSidLocalSystem = 0;
1340                     DWORD gle;
1341
1342                     GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize);
1343
1344                     pTokenUser = (PTOKEN_USER)malloc(dwSize);
1345
1346                     if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize))
1347                         gle = GetLastError();
1348
1349                     if (AllocateAndInitializeSid( &SIDAuth, 1,
1350                                                   SECURITY_LOCAL_SYSTEM_RID,
1351                                                   0, 0, 0, 0, 0, 0, 0,
1352                                                   &pSidLocalSystem))
1353                     {
1354                         if (EqualSid(pTokenUser->User.Sid, pSidLocalSystem)) {
1355                             fAdmin = TRUE;
1356                         }
1357
1358                         FreeSid(pSidLocalSystem);
1359                     }
1360
1361                     if ( pTokenUser )
1362                         free(pTokenUser);
1363                 }
1364             }
1365         }
1366
1367         free(psidAdmin);
1368         free(pszRefDomain);
1369
1370         fTested = TRUE;
1371     }
1372
1373     return fAdmin;
1374 }
1375
1376 CString Parent(const CString& path)
1377 {
1378     int last_slash = path.ReverseFind(_T('\\'));
1379
1380     if (last_slash != -1) {
1381         CString ret = path.Left(last_slash + 1);
1382         return ret;
1383     } else {
1384         if (path.GetLength() >= 2 && path[1] == _T(':')) {
1385             CString ret = path.Left(2);
1386             ret.AppendChar(_T('.'));
1387             return ret;
1388         } else {
1389             CString ret = _T(".");
1390             return ret;
1391         }
1392     }
1393     }
1394
1395 CString LastComponent(const CString& path)
1396 {
1397     int last_slash = path.ReverseFind(_T('\\'));
1398
1399     if (last_slash != -1) {
1400         CString ret = path.Mid(last_slash + 1);
1401         return ret;
1402     } else {
1403         if (path.GetLength() >= 2 && path[1] == _T(':')) {
1404             CString ret = path.Mid(2);
1405             return ret;
1406         } else {
1407             CString ret = path;
1408             return ret;
1409         }
1410     }
1411 }
1412
1413 static CString
1414 GetCell(const CString & path)
1415 {
1416     char cellname[MAXCELLCHARS];
1417     afs_int32 code;
1418     struct ViceIoctl blob;
1419
1420     blob.in_size = 0;
1421     blob.out_size = sizeof(cellname);
1422     blob.out = cellname;
1423
1424     code = pioctl_T(path, VIOC_FILE_CELL_NAME, &blob, 1);
1425     if (code) {
1426         CString s;
1427         s.Empty();
1428
1429         return s;
1430     } else {
1431         return Utf8ToCString(cellname);
1432     }
1433 }
1434
1435
1436 BOOL ListMount(CStringArray& files)
1437 {
1438     LONG code;
1439     struct ViceIoctl blob;
1440     int error;
1441
1442     CString parent_dir;         /* Parent directory of true name */
1443     CStringUtf8 last_component;         /* Last component of true name */
1444
1445     CStringArray mountPoints;
1446
1447     HOURGLASS hourglass;
1448
1449     error = 0;
1450
1451     for (int i = 0; i < files.GetSize(); i++) {
1452         int last_slash = files[i].ReverseFind(_T('\\'));
1453
1454         if (last_slash != -1) {
1455             last_component.SetString( files[i].Mid(last_slash + 1) );
1456             parent_dir.SetString( files[i].Left(last_slash + 1) );
1457             FixNetbiosPath(parent_dir);
1458         } else {
1459             // The path is of the form "C:foo" or just "foo".  If
1460             // there is a drive, then use the current directory of
1461             // that drive.  Otherwise we just use '.'.
1462
1463             if (files[i].GetLength() >= 2 && files[i][1] == _T(':')) {
1464                 parent_dir.Format(_T("%c:."), files[i][0]);
1465                 last_component.SetString( files[i].Mid(2) );
1466             } else {
1467                 parent_dir.SetString( _T("."));
1468                 last_component.SetString( files[i] );
1469             }
1470         }
1471
1472         blob.in_size = last_component.GetLength() + 1;
1473         blob.in = last_component.GetBuffer();
1474         blob.out_size = AFS_PIOCTL_MAXSIZE;
1475         blob.out = space;
1476         memset(space, 0, AFS_PIOCTL_MAXSIZE);
1477
1478         code = pioctl_T(parent_dir, VIOC_AFS_STAT_MT_PT, &blob, 1);
1479
1480         last_component.ReleaseBuffer();
1481
1482         if (code == 0) {
1483             int nPos;
1484             space[AFS_PIOCTL_MAXSIZE - 1] = '\0';
1485             nPos = strlen(space) - 1;
1486             if (space[nPos] == '.')
1487                 space[nPos] = 0;
1488             mountPoints.Add(ParseMountPoint(StripPath(files[i]), Utf8ToCString(space)));
1489         } else {
1490             error = 1;
1491             if (errno == EINVAL)
1492                 mountPoints.Add(GetMessageString(IDS_NOT_MOUNT_POINT_ERROR, StripPath(files[i])));
1493             else
1494                 mountPoints.Add(GetMessageString(IDS_LIST_MOUNT_POINT_ERROR, GetAfsError(errno, StripPath(files[i]))));
1495         }
1496     }
1497
1498     CMountPointsDlg dlg;
1499     dlg.SetMountPoints(mountPoints);
1500     dlg.DoModal();
1501
1502     return !error;
1503 }
1504
1505 BOOL
1506 MakeMount(const CString& strDir,
1507           const CString& strVolName,
1508           const CString& strInCellName,
1509           BOOL bRW)
1510 {
1511     LONG code;
1512     struct ViceIoctl blob;
1513     HOURGLASS hourglass;
1514
1515     ASSERT(strVolName.GetLength() < 64);
1516
1517     CString strParent = Parent(strDir);
1518
1519     FixNetbiosPath(strParent);
1520     if (!IsPathInAfs(strParent)) {
1521             ShowMessageBox(IDS_MAKE_MP_NOT_AFS_ERROR, MB_ICONERROR, IDS_MAKE_MP_NOT_AFS_ERROR);
1522             return FALSE;
1523         }
1524
1525     CString strPath = strParent + LastComponent(strDir);
1526
1527     if ( IsFreelanceRoot(strParent) && !IsAdmin() ) {
1528         ShowMessageBox(IDS_NOT_AFS_CLIENT_ADMIN_ERROR, MB_ICONERROR,
1529                        IDS_NOT_AFS_CLIENT_ADMIN_ERROR);
1530             return FALSE;
1531         }
1532
1533     CString strMount;
1534
1535     strMount.Format(_T("%c%s%s%s."),
1536                     ((bRW)?_T('%'):_T('#')),
1537                     strInCellName,
1538                     ((strInCellName.IsEmpty())?_T(""):_T(":")),
1539                     strVolName);
1540
1541     CStringUtf8 ustrMount(strMount);
1542
1543     blob.out_size = 0;
1544     blob.in_size = ustrMount.GetLength() + 1;
1545     blob.in = ustrMount.GetBuffer();
1546     blob.out = NULL;
1547
1548     code = pioctl_T(strPath, VIOC_AFS_CREATE_MT_PT, &blob, 0);
1549
1550     ustrMount.ReleaseBuffer();
1551
1552     if (code) {
1553         ShowMessageBox(IDS_MOUNT_POINT_ERROR, MB_ICONERROR, IDS_MOUNT_POINT_ERROR, GetAfsError(errno, strDir));
1554         return FALSE;
1555     }
1556
1557     return TRUE;
1558 }
1559
1560 /*
1561 */
1562 long fs_ExtractDriveLetter(const char *inPathp, char *outPathp)
1563 {
1564     if (inPathp[0] != 0 && inPathp[1] == ':') {
1565         /* there is a drive letter */
1566         *outPathp++ = *inPathp++;
1567         *outPathp++ = *inPathp++;
1568         *outPathp++ = 0;
1569     }
1570     else *outPathp = 0;
1571
1572     return 0;
1573 }
1574
1575 /* strip the drive letter from a component */
1576 long fs_StripDriveLetter(const char *inPathp, char *outPathp, long outSize)
1577 {
1578     char tempBuffer[1000];
1579     strcpy(tempBuffer, inPathp);
1580     if (tempBuffer[0] != 0 && tempBuffer[1] == ':') {
1581         /* drive letter present */
1582         strcpy(outPathp, tempBuffer+2);
1583     }
1584     else {
1585         /* no drive letter present */
1586         strcpy(outPathp, tempBuffer);
1587     }
1588     return 0;
1589 }
1590
1591
1592 BOOL RemoveSymlink(const CString& strName)
1593 {
1594     BOOL error = FALSE;
1595     INT code=0;
1596     struct ViceIoctl blob;
1597     char lsbuffer[1024];
1598
1599     HOURGLASS hourglass;
1600
1601     CString strParent = Parent(strName);
1602     CStringUtf8 ustrLast(LastComponent(strName));
1603     FixNetbiosPath(strParent);
1604
1605     if ( IsFreelanceRoot(strParent) && !IsAdmin() ) {
1606         ShowMessageBox(IDS_NOT_AFS_CLIENT_ADMIN_ERROR, MB_ICONERROR, IDS_NOT_AFS_CLIENT_ADMIN_ERROR);
1607         return FALSE;
1608     }
1609
1610     blob.in_size = ustrLast.GetLength() + 1;
1611     blob.in = ustrLast.GetBuffer();
1612     blob.out = lsbuffer;
1613     blob.out_size = sizeof(lsbuffer);
1614     code = pioctl_T(strParent, VIOC_LISTSYMLINK, &blob, 0);
1615     ustrLast.ReleaseBuffer();
1616     if (code)
1617         return FALSE;
1618     blob.out_size = 0;
1619     blob.in_size = ustrLast.GetLength() + 1;
1620     blob.in = ustrLast.GetBuffer();
1621
1622     code = pioctl_T(strParent, VIOC_DELSYMLINK, &blob, 0);
1623
1624     ustrLast.ReleaseBuffer();
1625
1626     return (code == 0);
1627 }
1628
1629 BOOL IsSymlink(const CString& strName)
1630 {
1631     struct ViceIoctl blob;
1632     int code;
1633
1634     HOURGLASS hourglass;
1635
1636     CStringUtf8 ustrLast(LastComponent(strName));
1637     CString strParent = Parent(strName);
1638
1639     FixNetbiosPath(strParent);
1640
1641     blob.in_size = ustrLast.GetLength() + 1;
1642     blob.in = ustrLast.GetBuffer();
1643     blob.out_size = AFS_PIOCTL_MAXSIZE;
1644     blob.out = space;
1645     memset(space, 0, AFS_PIOCTL_MAXSIZE);
1646
1647     code = pioctl_T(strParent, VIOC_LISTSYMLINK, &blob, 1);
1648
1649     ustrLast.ReleaseBuffer();
1650
1651     return (code==0);
1652 }
1653
1654
1655 BOOL IsMountPoint(const CString& path)
1656 {
1657     LONG code = 0;
1658     struct ViceIoctl blob;
1659     char lsbuffer[1024];
1660
1661     HOURGLASS hourglass;
1662
1663     CString parent = Parent(path);
1664     FixNetbiosPath(parent);
1665
1666     CStringUtf8 mountpoint(LastComponent(path));
1667
1668     blob.in_size = mountpoint.GetLength() + 1;
1669     blob.in = mountpoint.GetBuffer();
1670     blob.out = lsbuffer;
1671     blob.out_size = sizeof(lsbuffer);
1672
1673     code = pioctl_T(parent, VIOC_AFS_STAT_MT_PT, &blob, 0);
1674
1675     mountpoint.ReleaseBuffer();
1676
1677     return (code==0);
1678 }
1679
1680
1681 /*
1682  * Delete AFS mount points.  Variables are used as follows:
1683  *       tbuffer: Set to point to the null-terminated directory name of the mount point
1684  *          (or ``.'' if none is provided)
1685  *      tp: Set to point to the actual name of the mount point to nuke.
1686  */
1687 BOOL RemoveMount(CStringArray& files)
1688 {
1689     LONG code = 0;
1690     struct ViceIoctl blob;
1691     BOOL error = FALSE;
1692     CStringArray results;
1693     CString str;
1694     CString str2;
1695
1696     HOURGLASS hourglass;
1697
1698     for (int i = 0; i < files.GetSize(); i++) {
1699         if (!IsMountPoint(files[i])) {
1700             error = TRUE;
1701             if (errno == EINVAL)
1702                 results.Add(GetMessageString(IDS_NOT_MOUNT_POINT_ERROR, StripPath(files[i])));
1703             else
1704                 results.Add(GetMessageString(IDS_ERROR, GetAfsError(errno, StripPath(files[i]))));
1705             continue;   // don't bother trying
1706         }
1707
1708         CString parent = Parent(files[i]);
1709         CStringUtf8 mountpoint(LastComponent(files[i]));
1710         FixNetbiosPath(parent);
1711
1712         if ( IsFreelanceRoot(parent) && !IsAdmin() ) {
1713             results.Add(GetMessageString(IDS_NOT_AFS_CLIENT_ADMIN_ERROR, StripPath(files[i])));
1714             error = TRUE;
1715             continue;   /* skip */
1716         }
1717
1718         blob.out_size = 0;
1719         blob.in_size = mountpoint.GetLength() + 1;
1720         blob.in = mountpoint.GetBuffer();
1721
1722         code = pioctl_T(parent, VIOC_AFS_DELETE_MT_PT, &blob, 0);
1723
1724         mountpoint.ReleaseBuffer();
1725
1726         if (code) {
1727             error = TRUE;
1728             results.Add(GetMessageString(IDS_ERROR, GetAfsError(errno, StripPath(files[i]))));
1729         } else
1730             results.Add(GetMessageString(IDS_DELETED));
1731     }
1732
1733     LoadString (str, IDS_REMOVE_MP);
1734     LoadString (str2, IDS_REMOVE_MP_COLUMN);
1735     CResultsDlg dlg(REMOVE_MOUNT_POINTS_HELP_ID);
1736     dlg.SetContents(str, str2, StripPath(files), results);
1737     dlg.DoModal();
1738
1739     return !error;
1740 }
1741
1742 BOOL GetVolumeInfo(CString strFile, CVolInfo& volInfo)
1743 {
1744     LONG code;
1745     struct ViceIoctl blob;
1746     struct VolumeStatus *status;
1747     char *name;
1748
1749     HOURGLASS hourglass;
1750
1751     volInfo.m_strFilePath = strFile;
1752     volInfo.m_strFileName = StripPath(strFile);
1753
1754     /*
1755         volInfo.m_strName = "VolumeName";
1756         volInfo.m_nID = 10;
1757         volInfo.m_nQuota = 20 * 1024 * 1024;
1758         volInfo.m_nNewQuota = volInfo.m_nQuota;
1759         volInfo.m_nUsed = volInfo.m_nQuota / 2;
1760         volInfo.m_nPartSize = 50 * 1024 * 1024;
1761         volInfo.m_nPartFree = 30 * 1024 * 1024;
1762         volInfo.m_nDup = -1;
1763         return TRUE;
1764      */
1765
1766     blob.out_size = AFS_PIOCTL_MAXSIZE;
1767     blob.in_size = 0;
1768     blob.out = space;
1769
1770     code = pioctl_T(strFile, VIOCGETVOLSTAT, &blob, 1);
1771     if (code || blob.out_size < sizeof(*status)) {
1772         volInfo.m_strErrorMsg = GetAfsError(errno, strFile);
1773         return FALSE;
1774     }
1775
1776     status = (VolumeStatus *)space;
1777     name = (char *)status + sizeof(*status);
1778
1779     volInfo.m_strName = Utf8ToCString(name);
1780     volInfo.m_nID = status->Vid;
1781     volInfo.m_nQuota = status->MaxQuota;
1782     volInfo.m_nNewQuota = status->MaxQuota;
1783     volInfo.m_nUsed = status->BlocksInUse;
1784     volInfo.m_nPartSize = status->PartMaxBlocks;
1785     volInfo.m_nPartFree = status->PartBlocksAvail;
1786     volInfo.m_nDup = -1;
1787
1788     return TRUE;
1789 }
1790
1791 BOOL SetVolInfo(CVolInfo& volInfo)
1792 {
1793     LONG code;
1794     struct ViceIoctl blob;
1795     struct VolumeStatus *status;
1796     char *input;
1797
1798     HOURGLASS hourglass;
1799
1800     blob.out_size = AFS_PIOCTL_MAXSIZE;
1801     blob.in_size = sizeof(*status) + 3; /* for the three terminating nulls */
1802     blob.out = space;
1803     blob.in = space;
1804
1805     status = (VolumeStatus *)space;
1806     status->MinQuota = -1;
1807     status->MaxQuota = volInfo.m_nNewQuota;
1808
1809     input = (char *)status + sizeof(*status);
1810     *(input++) = '\0';  /* never set name: this call doesn't change vldb */
1811     *(input++) = '\0';  // No offmsg
1812     *(input++) = '\0';  // No motd
1813
1814 #ifdef LOGGING_ON
1815     FILE *fp = OpenFile(szLogFileName, "a");
1816     if (fp) {
1817         fprintf(fp, "\nSetVolInfo() pioctl parms:\n");
1818         fprintf(fp, "\tpathp = %s\n\topcode = VIOCSETVOLSTAT (%d)\n\tblobp = %ld\n", PCCHAR(volInfo.m_strFilePath), VIOCSETVOLSTAT, &blob);
1819         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);
1820         fprintf(fp, "\t\t\tstatus->MinQuota = %ld\n", status->MinQuota);
1821         fprintf(fp, "\t\t\tstatus->MaxQuota = %ld\n", status->MaxQuota);
1822         fprintf(fp, "\t\t\tOther status fields aren't set\n");
1823         fprintf(fp, "\t\t\t3 nulls follow the VolumeStatus structure.\n");
1824         fprintf(fp, "\tfollow = 1\n");
1825         fclose(fp);
1826     }
1827 #endif
1828
1829     code = pioctl_T(volInfo.m_strFilePath, VIOCSETVOLSTAT, &blob, 1);
1830     if (code || blob.out_size < sizeof(*status)) {
1831         ShowMessageBox(IDS_SET_QUOTA_ERROR, MB_ICONERROR, IDS_SET_QUOTA_ERROR, GetAfsError(errno, volInfo.m_strName));
1832         return FALSE;
1833     }
1834
1835     return TRUE;
1836 }
1837
1838 void GetCellName(const CString& cellNamep, struct afsconf_cell *infop)
1839 {
1840     CStringUtf8 uCellName(cellNamep);
1841
1842     StringCbCopyA(infop->name, sizeof(infop->name), uCellName);
1843 }
1844
1845 BOOL CheckServers(const CString& strCellName, WHICH_CELLS nCellsToCheck, BOOL bFast)
1846 {
1847     LONG code;
1848     struct ViceIoctl blob;
1849     LONG j;
1850     LONG temp = 0;
1851     struct afsconf_cell info;
1852     struct chservinfo checkserv;
1853
1854     HOURGLASS hourglass;
1855
1856     memset(&checkserv, 0, sizeof(struct chservinfo));
1857     blob.in_size = sizeof(struct chservinfo);
1858     blob.in = (caddr_t)&checkserv;
1859
1860     blob.out_size = AFS_PIOCTL_MAXSIZE;
1861     blob.out = space;
1862     memset(space, 0, sizeof(afs_int32));        /* so we assure zero when nothing is copied back */
1863
1864     if (nCellsToCheck == SPECIFIC_CELL) {
1865         temp = 2;
1866         GetCellName(strCellName, &info);
1867         strcpy(checkserv.tbuffer,info.name);
1868         checkserv.tsize = strlen(info.name) + 1;
1869     } else {
1870         if (nCellsToCheck != ALL_CELLS)
1871             temp = 2;
1872         strcpy(checkserv.tbuffer, "\0");
1873         checkserv.tsize = 0;
1874     }
1875     if (bFast)
1876         temp |= 1;      /* set fast flag */
1877
1878     checkserv.magic = 0x12345678;       /* XXX */
1879     checkserv.tflags = temp;
1880     checkserv.tinterval = -1;   /* don't change current interval */
1881
1882     code = pioctl_utf8(0, VIOCCKSERV, &blob, 1);
1883     if (code) {
1884         ShowMessageBox(IDS_CHECK_SERVERS_ERROR, MB_ICONERROR, IDS_CHECK_SERVERS_ERROR, GetAfsError(errno, CString()));
1885         return FALSE;
1886     }
1887
1888     memcpy(&temp, space, sizeof(LONG));
1889
1890     if (temp == 0) {
1891         ShowMessageBox(IDS_ALL_SERVERS_RUNNING, MB_OK|MB_ICONINFORMATION, IDS_ALL_SERVERS_RUNNING);
1892         return TRUE;
1893     }
1894
1895     CStringArray servers;
1896     for (j = 0; j < AFS_MAXHOSTS; j++) {
1897         memcpy(&temp, space + j * sizeof(LONG), sizeof(LONG));
1898         if (temp == 0)
1899             break;
1900
1901         char *name = hostutil_GetNameByINet(temp);
1902         servers.Add(name);
1903     }
1904
1905     CDownServersDlg dlg;
1906     dlg.SetServerNames(servers);
1907     dlg.DoModal();
1908
1909     return TRUE;
1910 }
1911
1912 BOOL GetTokenInfo(CStringArray& tokenInfo)
1913 {
1914     int cellNum;
1915     int rc;
1916     time_t current_time;
1917     time_t tokenExpireTime;
1918     char *expireString;
1919     char userName[100];
1920 //      char s[100];
1921     struct ktc_principal serviceName, clientName;
1922     struct ktc_token token;
1923
1924     CString strTokenInfo;
1925     CString strUserName;
1926     CString strCellName;
1927     CString strExpir;
1928
1929 //      tokenInfo.Add("");
1930 //      return TRUE;
1931
1932
1933     HOURGLASS hourglass;
1934
1935 //      printf("\nTokens held by the Cache Manager:\n\n");
1936     cellNum = 0;
1937     current_time = time(0);
1938
1939     while (1) {
1940         rc = ktc_ListTokens(cellNum, &cellNum, &serviceName);
1941         if (rc == KTC_NOENT) {
1942             /* end of list */
1943 //          printf("   --End of list --\n");
1944             break;
1945         }
1946         else if (rc == KTC_NOCM) {
1947             ShowMessageBox(IDS_GET_TOKENS_NO_AFS_SERVICE);
1948 //          printf("AFS service may not have started\n");
1949             break;
1950         }
1951         else if (rc) {
1952             ShowMessageBox(IDS_GET_TOKENS_UNEXPECTED_ERROR, MB_ICONERROR, IDS_GET_TOKENS_UNEXPECTED_ERROR, rc);
1953             return FALSE;
1954 //          printf("Unexpected error, code %d\n", rc);
1955 //          exit(1);
1956         }
1957         else {
1958             rc = ktc_GetToken(&serviceName, &token, sizeof(token), &clientName);
1959             if (rc) {
1960                 ShowMessageBox(IDS_GET_TOKENS_UNEXPECTED_ERROR2, MB_ICONERROR, IDS_GET_TOKENS_UNEXPECTED_ERROR2,
1961                                 serviceName.name, serviceName.instance, serviceName.cell, rc);
1962                 continue;
1963             }
1964
1965             tokenExpireTime = token.endTime;
1966
1967             strcpy(userName, clientName.name);
1968             if (clientName.instance[0] != 0) {
1969                 strcat(userName, ".");
1970                 strcat(userName, clientName.instance);
1971             }
1972
1973             BOOL bShowName = FALSE;
1974
1975             if (userName[0] == '\0')
1976                 ; //printf("Tokens");
1977 // AFS ID is not returned at this time.
1978 //          else if (strncmp(userName, "AFS ID", 6) == 0)
1979 //              printf("User's (%s) tokens", userName);
1980 //              sscanf(userName, "(AFS ID %s)", szAfsID);
1981             else if (strncmp(userName, "Unix UID", 8) == 0)
1982                 ; //printf("Tokens");
1983             else
1984                 strUserName = userName;
1985 //              printf("User %s's tokens", userName);
1986
1987 //              printf(" for %s%s%s@%s ", serviceName.name, serviceName.instance[0] ? "." : "", serviceName.instance, serviceName.cell);
1988             strCellName = serviceName.cell;
1989
1990             if (tokenExpireTime <= current_time)
1991                 strExpir = "[>> Expired <<]";
1992 //              printf("[>> Expired <<]\n");
1993             else {
1994                 expireString = ctime(&tokenExpireTime);
1995                 expireString += 4;       /* Skip day of week */
1996                 expireString[12] = '\0'; /* Omit secs & year */
1997 //              printf("[Expires %s]\n", expireString);
1998 #ifdef UNICODE
1999                 strExpir.Format(_T("%S"), expireString);
2000 #else
2001                 strExpir.Format(_T("%s"), expireString);
2002 #endif
2003             }
2004
2005             strTokenInfo = strUserName + "\t" + strCellName + "\t" + strExpir + "\t" + strCellName;
2006             tokenInfo.Add(strTokenInfo);
2007         }
2008     }
2009
2010 //      printf("Press <Enter> or <Return> when finished: ");
2011 //      gets(s);
2012     return TRUE;
2013 }
2014
2015 UINT MakeSymbolicLink(const CString& strName, const CString& strTarget)
2016 {
2017     struct ViceIoctl blob;
2018     UINT code;
2019     HOURGLASS hourglass;
2020
2021     CString strParent = Parent(strName);
2022     FixNetbiosPath(strParent);
2023
2024     if ( IsFreelanceRoot(strParent) && !IsAdmin() ) {
2025         ShowMessageBox(IDS_NOT_AFS_CLIENT_ADMIN_ERROR, MB_ICONERROR, IDS_NOT_AFS_CLIENT_ADMIN_ERROR);
2026         return FALSE;
2027     }
2028
2029     CStringUtf8 ustrTarget(strTarget);
2030
2031     blob.in_size = ustrTarget.GetLength() + 1;
2032     blob.in = ustrTarget.GetBuffer();
2033     blob.out_size = 0;
2034     blob.out = NULL;
2035
2036     code = pioctl_T(strName, VIOC_SYMLINK, &blob, 0);
2037
2038     ustrTarget.ReleaseBuffer();
2039
2040     if (code != 0)
2041         return code;
2042     return 0;
2043 }
2044
2045 void ListSymbolicLinkPath(const char *strName,char *strPath,UINT nlenPath)
2046 {
2047     ASSERT(nlenPath<MAX_PATH);
2048     struct ViceIoctl blob;
2049     char orig_name[MAX_PATH+1];         /*Original name, may be modified*/
2050     char true_name[MAX_PATH+1];         /*``True'' dirname (e.g., symlink target)*/
2051     char parent_dir[MAX_PATH+1];                /*Parent directory of true name*/
2052     char *last_component;       /*Last component of true name*/
2053     UINT code;
2054
2055     HOURGLASS hourglass;
2056
2057     strcpy(orig_name, strName);
2058     strcpy(true_name, orig_name);
2059     /*
2060      * Find rightmost slash, if any.
2061      */
2062     last_component = (char *) strrchr(true_name, '\\');
2063     if (!last_component)
2064         last_component = (char *) strrchr(true_name, '/');
2065     if (last_component) {
2066         /*
2067          * Found it.  Designate everything before it as the parent directory,
2068          * everything after it as the final component.
2069          */
2070         strncpy(parent_dir, true_name, last_component - true_name + 1);
2071         parent_dir[last_component - true_name + 1] = 0;
2072         last_component++;   /*Skip the slash*/
2073
2074         if (!IsPathInAfs(parent_dir)) {
2075             const char * nbname = NetbiosName();
2076             int len = strlen(nbname);
2077
2078             if (parent_dir[0] == '\\' && parent_dir[1] == '\\' &&
2079                  parent_dir[len+2] == '\\' &&
2080                  parent_dir[len+3] == '\0' &&
2081                  !strnicmp(nbname,&parent_dir[2],len))
2082             {
2083                 sprintf(parent_dir,"\\\\%s\\all\\", nbname);
2084             }
2085         }
2086     }
2087     else {
2088         /*
2089          * No slash appears in the given file name.  Set parent_dir to the current
2090          * directory, and the last component as the given name.
2091          */
2092         fs_ExtractDriveLetter(true_name, parent_dir);
2093         strcat(parent_dir, ".");
2094         last_component = true_name;
2095         fs_StripDriveLetter(true_name, true_name, sizeof(true_name));
2096     }
2097     blob.in = last_component;
2098     blob.in_size = strlen(last_component)+1;
2099     blob.out_size = AFS_PIOCTL_MAXSIZE;
2100     blob.out = space;
2101     memset(space, 0, AFS_PIOCTL_MAXSIZE);
2102     if ((code = pioctl(parent_dir, VIOC_LISTSYMLINK, &blob, 1)))
2103         strcpy(space,"???");
2104     ASSERT(strlen(space)<MAX_PATH);
2105     strncpy(strPath,space,nlenPath);
2106 }
2107
2108 BOOL ListSymlink(CStringArray& files)
2109 {
2110     LONG code;
2111     struct ViceIoctl blob;
2112     int error;
2113     CStringArray symlinks;
2114
2115     HOURGLASS hourglass;
2116
2117     error = 0;
2118
2119     for (int i = 0; i < files.GetSize(); i++) {
2120
2121         CString strParent = Parent(files[i]);
2122         CStringUtf8 ustrLast(LastComponent(files[i]));
2123
2124         FixNetbiosPath(strParent);
2125
2126         blob.in_size = ustrLast.GetLength() + 1;
2127         blob.in = ustrLast.GetBuffer();
2128         blob.out_size = AFS_PIOCTL_MAXSIZE;
2129         blob.out = space;
2130         memset(space, 0, AFS_PIOCTL_MAXSIZE);
2131
2132         code = pioctl_T(strParent, VIOC_LISTSYMLINK, &blob, 1);
2133
2134         ustrLast.ReleaseBuffer();
2135
2136         if (code == 0) {
2137             CString syml;
2138             int len;
2139
2140             space[AFS_PIOCTL_MAXSIZE - 1] = '\0';
2141             syml = Utf8ToCString(space);
2142             len = syml.GetLength();
2143
2144             if (len > 0) {
2145                 if (syml[len - 1] == _T('.'))
2146                     syml.Truncate(len - 1);
2147             }
2148
2149             symlinks.Add(ParseSymlink(StripPath(files[i]), syml));
2150
2151         } else {
2152             error = 1;
2153             if (errno == EINVAL)
2154                 symlinks.Add(GetMessageString(IDS_NOT_SYMLINK_ERROR, StripPath(files[i])));
2155             else
2156                 symlinks.Add(GetMessageString(IDS_LIST_MOUNT_POINT_ERROR, GetAfsError(errno, StripPath(files[i]))));
2157         }
2158     }
2159
2160     CSymlinksDlg dlg;
2161     dlg.SetSymlinks(symlinks);
2162     dlg.DoModal();
2163
2164     return !error;
2165 }
2166