Windows: Explorer Shell Set Unix Mode bits
[openafs.git] / src / WINNT / client_exp / gui2fs.cpp
index 87a1ef0..6ba5c53 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright 2000, International Business Machines Corporation and others.
  * All Rights Reserved.
- * 
+ *
  * This software has been released under the terms of the IBM Public
  * License.  For details, see the LICENSE file in the top-level source
  * directory or online at http://www.openafs.org/dl/license10.html
@@ -12,7 +12,9 @@
 #include <ws2tcpip.h>
 
 extern "C" {
+#include <afsconfig.h>
 #include <afs/param.h>
+#include <roken.h>
 #include <afs/stds.h>
 }
 
@@ -30,11 +32,13 @@ extern "C" {
 
 extern "C" {
 #include <rx/rx_globals.h>
-#include "fs.h"
 #include "fs_utils.h"
+#include "fs_acl.h"
 #include <afs/afsint.h>
 #include <afs/afs_consts.h>
 #include <afs/cellconfig.h>
+#include <afs/ptserver.h>
+#include <afs/ptuser.h>
 #include <afs/vldbint.h>
 #include <afs/volser.h>
 #include <afs/auth.h>
@@ -45,11 +49,19 @@ extern "C" {
 #include <cm_user.h>
 #include <cm_scache.h>
 #include <cm_ioctl.h>
+#include <cm_config.h>
 }
 
 #define STRSAFE_NO_DEPRECATE
 #include <strsafe.h>
 
+/*
+ * the NO_CALLER symbol is used to document functions
+ * that are present in this file but have no caller
+ */
+#define NO_CALLER
+
+
 #define PCCHAR(str)            ((char *)(const char *)(str))
 #define VL_NOENT                (363524L)
 
@@ -61,9 +73,6 @@ extern "C" {
 #define MAXHOSTCHARS           64
 #define MAXHOSTSPERCELL                8
 
-static char space[AFS_PIOCTL_MAXSIZE];
-static char tspace[1024];
-
 static struct ubik_client *uclient;
 static int rxInitDone = 0;
 static char pn[] = "fs";
@@ -136,7 +145,8 @@ public:
     }
 };
 
-long pioctl_T(const CString& path, long opcode, struct ViceIoctl * blob, int follow)
+long
+pioctl_T(const CString& path, long opcode, struct ViceIoctl * blob, int follow)
 {
     CStringUtf8 upath(path);
 
@@ -149,15 +159,13 @@ long pioctl_T(const CString& path, long opcode, struct ViceIoctl * blob, int fol
 #define Utf8ToCString(cs) (cs)
 #endif
 
-
-
-static int
+static int NO_CALLER
 VLDBInit(int noAuthFlag, struct afsconf_cell *info)
 {
     afs_int32 code;
 
-    code = ugen_ClientInit(noAuthFlag, (char *)AFSDIR_CLIENT_ETC_DIRPATH, 
-                          info->name, 0, &uclient, 
+    code = ugen_ClientInit(noAuthFlag, (char *)AFSDIR_CLIENT_ETC_DIRPATH,
+                          info->name, 0, &uclient,
                            NULL, pn, rxkad_clear,
                            VLDB_MAXSERVERS, AFSCONF_VLDBSERVICE, 50,
                            0, 0, USER_SERVICE_ID);
@@ -174,7 +182,7 @@ OpenFile(char *file, char *rwp)
     FILE *fp;
 
     code = GetWindowsDirectoryA(wdir, sizeof(wdir));
-    if (code == 0 || code > sizeof(wdir)) 
+    if (code == 0 || code > sizeof(wdir))
         return FALSE;
 
     /* add trailing backslash, if required */
@@ -187,9 +195,9 @@ OpenFile(char *file, char *rwp)
     fp = fopen(wdir, rwp);
 
     return fp;
-}       
+}
 
-CString StripPath(CString& strPath)
+CString StripPath(const CString& strPath)
 {
     int nIndex = strPath.ReverseFind('\\');
 
@@ -200,7 +208,8 @@ CString StripPath(CString& strPath)
     return strFile;
 }
 
-CStringArray& StripPath(CStringArray& files)
+CStringArray&
+StripPath(CStringArray& files)
 {
     for (int i = 0; i < files.GetSize(); i++)
         files[i] = StripPath(files[i]);
@@ -208,7 +217,8 @@ CStringArray& StripPath(CStringArray& files)
     return files;
 }
 
-void Flush(const CStringArray& files)
+void
+Flush(const CStringArray& files)
 {
     LONG code;
     struct ViceIoctl blob;
@@ -224,16 +234,17 @@ void Flush(const CStringArray& files)
             error = 1;
             if (errno == EMFILE)
                 ShowMessageBox(IDS_FLUSH_FAILED, MB_ICONERROR, IDS_FLUSH_FAILED, files[i]);
-            else 
+            else
                 ShowMessageBox(IDS_FLUSH_ERROR, MB_ICONERROR, IDS_FLUSH_ERROR, files[i], strerror(errno));
         }
-    }   
+    }
 
     if (!error)
         ShowMessageBox(IDS_FLUSH_OK, MB_ICONINFORMATION, IDS_FLUSH_OK);
-}       
+}
 
-void FlushVolume(const CStringArray& files)
+void
+FlushVolume(const CStringArray& files)
 {
     LONG code;
     struct ViceIoctl blob;
@@ -249,13 +260,14 @@ void FlushVolume(const CStringArray& files)
             error = 1;
             ShowMessageBox(IDS_FLUSH_VOLUME_ERROR, MB_ICONERROR, IDS_FLUSH_VOLUME_ERROR, files[i], strerror(errno));
         }
-    }   
+    }
 
     if (!code)
         ShowMessageBox(IDS_FLUSH_VOLUME_OK, MB_ICONINFORMATION, IDS_FLUSH_VOLUME_OK);
-}       
+}
 
-void WhichCell(CStringArray& files)
+void NO_CALLER
+WhichCell(CStringArray& files)
 {
     LONG code;
     struct ViceIoctl blob;
@@ -270,6 +282,8 @@ void WhichCell(CStringArray& files)
     HOURGLASS hourglass;
 
     for (int i = 0; i < files.GetSize(); i++) {
+        char space[AFS_PIOCTL_MAXSIZE];
+
         blob.in_size = 0;
         blob.out_size = AFS_PIOCTL_MAXSIZE;
         blob.out = space;
@@ -285,7 +299,7 @@ void WhichCell(CStringArray& files)
             space[AFS_PIOCTL_MAXSIZE - 1] = '\0';
             results.Add(Utf8ToCString(space));
         }
-    }       
+    }
 
     LoadString (str, IDS_SHOW_CELL);
     LoadString (str2, IDS_SHOW_CELL_COLUMN);
@@ -294,11 +308,13 @@ void WhichCell(CStringArray& files)
     dlg.DoModal();
 }
 
-void WSCellCmd()
+void NO_CALLER
+WSCellCmd(void)
 {
+    char space[AFS_PIOCTL_MAXSIZE];
     LONG code;
     struct ViceIoctl blob;
-    
+
     HOURGLASS hourglass;
 
     blob.in_size = 0;
@@ -308,18 +324,18 @@ void WSCellCmd()
 
     code = pioctl((char *) 0, VIOC_GET_WS_CELL, &blob, 1);
 
-    if (code) {
-        //Die(errno, (char *) 0);
-    }
-    //else
-    //printf("This workstation belongs to cell '%s'\n", space);
+    /*
+     * Cell name is left in 'space' as side effect.
+     * At present no callers of this function.
+     */
 }
 
-BOOL CheckVolumes()
+BOOL NO_CALLER
+CheckVolumes(void)
 {
     LONG code;
     struct ViceIoctl blob;
-    
+
     blob.in_size = 0;
     blob.out_size = 0;
     code = pioctl(0, VIOCCKBACK, &blob, 1);
@@ -333,11 +349,12 @@ BOOL CheckVolumes()
     return TRUE;
 }
 
-void SetCacheSizeCmd(LONG nNewCacheSize)
+void NO_CALLER
+SetCacheSizeCmd(LONG nNewCacheSize)
 {
     LONG code;
     struct ViceIoctl blob;
-    
+
     HOURGLASS hourglass;
 
     blob.in = (char *) &nNewCacheSize;
@@ -345,13 +362,12 @@ void SetCacheSizeCmd(LONG nNewCacheSize)
     blob.out_size = 0;
 
     code = pioctl(0, VIOCSETCACHESIZE, &blob, 1);
-    //if (code)
-    // Die(errno, (char *) 0);
-    //else
-    // printf("New cache size set.\n");
+
+    /* error handling? */
 }
 
-void WhereIs(CStringArray& files)
+void NO_CALLER
+WhereIs(CStringArray& files)
 {
     LONG code;
     struct ViceIoctl blob;
@@ -363,6 +379,8 @@ void WhereIs(CStringArray& files)
     HOURGLASS hourglass;
 
     for (int i = 0; i < files.GetSize(); i++) {
+        char space[AFS_PIOCTL_MAXSIZE];
+
         blob.out_size = AFS_PIOCTL_MAXSIZE;
         blob.in_size = 0;
         blob.out = space;
@@ -397,7 +415,7 @@ void WhereIs(CStringArray& files)
     CResultsDlg dlg(SHOW_FILE_SERVERS_HELP_ID);
     dlg.SetContents(str, str2, resultFiles, servers);
     dlg.DoModal();
-}       
+}
 
 static int
 CMtoUNIXerror(int cm_code)
@@ -441,7 +459,8 @@ CMtoUNIXerror(int cm_code)
     }
 }
 
-CString GetAfsError(int code, const TCHAR *filename)
+CString
+GetAfsError(int code, const TCHAR *filename)
 {
     CString strMsg;
 
@@ -450,12 +469,12 @@ CString GetAfsError(int code, const TCHAR *filename)
     if (code == EINVAL) {
         if (filename)
             strMsg.Format(_T("Invalid argument; it is possible that the file is not in AFS"));
-        else 
+        else
             strMsg.Format(_T("Invalid argument"));
     } else if (code == ENOENT) {
-        if (filename) 
+        if (filename)
             strMsg.Format(_T("The file does not exist"));
-        else 
+        else
             strMsg.Format(_T("No such file returned"));
     } else if (code == EROFS)  {
         strMsg.Format(_T("You can not change a backup or readonly volume"));
@@ -477,30 +496,8 @@ CString GetAfsError(int code, const TCHAR *filename)
 }
 
 
-/************************************************************************
-************************** ACL Code *************************************
-************************************************************************/
-
-typedef char sec_rgy_name_t[1025];     /* A DCE definition */
-
-struct AclEntry {
-    struct AclEntry *next;
-    char name[MAXNAME];
-    LONG rights;
-};
-
-struct Acl {
-    int dfs;                   //      Originally true if a dfs acl; now also the type
-                                //     of the acl (1, 2, or 3, corresponding to object,
-                               //      initial dir, or initial object).
-    sec_rgy_name_t cell;       //      DFS cell name
-    int nplus;
-    int nminus;
-    struct AclEntry *pluslist;
-    struct AclEntry *minuslist;
-};
-
-int foldcmp (char *a, char *b)
+static int
+foldcmp (char *a, char *b)
 {
     char t, u;
     while (1) {
@@ -513,70 +510,8 @@ int foldcmp (char *a, char *b)
     }
 }
 
-extern "C" void ZapList(struct AclEntry *alist)
-{
-    struct AclEntry *tp, *np;
-
-    for (tp = alist; tp; tp = np) {
-        np = tp->next;
-        free(tp);
-    }
-}
-
-extern "C" void ZapAcl (struct Acl *acl)
-{
-    ZapList(acl->pluslist);
-    ZapList(acl->minuslist);
-    free(acl);
-}
-
-extern "C" int PruneList (struct AclEntry **ae, int dfs)
-{
-    struct AclEntry **lp = ae;
-    struct AclEntry *te, *ne;
-    LONG ctr = 0;
-    
-    for (te = *ae; te; te = ne) {
-        if ((!dfs && te->rights == 0) || te->rights == -1) {
-            *lp = te->next;
-            ne = te->next;
-            free(te);
-            ctr++;
-        }
-        else {
-            ne = te->next;
-            lp = &te->next;
-        }
-    }
-    
-    return ctr;
-}
-
-char *SkipLine (char *astr)
-{
-    while (*astr != '\n') 
-        astr++;
-    
-    astr++;
-    
-    return astr;
-}
-
-/* tell if a name is 23 or -45 (digits or minus digits), which are bad names we must prune */
-static int BadName(char *aname)
-{
-    int tc;
-
-    /* all must be '-' or digit to be bad */
-    while (tc = *aname++) {
-        if ((tc != '-') && (tc < '0' || tc > '9')) 
-            return 0;
-    }
-
-    return 1;
-}
-
-CString GetRightsString(LONG arights, int dfs)
+CString
+GetRightsString(LONG arights, int dfs)
 {
     CString str;
 
@@ -599,42 +534,17 @@ CString GetRightsString(LONG arights, int dfs)
                if (arights & DFS_DELETE) str += _T("d"); else printf(_T("-"));
                if (arights & (DFS_USRALL)) str += _T("+");
 */
-    }  
+    }
 
     return str;
 }
 
-char *AclToString(struct Acl *acl)
-{
-    static char mydata[AFS_PIOCTL_MAXSIZE];
-    char tstring[AFS_PIOCTL_MAXSIZE];
-    char dfsstring[30];
-    struct AclEntry *tp;
-    
-    if (acl->dfs)
-        sprintf(dfsstring, " dfs:%d %s", acl->dfs, acl->cell);
-    else
-        dfsstring[0] = '\0';
-    sprintf(mydata, "%d%s\n%d\n", acl->nplus, dfsstring, acl->nminus);
-    
-    for(tp = acl->pluslist; tp; tp = tp->next) {
-        sprintf(tstring, "%s %d\n", tp->name, tp->rights);
-        strcat(mydata, tstring);
-    }
-    
-    for(tp = acl->minuslist; tp; tp = tp->next) {
-        sprintf(tstring, "%s %d\n", tp->name, tp->rights);
-        strcat(mydata, tstring);
-    }
-    
-    return mydata;
-}
-
-struct Acl *EmptyAcl(const CString& strCellName)
+struct Acl *
+EmptyAcl(const CString& strCellName)
 {
     struct Acl *tp;
     CStringUtf8 ustrCell(strCellName);
-    
+
     tp = (struct Acl *)malloc(sizeof (struct Acl));
     tp->nplus = tp->nminus = 0;
     tp->pluslist = tp->minuslist = 0;
@@ -644,158 +554,8 @@ struct Acl *EmptyAcl(const CString& strCellName)
     return tp;
 }
 
-struct Acl *EmptyAcl(char *astr)
-{
-    struct Acl *tp;
-    int junk;
-
-    tp = (struct Acl *)malloc(sizeof (struct Acl));
-    tp->nplus = tp->nminus = 0;
-    tp->pluslist = tp->minuslist = 0;
-    tp->dfs = 0;
-    if (astr == NULL || sscanf(astr, "%d dfs:%d %s", &junk, &tp->dfs, tp->cell) <= 0) {
-        tp->dfs = 0;
-        tp->cell[0] = '\0';
-    }
-    return tp;
-}
-
-struct Acl *
-ParseAcl (char *astr)
-{
-    int nplus, nminus, i, trights, ret;
-    char tname[MAXNAME];
-    struct AclEntry *first, *next, *last, *tl;
-    struct Acl *ta;
-
-    ta = EmptyAcl(NULL);
-    if (astr == NULL || strlen(astr) == 0)
-        return ta;
-
-    ret = sscanf(astr, "%d dfs:%d %s", &ta->nplus, &ta->dfs, ta->cell);
-    if (ret <= 0) {
-        free(ta);
-        return NULL;
-    }
-    astr = SkipLine(astr);
-    ret = sscanf(astr, "%d", &ta->nminus);
-    if (ret <= 0) {
-        free(ta);
-        return NULL;
-    }
-    astr = SkipLine(astr);
-
-    nplus = ta->nplus;
-    nminus = ta->nminus;
-
-    last = 0;
-    first = 0;
-    for(i=0;i<nplus;i++) {
-        ret = sscanf(astr, "%100s %d", tname, &trights); 
-        if (ret <= 0)
-            goto nplus_err;
-        astr = SkipLine(astr);
-        tl = (struct AclEntry *) malloc(sizeof (struct AclEntry));
-        if (tl == NULL)
-            goto nplus_err;
-        if (!first) 
-            first = tl;
-        strcpy(tl->name, tname);
-        tl->rights = trights;
-        tl->next = 0;
-        if (last) 
-            last->next = tl;
-        last = tl;
-    }
-    ta->pluslist = first;
-
-    last = 0;
-    first = 0;
-    for(i=0;i<nminus;i++) {
-        ret = sscanf(astr, "%100s %d", tname, &trights);
-        if (ret <= 0)
-            goto nminus_err;
-        astr = SkipLine(astr);
-        tl = (struct AclEntry *) malloc(sizeof (struct AclEntry));
-        if (tl == NULL)
-            goto nminus_err;
-        if (!first) 
-            first = tl;
-        strcpy(tl->name, tname);
-        tl->rights = trights;
-        tl->next = 0;
-        if (last) 
-            last->next = tl;
-        last = tl;
-    }
-    ta->minuslist = first;
-
-    return ta;
-
-  nminus_err:
-    for (;first; first = next) {
-        next = first->next;
-        free(first);
-    }   
-    first = ta->pluslist;
-
-  nplus_err:
-    for (;first; first = next) {
-        next = first->next;
-        free(first);
-    }   
-    free(ta);
-    return NULL;
-}
-
-/* clean up an access control list of its bad entries; return 1 if we made
-   any changes to the list, and 0 otherwise */
-extern "C" int CleanAcl(struct Acl *aa)
-{
-    struct AclEntry *te, **le, *ne;
-    int changes;
-
-    HOURGLASS hourglass;
-
-    /* Don't correct DFS ACL's for now */
-    if (aa->dfs)
-        return 0;
-
-    /* prune out bad entries */
-    changes = 0;           /* count deleted entries */
-    le = &aa->pluslist;
-    for(te = aa->pluslist; te; te = ne) {
-        ne = te->next;
-        if (BadName(te->name)) {
-            /* zap this dude */
-            *le = te->next;
-            aa->nplus--;
-            free(te);
-            changes++;
-        }
-        else
-            le = &te->next;
-    }
-
-    le = &aa->minuslist;
-    
-    for(te = aa->minuslist; te; te = ne) {
-        ne = te->next;
-        if (BadName(te->name)) {
-            /* zap this dude */
-            *le = te->next;
-            aa->nminus--;
-            free(te);
-            changes++;
-        }
-        else
-            le = &te->next;
-    }   
-
-    return changes;
-}
-
-void CleanACL(CStringArray& names)
+void
+CleanACL(CStringArray& names)
 {
     LONG code;
     struct Acl *ta;
@@ -807,6 +567,8 @@ void CleanACL(CStringArray& names)
     HOURGLASS hourglass;
 
     for (int i = 0; i < names.GetSize(); i++) {
+        char space[AFS_PIOCTL_MAXSIZE];
+
         blob.out_size = AFS_PIOCTL_MAXSIZE;
         blob.in_size = 0;
         blob.out = space;
@@ -817,7 +579,7 @@ void CleanACL(CStringArray& names)
             continue;
         }
 
-        ta = ParseAcl(space);
+        ta = ParseAcl(space, AFS_PIOCTL_MAXSIZE);
         if (ta == NULL) {
             ShowMessageBox(IDS_INVALID_ACL_DATA, MB_ICONERROR, IDS_INVALID_ACL_DATA);
             continue;
@@ -827,7 +589,7 @@ void CleanACL(CStringArray& names)
             continue;
         }
 
-        changes = CleanAcl(ta);
+        changes = CleanAcl(ta, NULL);
         if (!changes)
             continue;
 
@@ -835,7 +597,7 @@ void CleanACL(CStringArray& names)
         blob.in = AclToString(ta);
         blob.in_size = strlen((char *)blob.in) + 1;
         blob.out_size = 0;
-               
+
         code = pioctl_T(names[i], VIOCSETAL, &blob, 1);
         if (code) {
             if (errno == EINVAL) {
@@ -848,30 +610,31 @@ void CleanACL(CStringArray& names)
             }
         }
     }
-}       
+}
 
 // Derived from fs.c's ListAclCmd
-BOOL GetRights(const CString& strDir, CStringArray& strNormal, CStringArray& strNegative)
+BOOL
+GetRights(const CString& strDir, CStringArray& strNormal, CStringArray& strNegative)
 {
     LONG code;
     struct Acl *ta;
     struct ViceIoctl blob;
     struct AclEntry *te;
     int idf = 0; //getidf(as, parm_listacl_id);
-
+    char space[AFS_PIOCTL_MAXSIZE];
     HOURGLASS hourglass;
 
     blob.out_size = AFS_PIOCTL_MAXSIZE;
     blob.in_size = idf;
     blob.in = blob.out = space;
-       
+
     code = pioctl_T(strDir, VIOCGETAL, &blob, 1);
     if (code) {
         ShowMessageBox(IDS_GETRIGHTS_ERROR, MB_ICONERROR, IDS_GETRIGHTS_ERROR, strDir, GetAfsError(errno));
         return FALSE;
     }
 
-    ta = ParseAcl(space);
+    ta = ParseAcl(space, AFS_PIOCTL_MAXSIZE);
     if (ta == NULL) {
         ShowMessageBox(IDS_INVALID_ACL_DATA, MB_ICONERROR, IDS_INVALID_ACL_DATA);
         return FALSE;
@@ -903,22 +666,24 @@ BOOL GetRights(const CString& strDir, CStringArray& strNormal, CStringArray& str
     return TRUE;
 }
 
-struct AclEntry *FindList(struct AclEntry *pCurEntry, const char *entryName)
+struct AclEntry *
+FindList(struct AclEntry *pCurEntry, const char *entryName)
 {
     while (pCurEntry) {
         if (!foldcmp(pCurEntry->name, PCCHAR(entryName)))
             return pCurEntry;
         pCurEntry = pCurEntry->next;
     }
-    
+
     return 0;
 }
 
-void ChangeList(struct Acl *pAcl, BYTE bNormalRights, const CString & entryName, LONG nEntryRights)
+void
+ChangeList(struct Acl *pAcl, BYTE bNormalRights, const CString & entryName, LONG nEntryRights)
 {
     ASSERT(pAcl);
     ASSERT(entryName);
-    
+
     struct AclEntry *pEntry;
     CStringUtf8 uEntryName(entryName);
 
@@ -940,10 +705,10 @@ void ChangeList(struct Acl *pAcl, BYTE bNormalRights, const CString & entryName,
     /* Otherwise we make a new item and plug in the new data. */
     pEntry = (struct AclEntry *) malloc(sizeof (struct AclEntry));
     ASSERT(pEntry);
-       
+
     strcpy(pEntry->name, uEntryName);
     pEntry->rights = nEntryRights;
-    
+
     if (bNormalRights) {
         pEntry->next = pAcl->pluslist;
         pAcl->pluslist = pEntry;
@@ -960,24 +725,23 @@ void ChangeList(struct Acl *pAcl, BYTE bNormalRights, const CString & entryName,
     }
 }
 
-enum rtype {add, destroy, deny};
-
-static LONG Convert(const CString& strRights, int dfs, enum rtype *rtypep)
+static LONG
+Convert(const CString& strRights, int dfs, enum rtype *rtypep)
 {
     int i, len;
     LONG mode;
 
     *rtypep = add;     /* add rights, by default */
 
-    if (strRights == _T("read")) 
+    if (strRights == _T("read"))
         return PRSFS_READ | PRSFS_LOOKUP;
-    if (strRights == _T("write")) 
+    if (strRights == _T("write"))
         return PRSFS_READ | PRSFS_LOOKUP | PRSFS_INSERT | PRSFS_DELETE | PRSFS_WRITE | PRSFS_LOCK;
-    if (strRights == _T("mail")) 
+    if (strRights == _T("mail"))
         return PRSFS_INSERT | PRSFS_LOCK | PRSFS_LOOKUP;
-    if (strRights == _T("all")) 
+    if (strRights == _T("all"))
         return PRSFS_READ | PRSFS_LOOKUP | PRSFS_INSERT | PRSFS_DELETE | PRSFS_WRITE | PRSFS_LOCK | PRSFS_ADMINISTER;
-    
+
     if (strRights == _T("none")) {
         *rtypep = destroy; /* Remove entire entry */
         return 0;
@@ -999,11 +763,12 @@ static LONG Convert(const CString& strRights, int dfs, enum rtype *rtypep)
             fprintf(stderr, "illegal rights character '%c'.\n", c);
             exit(1);
         }
-    }   
+    }
     return mode;
 }
 
-BOOL SaveACL(const CString& strCellName, const CString& strDir, const CStringArray& normal, const CStringArray& negative)
+BOOL
+SaveACL(const CString& strCellName, const CString& strDir, const CStringArray& normal, const CStringArray& negative)
 {
     LONG code;
     struct ViceIoctl blob;
@@ -1040,44 +805,45 @@ BOOL SaveACL(const CString& strCellName, const CString& strDir, const CStringArr
             ShowMessageBox(IDS_SAVE_ACL_EINVAL_ERROR, MB_ICONERROR, IDS_SAVE_ACL_EINVAL_ERROR, strDir);
         else
             ShowMessageBox(IDS_SAVE_ACL_ERROR, MB_ICONERROR, IDS_SAVE_ACL_ERROR, strDir, GetAfsError(errno, strDir));
-    }       
+    }
 
     ZapAcl(pAcl);
 
     return (code == 0);
 }
 
-BOOL CopyACL(const CString& strToDir, const CStringArray& normal, const CStringArray& negative, BOOL bClear)
+BOOL
+CopyACL(const CString& strToDir, const CStringArray& normal, const CStringArray& negative, BOOL bClear)
 {
     LONG code;
     struct ViceIoctl blob;
     struct Acl *pToAcl;
     int idf = 0; // getidf(as, parm_copyacl_id);
-
+    char space[AFS_PIOCTL_MAXSIZE];
     HOURGLASS hourglass;
 
     // Get ACL to copy to
     blob.out_size = AFS_PIOCTL_MAXSIZE;
     blob.in_size = idf;
     blob.in = blob.out = space;
-       
+
     code = pioctl_T(strToDir, VIOCGETAL, &blob, 1);
     if (code) {
         ShowMessageBox(IDS_ACL_READ_ERROR, MB_ICONERROR, IDS_ACL_READ_ERROR, strToDir, GetAfsError(errno, strToDir));
         return FALSE;
     }
-       
-    if (bClear) 
+
+    if (bClear)
         pToAcl = EmptyAcl(space);
-    else 
-        pToAcl = ParseAcl(space);
+    else
+        pToAcl = ParseAcl(space, AFS_PIOCTL_MAXSIZE);
 
     if (pToAcl == NULL) {
         ShowMessageBox(IDS_INVALID_ACL_DATA, MB_ICONERROR, IDS_INVALID_ACL_DATA);
         return FALSE;
     }
 
-    CleanAcl(pToAcl);
+    CleanAcl(pToAcl, NULL);
 
     if (pToAcl->dfs) {
         ShowMessageBox(IDS_NO_DFS_COPY_ACL, MB_ICONERROR, IDS_NO_DFS_COPY_ACL, strToDir);
@@ -1110,7 +876,7 @@ BOOL CopyACL(const CString& strToDir, const CStringArray& normal, const CStringA
         ZapAcl(pToAcl);
         if (errno == EINVAL)
             ShowMessageBox(IDS_COPY_ACL_EINVAL_ERROR, MB_ICONERROR, IDS_COPY_ACL_EINVAL_ERROR, strToDir);
-        else 
+        else
             ShowMessageBox(IDS_COPY_ACL_ERROR, MB_ICONERROR, IDS_COPY_ACL_ERROR, strToDir, GetAfsError(errno, strToDir));
         return FALSE;
     }
@@ -1122,7 +888,8 @@ BOOL CopyACL(const CString& strToDir, const CStringArray& normal, const CStringA
     return TRUE;
 }
 
-CString ParseMountPoint(const CString strFile, CString strMountPoint)
+CString
+ParseMountPoint(const CString strFile, CString strMountPoint)
 {
     CString strType;
     CString strVolume;
@@ -1138,24 +905,57 @@ CString ParseMountPoint(const CString strFile, CString strMountPoint)
     if (nColon >= 0) {
         strCell = strMountPoint.Mid(1, nColon - 1);
         strVolume = strMountPoint.Mid(nColon + 1);
-    } else
+    } else {
         strVolume = strMountPoint.Mid(1);
+        strCell = _T("(local)");
+    }
 
-    strMountPointInfo = strFile + _T("\t") + strVolume + _T("\t") + strCell + _T("\t") + strType;
+    strMountPointInfo = _T("=> ") + strVolume + _T(" : ") + strCell + _T(" cell [") + strType + _T("]");
 
     return strMountPointInfo;
-}       
+}
 
-CString ParseSymlink(const CString strFile, CString strSymlink)
+CString
+ParseSymlink(const CString strFile, CString strSymlink)
 {
     CString strSymlinkInfo;
 
-    strSymlinkInfo = strFile + _T("\t") + strSymlink;
+    strSymlinkInfo = _T("-> ") + strSymlink;
 
     return strSymlinkInfo;
-}       
+}
 
-BOOL IsPathInAfs(const CString & strPath)
+BOOL
+GetFID(const CString& path, CString& fidstring, BOOL bLiteral)
+{
+    struct ViceIoctl blob;
+    cm_ioctlQueryOptions_t options;
+    cm_fid_t fid;
+    int code;
+
+    memset(&options, 0, sizeof(options));
+    options.size = sizeof(options);
+    options.field_flags |= CM_IOCTL_QOPTS_FIELD_LITERAL;
+    options.literal = bLiteral ? 1 : 0;
+    blob.in_size = options.size;    /* no variable length data */
+    blob.in = &options;
+    blob.out_size = sizeof(cm_fid_t);
+    blob.out = (char *) &fid;
+
+    code = pioctl_T(path, VIOCGETFID, &blob, 1);
+    fidstring.Empty();
+    if (code == 0) {
+        fidstring.Format( TEXT("%lu.%lu.%lu"),
+                          fid.volume,
+                          fid.vnode,
+                          fid.unique);
+        return TRUE;
+    }
+    return FALSE;
+}
+
+BOOL
+IsPathInAfs(const CString & strPath)
 {
     struct ViceIoctl blob;
     cm_ioctlQueryOptions_t options;
@@ -1181,11 +981,12 @@ BOOL IsPathInAfs(const CString & strPath)
     return TRUE;
 }
 
-static int 
+static int
 IsFreelanceRoot(const CString& apath)
 {
     struct ViceIoctl blob;
     afs_int32 code;
+    char space[AFS_PIOCTL_MAXSIZE];
 
     blob.in_size = 0;
     blob.out_size = AFS_PIOCTL_MAXSIZE;
@@ -1197,7 +998,8 @@ IsFreelanceRoot(const CString& apath)
     return 1;   /* assume it is because it is more restrictive that way */
 }
 
-static const char * NetbiosName(void)
+static const char *
+NetbiosName(void)
 {
     static char buffer[1024] = "AFS";
     HKEY  parmKey;
@@ -1218,17 +1020,14 @@ static const char * NetbiosName(void)
     return buffer;
 }
 
-static void FixNetbiosPath(CString& path)
+static void
+FixNetbiosPath(CString& path)
 {
     if (!IsPathInAfs(path)) {
         CString nbroot;
         const char * nbname = NetbiosName();
 
-#ifdef UNICODE
-        nbroot.Format(_T("\\\\%S\\"), nbname);
-#else
         nbroot.Format(_T("\\\\%s\\"), nbname);
-#endif
 
         if (nbroot.CompareNoCase(path) == 0) {
             path.Append(_T("all\\"));
@@ -1238,7 +1037,8 @@ static void FixNetbiosPath(CString& path)
 
 #define AFSCLIENT_ADMIN_GROUPNAME "AFS Client Admins"
 
-static BOOL IsAdmin (void)
+static BOOL
+IsAdmin (void)
 {
     static BOOL fAdmin = FALSE;
     static BOOL fTested = FALSE;
@@ -1287,7 +1087,7 @@ static BOOL IsAdmin (void)
         pszRefDomain = (TCHAR *)malloc(dwSize2);
 
         if (!LookupAccountName(NULL, pszAdminGroup, psidAdmin, &dwSize, pszRefDomain, &dwSize2, &snu)) {
-            /* We can't lookup the group now even though we looked it up earlier.  
+            /* We can't lookup the group now even though we looked it up earlier.
                Could this happen? */
             fAdmin = TRUE;
         } else {
@@ -1371,7 +1171,8 @@ static BOOL IsAdmin (void)
     return fAdmin;
 }
 
-CString Parent(const CString& path)
+CString
+Parent(const CString& path)
 {
     int last_slash = path.ReverseFind(_T('\\'));
 
@@ -1389,8 +1190,9 @@ CString Parent(const CString& path)
         }
     }
     }
-    
-CString LastComponent(const CString& path)
+
+CString
+LastComponent(const CString& path)
 {
     int last_slash = path.ReverseFind(_T('\\'));
 
@@ -1409,7 +1211,10 @@ CString LastComponent(const CString& path)
 }
 
 static CString
-GetCell(const CString & path)
+GetCell(const CString & path, BOOL bFollow = TRUE);
+
+static CString
+GetCell(const CString & path, BOOL bFollow)
 {
     char cellname[MAXCELLCHARS];
     afs_int32 code;
@@ -1431,7 +1236,8 @@ GetCell(const CString & path)
 }
 
 
-BOOL ListMount(CStringArray& files)
+BOOL
+ListMount(CStringArray& files)
 {
     LONG code;
     struct ViceIoctl blob;
@@ -1441,13 +1247,14 @@ BOOL ListMount(CStringArray& files)
     CStringUtf8 last_component;                /* Last component of true name */
 
     CStringArray mountPoints;
-    
+
     HOURGLASS hourglass;
 
     error = 0;
 
     for (int i = 0; i < files.GetSize(); i++) {
         int last_slash = files[i].ReverseFind(_T('\\'));
+        char space[AFS_PIOCTL_MAXSIZE];
 
         if (last_slash != -1) {
             last_component.SetString( files[i].Mid(last_slash + 1) );
@@ -1551,49 +1358,18 @@ MakeMount(const CString& strDir,
         ShowMessageBox(IDS_MOUNT_POINT_ERROR, MB_ICONERROR, IDS_MOUNT_POINT_ERROR, GetAfsError(errno, strDir));
         return FALSE;
     }
-    
+
     return TRUE;
 }
 
-/*
-*/
-long fs_ExtractDriveLetter(const char *inPathp, char *outPathp)
-{
-    if (inPathp[0] != 0 && inPathp[1] == ':') {
-        /* there is a drive letter */
-        *outPathp++ = *inPathp++;
-        *outPathp++ = *inPathp++;
-        *outPathp++ = 0;
-    }
-    else *outPathp = 0;
-
-    return 0;
-}       
-
-/* strip the drive letter from a component */
-long fs_StripDriveLetter(const char *inPathp, char *outPathp, long outSize)
-{
-    char tempBuffer[1000];
-    strcpy(tempBuffer, inPathp);
-    if (tempBuffer[0] != 0 && tempBuffer[1] == ':') {
-        /* drive letter present */
-        strcpy(outPathp, tempBuffer+2);
-    }
-    else {
-        /* no drive letter present */
-        strcpy(outPathp, tempBuffer);
-    }
-    return 0;
-}       
-
-
-BOOL RemoveSymlink(const CString& strName)
+BOOL
+RemoveSymlink(const CString& strName)
 {
     BOOL error = FALSE;
     INT code=0;
     struct ViceIoctl blob;
     char lsbuffer[1024];
-    
+
     HOURGLASS hourglass;
 
     CString strParent = Parent(strName);
@@ -1622,13 +1398,14 @@ BOOL RemoveSymlink(const CString& strName)
     ustrLast.ReleaseBuffer();
 
     return (code == 0);
-}       
+}
 
-BOOL IsSymlink(const CString& strName)
+BOOL
+IsSymlink(const CString& strName)
 {
     struct ViceIoctl blob;
     int code;
-
+    char space[AFS_PIOCTL_MAXSIZE];
     HOURGLASS hourglass;
 
     CStringUtf8 ustrLast(LastComponent(strName));
@@ -1647,10 +1424,11 @@ BOOL IsSymlink(const CString& strName)
     ustrLast.ReleaseBuffer();
 
     return (code==0);
-}       
+}
 
 
-BOOL IsMountPoint(const CString& path)
+BOOL
+IsMountPoint(const CString& path)
 {
     LONG code = 0;
     struct ViceIoctl blob;
@@ -1673,7 +1451,7 @@ BOOL IsMountPoint(const CString& path)
     mountpoint.ReleaseBuffer();
 
     return (code==0);
-}       
+}
 
 
 /*
@@ -1682,7 +1460,8 @@ BOOL IsMountPoint(const CString& path)
  *         (or ``.'' if none is provided)
  *      tp: Set to point to the actual name of the mount point to nuke.
  */
-BOOL RemoveMount(CStringArray& files)
+BOOL
+RemoveMount(CStringArray& files)
 {
     LONG code = 0;
     struct ViceIoctl blob;
@@ -1726,7 +1505,7 @@ BOOL RemoveMount(CStringArray& files)
             results.Add(GetMessageString(IDS_ERROR, GetAfsError(errno, StripPath(files[i]))));
         } else
             results.Add(GetMessageString(IDS_DELETED));
-    }   
+    }
 
     LoadString (str, IDS_REMOVE_MP);
     LoadString (str2, IDS_REMOVE_MP_COLUMN);
@@ -1737,17 +1516,19 @@ BOOL RemoveMount(CStringArray& files)
     return !error;
 }
 
-BOOL GetVolumeInfo(CString strFile, CVolInfo& volInfo)
+BOOL
+GetVolumeInfo(CString strFile, CVolInfo& volInfo, BOOL bFollow)
 {
     LONG code;
     struct ViceIoctl blob;
     struct VolumeStatus *status;
     char *name;
-
+    char space[AFS_PIOCTL_MAXSIZE];
     HOURGLASS hourglass;
+    CString strTarget = bFollow ? strFile : Parent(strFile);
 
-    volInfo.m_strFilePath = strFile;
-    volInfo.m_strFileName = StripPath(strFile);
+    volInfo.m_strFilePath = strTarget;
+    volInfo.m_strFileName = StripPath(strTarget);
 
     /*
        volInfo.m_strName = "VolumeName";
@@ -1765,9 +1546,9 @@ BOOL GetVolumeInfo(CString strFile, CVolInfo& volInfo)
     blob.in_size = 0;
     blob.out = space;
 
-    code = pioctl_T(strFile, VIOCGETVOLSTAT, &blob, 1);
+    code = pioctl_T(strTarget, VIOCGETVOLSTAT, &blob, 1);
     if (code || blob.out_size < sizeof(*status)) {
-        volInfo.m_strErrorMsg = GetAfsError(errno, strFile);
+        volInfo.m_strErrorMsg = GetAfsError(errno, strTarget);
         return FALSE;
     }
 
@@ -1783,16 +1564,36 @@ BOOL GetVolumeInfo(CString strFile, CVolInfo& volInfo)
     volInfo.m_nPartFree = status->PartBlocksAvail;
     volInfo.m_nDup = -1;
 
+    errno = 0;
+    code = pioctl_T(strTarget, VIOC_PATH_AVAILABILITY, &blob, 1);
+    switch (errno) {
+    case 0:
+        volInfo.m_strAvail =_T("Online");
+        break;
+    case ENXIO:
+        volInfo.m_strAvail = _T("Offline");
+        break;
+    case ENOSYS:
+        volInfo.m_strAvail = _T("Unreachable");
+        break;
+    case EBUSY:
+        volInfo.m_strAvail = _T("Busy");
+        break;
+    default:
+        volInfo.m_strAvail = _T("Unknown");
+    }
+
     return TRUE;
 }
-       
-BOOL SetVolInfo(CVolInfo& volInfo)
+
+BOOL
+SetVolInfo(CVolInfo& volInfo)
 {
     LONG code;
     struct ViceIoctl blob;
     struct VolumeStatus *status;
     char *input;
-
+    char space[AFS_PIOCTL_MAXSIZE];
     HOURGLASS hourglass;
 
     blob.out_size = AFS_PIOCTL_MAXSIZE;
@@ -1803,7 +1604,7 @@ BOOL SetVolInfo(CVolInfo& volInfo)
     status = (VolumeStatus *)space;
     status->MinQuota = -1;
     status->MaxQuota = volInfo.m_nNewQuota;
-       
+
     input = (char *)status + sizeof(*status);
     *(input++) = '\0'; /* never set name: this call doesn't change vldb */
     *(input++) = '\0'; // No offmsg
@@ -1820,7 +1621,7 @@ BOOL SetVolInfo(CVolInfo& volInfo)
         fprintf(fp, "\t\t\tOther status fields aren't set\n");
         fprintf(fp, "\t\t\t3 nulls follow the VolumeStatus structure.\n");
         fprintf(fp, "\tfollow = 1\n");
-        fclose(fp);            
+        fclose(fp);
     }
 #endif
 
@@ -1833,14 +1634,16 @@ BOOL SetVolInfo(CVolInfo& volInfo)
     return TRUE;
 }
 
-void GetCellName(const CString& cellNamep, struct afsconf_cell *infop)
+void
+GetCellName(const CString& cellNamep, struct afsconf_cell *infop)
 {
     CStringUtf8 uCellName(cellNamep);
 
     StringCbCopyA(infop->name, sizeof(infop->name), uCellName);
 }
 
-BOOL CheckServers(const CString& strCellName, WHICH_CELLS nCellsToCheck, BOOL bFast)
+BOOL NO_CALLER
+CheckServers(const CString& strCellName, WHICH_CELLS nCellsToCheck, BOOL bFast)
 {
     LONG code;
     struct ViceIoctl blob;
@@ -1848,7 +1651,7 @@ BOOL CheckServers(const CString& strCellName, WHICH_CELLS nCellsToCheck, BOOL bF
     LONG temp = 0;
     struct afsconf_cell info;
     struct chservinfo checkserv;
-
+    char space[AFS_PIOCTL_MAXSIZE];
     HOURGLASS hourglass;
 
     memset(&checkserv, 0, sizeof(struct chservinfo));
@@ -1872,7 +1675,7 @@ BOOL CheckServers(const CString& strCellName, WHICH_CELLS nCellsToCheck, BOOL bF
     }
     if (bFast)
         temp |= 1;     /* set fast flag */
-    
+
     checkserv.magic = 0x12345678;      /* XXX */
     checkserv.tflags = temp;
     checkserv.tinterval = -1;  /* don't change current interval */
@@ -1905,9 +1708,10 @@ BOOL CheckServers(const CString& strCellName, WHICH_CELLS nCellsToCheck, BOOL bF
     dlg.DoModal();
 
     return TRUE;
-}       
+}
 
-BOOL GetTokenInfo(CStringArray& tokenInfo)
+BOOL
+GetTokenInfo(CStringArray& tokenInfo)
 {
     int cellNum;
     int rc;
@@ -1955,7 +1759,7 @@ BOOL GetTokenInfo(CStringArray& tokenInfo)
         else {
             rc = ktc_GetToken(&serviceName, &token, sizeof(token), &clientName);
             if (rc) {
-                ShowMessageBox(IDS_GET_TOKENS_UNEXPECTED_ERROR2, MB_ICONERROR, IDS_GET_TOKENS_UNEXPECTED_ERROR2, 
+                ShowMessageBox(IDS_GET_TOKENS_UNEXPECTED_ERROR2, MB_ICONERROR, IDS_GET_TOKENS_UNEXPECTED_ERROR2,
                                 serviceName.name, serviceName.instance,        serviceName.cell, rc);
                 continue;
             }
@@ -1981,10 +1785,10 @@ BOOL GetTokenInfo(CStringArray& tokenInfo)
             else
                 strUserName = userName;
 //             printf("User %s's tokens", userName);
-                       
+
 //             printf(" for %s%s%s@%s ", serviceName.name, serviceName.instance[0] ? "." : "", serviceName.instance, serviceName.cell);
             strCellName = serviceName.cell;
-                       
+
             if (tokenExpireTime <= current_time)
                 strExpir = "[>> Expired <<]";
 //             printf("[>> Expired <<]\n");
@@ -2010,7 +1814,8 @@ BOOL GetTokenInfo(CStringArray& tokenInfo)
     return TRUE;
 }
 
-UINT MakeSymbolicLink(const CString& strName, const CString& strTarget)
+UINT
+MakeSymbolicLink(const CString& strName, const CString& strTarget)
 {
     struct ViceIoctl blob;
     UINT code;
@@ -2040,7 +1845,8 @@ UINT MakeSymbolicLink(const CString& strName, const CString& strTarget)
     return 0;
 }
 
-void ListSymbolicLinkPath(const char *strName,char *strPath,UINT nlenPath)
+void NO_CALLER
+ListSymbolicLinkPath(const char *strName,char *strPath,UINT nlenPath)
 {
     ASSERT(nlenPath<MAX_PATH);
     struct ViceIoctl blob;
@@ -2048,8 +1854,8 @@ void ListSymbolicLinkPath(const char *strName,char *strPath,UINT nlenPath)
     char true_name[MAX_PATH+1];                /*``True'' dirname (e.g., symlink target)*/
     char parent_dir[MAX_PATH+1];               /*Parent directory of true name*/
     char *last_component;      /*Last component of true name*/
-    UINT code;    
-
+    UINT code;
+    char space[AFS_PIOCTL_MAXSIZE];
     HOURGLASS hourglass;
 
     strcpy(orig_name, strName);
@@ -2101,15 +1907,16 @@ void ListSymbolicLinkPath(const char *strName,char *strPath,UINT nlenPath)
         strcpy(space,"???");
     ASSERT(strlen(space)<MAX_PATH);
     strncpy(strPath,space,nlenPath);
-}       
+}
 
-BOOL ListSymlink(CStringArray& files)
+BOOL NO_CALLER
+ListSymlink(CStringArray& files)
 {
     LONG code;
     struct ViceIoctl blob;
     int error;
     CStringArray symlinks;
-    
+    char space[AFS_PIOCTL_MAXSIZE];
     HOURGLASS hourglass;
 
     error = 0;
@@ -2162,3 +1969,345 @@ BOOL ListSymlink(CStringArray& files)
     return !error;
 }
 
+CString
+GetCellName( const CString& strPath, BOOL bFollow )
+{
+    return GetCell(bFollow ? strPath : Parent(strPath));
+}
+
+CString
+GetServer( const CString& strPath, BOOL bFollow )
+{
+    LONG code;
+    struct ViceIoctl blob;
+    CString server;
+    char space[AFS_PIOCTL_MAXSIZE];
+
+    blob.out_size = AFS_PIOCTL_MAXSIZE;
+    blob.in_size = 0;
+    blob.out = space;
+    memset(space, 0, sizeof(space));
+
+    code = pioctl_T(bFollow ? strPath : Parent(strPath), VIOCWHEREIS, &blob, 1);
+    if (code) {
+        server=GetAfsError(errno);
+    } else {
+        LONG *hosts = (LONG *)space;
+
+        for (int j = 0; j < AFS_MAXHOSTS; j++) {
+            if (hosts[j] == 0)
+                break;
+            char *hostName = hostutil_GetNameByINet(hosts[j]);
+            server=hostName;
+        }
+    }
+
+    return server;
+}
+
+void
+GetServers( const CString& strPath, CStringArray& servers, BOOL bFollow )
+{
+    LONG code;
+    struct ViceIoctl blob;
+    char space[AFS_PIOCTL_MAXSIZE];
+
+    blob.out_size = AFS_PIOCTL_MAXSIZE;
+    blob.in_size = 0;
+    blob.out = space;
+    memset(space, 0, sizeof(space));
+
+    code = pioctl_T(bFollow ? strPath : Parent(strPath), VIOCWHEREIS, &blob, 1);
+    if (code) {
+        servers.Add(GetAfsError(errno));
+    } else {
+        LONG *hosts = (LONG *)space;
+
+        for (int j = 0; j < AFS_MAXHOSTS; j++) {
+            if (hosts[j] == 0)
+                break;
+            char *hostName = hostutil_GetNameByINet(hosts[j]);
+            servers.Add(hostName);
+        }
+    }
+}
+
+CString
+GetOwner( const CString& strPath, BOOL bFollow )
+{
+    LONG code;
+    struct ViceIoctl blob;
+    afs_int32 owner[2];
+    CString ret = TEXT("(unknown)");
+
+    blob.in_size = 0;
+    blob.out_size = 2 * sizeof(afs_int32);
+    blob.out = (char *) &owner;
+
+    code = pioctl_T(bFollow ? strPath : Parent(strPath), VIOCGETOWNER, &blob, 1);
+    if (code == 0 && blob.out_size == 2 * sizeof(afs_int32)) {
+        char oname[PR_MAXNAMELEN] = "(unknown)";
+        char confDir[257];
+        CStringUtf8 cell;
+
+        cell = GetCell(strPath, bFollow);
+
+        /* Go to the PRDB and see if this all number username is valid */
+        cm_GetConfigDir(confDir, sizeof(confDir));
+
+        pr_Initialize(1, confDir, PCCHAR(cell));
+        pr_SIdToName(owner[0], oname);
+
+        ret.Empty();
+        ret.Format(TEXT("%s [%d]"), (LPCTSTR)CString(oname), owner[0]);
+    }
+
+    return ret;
+}
+
+CString
+GetGroup( const CString& strPath, BOOL bFollow )
+{
+    LONG code;
+    struct ViceIoctl blob;
+    afs_int32 owner[2];
+    CString ret = TEXT("(unknown)");
+
+    blob.in_size = 0;
+    blob.out_size = 2 * sizeof(afs_int32);
+    blob.out = (char *) &owner;
+
+    code = pioctl_T(bFollow ? strPath : Parent(strPath), VIOCGETOWNER, &blob, 1);
+    if (code == 0 && blob.out_size == 2 * sizeof(afs_int32)) {
+        char gname[PR_MAXNAMELEN] = "(unknown)";
+        char confDir[257];
+        CStringUtf8 cell;
+
+        cell = GetCell(strPath, bFollow);
+
+        /* Go to the PRDB and see if this all number username is valid */
+        cm_GetConfigDir(confDir, sizeof(confDir));
+
+        pr_Initialize(1, confDir, PCCHAR(cell));
+        pr_SIdToName(owner[1], gname);
+
+        ret.Empty();
+        ret.Format(TEXT("%s [%d]"), (LPCTSTR)CString(gname), owner[1]);
+    }
+
+    return ret;
+}
+
+BOOL
+GetUnixModeBits( const CString& strPath, CString& user, CString& group, CString& other, CString& suid )
+{
+    // the strings must match the unix ls command output: "rwx" means read/write/execute permissions
+
+    LONG code;
+    struct ViceIoctl blob;
+    afs_uint32 unixModeBits;
+    CString ret = TEXT("(unknown)");
+
+    user.Empty();
+    group.Empty();
+    other.Empty();
+    suid.Empty();
+
+    blob.in_size = 0;
+    blob.out_size = sizeof(afs_int32);
+    blob.out = (char *) &unixModeBits;
+
+    code = pioctl_T(strPath, VIOC_GETUNIXMODE, &blob, 1);
+    if (code == 0 && blob.out_size == sizeof(afs_uint32)) {
+        if (unixModeBits & S_IRUSR)
+            user += _T("r");
+        if (unixModeBits & S_IWUSR)
+            user += _T("w");
+        if (unixModeBits & S_IXUSR)
+            user += _T("x");
+
+        if (unixModeBits & S_IRGRP)
+            group += _T("r");
+        if (unixModeBits & S_IWGRP)
+            group += _T("w");
+        if (unixModeBits & S_IXGRP)
+            group += _T("x");
+
+        if (unixModeBits & S_IROTH)
+            other += _T("r");
+        if (unixModeBits & S_IWOTH)
+            other += _T("w");
+        if (unixModeBits & S_IXOTH)
+            other += _T("x");
+
+        if (unixModeBits & S_ISUID)
+            suid += _T("s");
+        if (unixModeBits & S_ISGID)
+            suid += _T("g");
+        if (unixModeBits & S_ISVTX)
+            suid += _T("v");
+
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+void
+SetUnixModeBits( const CStringArray& files, const CString& user, const CString& group, const CString& other, const CString& suid )
+{
+    // set the unix file attributes for all paths in 'files'.
+    LONG code;
+    struct ViceIoctl blob;
+    struct {
+        cm_ioctlQueryOptions_t options;
+        afs_uint32 unixModeBits;
+    } inData;
+
+    memset(&inData, 0, sizeof(inData));
+    inData.options.size = sizeof(inData.options);
+    inData.options.field_flags = 0;
+    inData.options.literal = 0;            /* always applying to target */
+    blob.in_size = sizeof(inData);         /* no variable length data */
+    blob.in = &inData;
+    blob.out = NULL;
+    blob.out_size = 0;
+    inData.unixModeBits = 0;
+
+    if (user.Find(_T('r')) != -1)
+        inData.unixModeBits |= S_IRUSR;
+    if (user.Find(_T('w')) != -1)
+        inData.unixModeBits |= S_IWUSR;
+    if (user.Find(_T('x')) != -1)
+        inData.unixModeBits |= S_IXUSR;
+
+    if (group.Find(_T('r')) != -1)
+        inData.unixModeBits |= S_IRGRP;
+    if (group.Find(_T('w')) != -1)
+        inData.unixModeBits |= S_IWGRP;
+    if (group.Find(_T('x')) != -1)
+        inData.unixModeBits |= S_IXGRP;
+
+    if (other.Find(_T('r')) != -1)
+        inData.unixModeBits |= S_IROTH;
+    if (other.Find(_T('w')) != -1)
+        inData.unixModeBits |= S_IWOTH;
+    if (other.Find(_T('x')) != -1)
+        inData.unixModeBits |= S_IXOTH;
+
+    if (suid.Find(_T('s')) != -1)
+        inData.unixModeBits |= S_ISUID;
+    if (suid.Find(_T('g')) != -1)
+        inData.unixModeBits |= S_ISGID;
+    if (suid.Find(_T('v')) != -1)
+        inData.unixModeBits |= S_ISVTX;
+
+    for (int i = 0; i < files.GetSize(); i++)
+        code = pioctl_T(files[i], VIOC_SETUNIXMODE, &blob, 1);
+}
+
+CString GetMountpoint( const CString& strPath )
+{
+    LONG code;
+    struct ViceIoctl blob;
+    char space[AFS_PIOCTL_MAXSIZE];
+    CString parent_dir;                /* Parent directory of true name */
+    CStringUtf8 last_component;                /* Last component of true name */
+
+    CString mountPoint;
+
+
+    int last_slash = strPath.ReverseFind(_T('\\'));
+
+    if (last_slash != -1) {
+        last_component.SetString( strPath.Mid(last_slash + 1) );
+        parent_dir.SetString( strPath.Left(last_slash + 1) );
+        FixNetbiosPath(parent_dir);
+    } else {
+        // The path is of the form "C:foo" or just "foo".  If
+        // there is a drive, then use the current directory of
+        // that drive.  Otherwise we just use '.'.
+
+        if (strPath.GetLength() >= 2 && strPath[1] == _T(':')) {
+            parent_dir.Format(_T("%c:."), strPath[0]);
+            last_component.SetString( strPath.Mid(2) );
+        } else {
+            parent_dir.SetString( _T("."));
+            last_component.SetString( strPath );
+        }
+    }
+
+    blob.in_size = last_component.GetLength() + 1;
+    blob.in = last_component.GetBuffer();
+    blob.out_size = AFS_PIOCTL_MAXSIZE;
+    blob.out = space;
+    memset(space, 0, AFS_PIOCTL_MAXSIZE);
+
+    code = pioctl_T(parent_dir, VIOC_AFS_STAT_MT_PT, &blob, 1);
+
+    last_component.ReleaseBuffer();
+
+    if (code == 0) {
+        int nPos;
+        space[AFS_PIOCTL_MAXSIZE - 1] = '\0';
+        nPos = strlen(space) - 1;
+        if (space[nPos] == '.')
+            space[nPos] = 0;
+        mountPoint = ParseMountPoint(StripPath(strPath), Utf8ToCString(space));
+    } else {
+        if (errno == EINVAL)
+            mountPoint = GetMessageString(IDS_NOT_MOUNT_POINT_ERROR, StripPath(strPath));
+        else
+            mountPoint = GetMessageString(IDS_LIST_MOUNT_POINT_ERROR, GetAfsError(errno, StripPath(strPath)));
+    }
+
+    return mountPoint;
+}
+
+CString GetSymlink( const CString& strPath )
+{
+    LONG code;
+    struct ViceIoctl blob;
+    CString symlink;
+    char space[AFS_PIOCTL_MAXSIZE];
+
+    CString strParent = Parent(strPath);
+    CStringUtf8 ustrLast(LastComponent(strPath));
+
+    FixNetbiosPath(strParent);
+
+    blob.in_size = ustrLast.GetLength() + 1;
+    blob.in = ustrLast.GetBuffer();
+    blob.out_size = AFS_PIOCTL_MAXSIZE;
+    blob.out = space;
+    memset(space, 0, AFS_PIOCTL_MAXSIZE);
+
+    code = pioctl_T(strParent, VIOC_LISTSYMLINK, &blob, 1);
+
+    ustrLast.ReleaseBuffer();
+
+    if (code == 0) {
+        CString syml;
+        int len;
+
+        space[AFS_PIOCTL_MAXSIZE - 1] = '\0';
+        syml = Utf8ToCString(space);
+        len = syml.GetLength();
+
+        if (len > 0) {
+            if (syml[len - 1] == _T('.'))
+                syml.Truncate(len - 1);
+        }
+
+        symlink = ParseSymlink(StripPath(strPath), syml);
+
+    } else {
+        if (errno == EINVAL)
+            symlink = GetMessageString(IDS_NOT_SYMLINK_ERROR, StripPath(strPath));
+        else
+            symlink = GetMessageString(IDS_LIST_MOUNT_POINT_ERROR, GetAfsError(errno, StripPath(strPath)));
+    }
+
+
+    return symlink;
+}