This document is current as of release 1.6.0b. It has not been updated to reflect the changes due to the use of the AFSRedir.sys file system redirector driver in 1.7.0100 and later. How to determine if OpenAFS is installed? When the OpenAFS Client Service is installed there will be several registry keys created: HKLM\SYSTEM\CurrentControlSet\Services\TransarcAFSDaemon "ImagePath" = "path to afsd_service.exe" HKLM\SOFTWARE\TransarcCorporation\AFS Client\CurrentVersion "PathName" = "the path to the client installation directory" "MajorVersion" "MinorVersion" "VersionString" BOOL IsAFSServerInstalled (void) { BOOL fInstalled = FALSE; TCHAR szKey[] = TEXT("HKLM\SYSTEM\CurrentControlSet\Services\TransarcAFSDaemon"); LPCTSTR pch = lstrchr (szKey, TEXT('\\')); HKEY hk; if (RegOpenKey (HKEY_LOCAL_MACHINE, &pch[1], &hk) == 0) { fInstalled = TRUE; RegCloseKey (hk); } return fInstalled; } How to determine if OpenAFS is active? The AFS Client Service is normally started automatically at system boot. The state of the service may be queried by asking the Windows Service Manager. BOOL IsAFSServiceRunning (void) { SERVICE_STATUS Status; memset (&Status, 0x00, sizeof(Status)); Status.dwCurrentState = SERVICE_STOPPED; SC_HANDLE hManager; if ((hManager = OpenSCManager (NULL, NULL, GENERIC_READ)) != NULL) { SC_HANDLE hService; if ((hService = OpenService (hManager, TEXT("TransarcAFSDaemon"), GENERIC_READ)) != NULL) { QueryServiceStatus (hService, &Status); CloseServiceHandle (hService); } CloseServiceHandle (hManager); } return (Status.dwCurrentState == SERVICE_RUNNING); } How to determine the AFS UNC Service Name? The local UNC service name registered by the OpenAFS Client Service SMB/CIFS Server depends on whether or not a Microsoft Loopback Adapter has been installed and the contents of a registry value. The loopback adapter is important because if the service cannot bind itself to a loopback adapter then the registered SMB/CIFS service name must be unique to the WINS name space. When the loopback adapter is installed, a globally common name such as "AFS" can be used. If the loopback adapter is installed the UNC server name will be the value at: HKLM\SYSTEM\CurrentControlSet\Services\TransarcAFSDaemon\Parameters REG_SZ/REG_EXPAND_SZ "NetbiosName" If this value is not present, the default is "AFS". When the loopback adapter is not installed the UNC name will be: %COMPUTERNAME%-%NetbiosName% if the Computer Name is "MYHOST" and the Netbios Name is "AFS" then the UNC server name will be: MYHOST-AFS At the moment there is no readily available code exported by a library to determine if the loopback adapter is installed or not. What I will do if someone requests it is add a new AFS pioctl operation which will return the in use UNC Server Name. How to determine the AFS unix mount point path? On Unix systems the local mount point of the AFS file system is usually "/afs". Some organizations have their own custom local mount point locations. To determine what the locally configured unix mount point is for interpretting Unix style paths there is a registry value: HKLM\SYSTEM\CurrentControlSet\Services\TransarcAFSDaemon\Parameters REG_SZ "MountRoot" If this value does not exist the default value is "/afs". What are AFS pioctl() operations and how do I call them? AFS pioctl() operations are IPCs which can be used to communicate with the AFS Client Service for the purposes of querying or changing the state of the service. The pioctl() function has a prototype of: struct ViceIoctl { long in_size; long out_size; void *in; void *out; }; long pioctl(char *pathp, long opcode, struct ViceIoctl *blobp, int follow); and can be loaded from the library "afsauthent.dll" at runtime. The default calling convention is used. How to test to see if a PATH is within AFS? Given an arbitrary file path, you can test to see if the path is in the AFS file system with the following function. It asks the AFS Client Service to return the name of the cell in which the path exists. If the cell name cannot be found, the path is not in the AFS file space. BOOL IsPathInAFS(const CHAR *strPath) { struct ViceIoctl blob; char cellname[256]; int code; blob.in_size = 0; blob.out_size = sizeof(cellname); blob.out = cellname; code = pioctl((LPTSTR)((LPCTSTR)strPath), VIOC_FILE_CELL_NAME, &blob, 1); if (code) return FALSE; return TRUE; } What are AFS cells, volumes and mount points? The AFS file system consists of a series of administrative domains called "cells" each of which contain two or more volumes. A volume is a file system unit which contains files, directories, mount points, symlinks and hard links. Each cell has a minimum of two volumes. When an AFS client connects to a cell it mounts the cell's "root.afs" volume at the local afs mount point path. Each "root.afs" volume contains one or more mount points which allow the AFS client to other volumes in both in the current cell as well as other cells. There are two types of mount points: read-only and read-write. By following a read-only mount point the client can obtain data from any of the equivalent read-only volume replicas. By following a read-write mount point the client is restricted to the one and only read-write copy of the volume. Periodically replicated volumes have their read-write copy "released" which results in a synchronization with the read-only copies. By convention the first volume of every cell to contain real data is called "root.cell". The name of the read-only mount point which joins the "root.afs" volume to the "root.cell" volume is the name of the cell. The name of the read-write mount point is the name of the cell prefaced by a dot. For example, the "athena.mit.edu" cell's "root.afs" volume will contain mount points such as "athena.mit.edu" -> "#athena.mit.edu:root.cell" ".athena.mit.edu" -> "%athena.mit.edu:root.cell" The '#' indicates a read-only mount point and the '%' indicates a read-write mount point. The mount points are not limited to the local cell so additional mount points might be included such as: "andrew.cmu.edu" -> "#andrew.cmu.edu:root.cell" "sipb.mit.edu" -> "#sipb.mit.edu:root.cell" The mount points appear as directory entries to the operating system. Volumes can also store files, hard links to files, and symlinks to files. On Windows, hardlinks can be created and destroyed using the CreateHardLink() and DeleteFile() Win32 APIs. Creating, Listing and Destroying symlinks and mount points is performed by the user via the OpenAFS provided command line tools: fs.exe and symlink.exe. symlink make symlink list symlink rm fs mkmount [] [-rw] fs lsmount + fs rmmount + These operations are performed via pioctl calls. BOOL WhichCell(const char *strPath, char *cell, int len) { struct ViceIoctl blob; int code; blob.in_size = 0; blob.out_size = len blob.out = cell; code = pioctl((LPTSTR)((LPCTSTR)strPath), VIOC_FILE_CELL_NAME, &blob, 1); if (code) return FALSE; return TRUE; } BOOL WorkstationCell(char *cell, int len) { struct ViceIoctl blob; int code; blob.in_size = 0; blob.out_size = len blob.out = cell; code = pioctl(NULL, VIOC_GET_WS_CELL, &blob, 1); if (code) return FALSE; return TRUE; } /* from afs/afsint.h */ struct VolumeStatus { afs_int32 Vid; afs_int32 ParentId; char Online; char InService; char Blessed; char NeedsSalvage; afs_int32 Type; afs_int32 MinQuota; afs_int32 MaxQuota; afs_int32 BlocksInUse; afs_int32 PartBlocksAvail; afs_int32 PartMaxBlocks; }; typedef struct VolumeStatus VolumeStatus; BOOL WhichVolume(const char *strPath, DWORD * volID, char *volname, int len) { struct ViceIoctl blob; char space[2048]; struct VolumeStatus *status; char *name, *offmsg, *motd; int code; blob.in_size = 0; blob.out_size = sizeof(space); blob.out = space; code = pioctl(strPath, VIOCGETVOLSTAT, &blob, 1); if (code) return FALSE; status = (VolumeStatus *)space; name = (char *)status + sizeof(*status); offmsg = name + strlen(name) + 1; motd = offmsg + strlen(offmsg) + 1; if (volID) *volID = status->Vid; if (volname) { strncpy(volname, name, len); volname[len-1] = '\0'; } /* Other items you could grab if you wanted * if (*offmsg) * then there is a message explaining why the volume is offline * * if (*motd) * then there is a message of the day. (very rarely used) * * status->MaxQuota: 0 is unlimited; otherwise 32-bit number of Blocks * status->BlocksInUse: 32-bit number of blocks * status->PartBlocksAvail: 32-bit number of blocks available in * the partition the volume is located on * status->PartMaxBlocks: 32-bit number representing the actual size * of the partition. * * These can be used to compute Quota Used, Partition Used, Space Avail, * etc. A block is 1K. * * status->Type 0=ReadOnly; 1=ReadWrite * status->Online (boolean) * status->InService (boolean) * status->Blessed (boolean) * status->NeedsSalvage (boolean) * status->ParentId Volume ID of the parent volume. (for readonly) */ return TRUE; } BOOL IsSymlink(const char * dir, const char * entry) { struct ViceIoctl blob; char space[2048]; int code; blob.in_size = strlen(entry); blob.in = entry; blob.out_size = sizeof(space); blob.out = space; memset(space, 0, sizeof(space)); code = pioctl(dir, VIOC_LISTSYMLINK, &blob, 1); if (code) return FALSE; return TRUE; } BOOL GetSymlink(const char * dir, const char * entry, char * dest, int len) { struct ViceIoctl blob; char space[2048]; int code; blob.in_size = strlen(entry); blob.in = entry; blob.out_size = sizeof(space); blob.out = space; memset(space, 0, sizeof(space)); code = pioctl(dir, VIOC_LISTSYMLINK, &blob, 1); if (code) return FALSE; strncpy(dest, space, len); dest[len-1] = '\0'; return TRUE; } BOOL IsMountPoint(const char * dir, const char * entry) { struct ViceIoctl blob; char space[2048]; int code; blob.in_size = strlen(entry); blob.in = entry; blob.out_size = sizeof(space); blob.out = space; memset(space, 0, sizeof(space)); code = pioctl(dir, VIOC_AFS_STAT_MT_PT, &blob, 1); if (code) return FALSE; return TRUE; } BOOL GetMountPoint(const char * dir, const char * entry, char * dest, int len) { struct ViceIoctl blob; char space[2048]; int code; blob.in_size = strlen(entry); blob.in = entry; blob.out_size = sizeof(space); blob.out = space; memset(space, 0, sizeof(space)); code = pioctl(dir, VIOC_AFS_STAT_MT_PT, &blob, 1); if (code) return FALSE; strncpy(dest, space, len); dest[len-1] = '\0'; return TRUE; } BOOL IsOnline(const char *strPath) { struct ViceIoctl blob; char space[2048]; struct VolumeStatus *status; int code; blob.in_size = 0; blob.out_size = sizeof(space); blob.out = space; code = pioctl(strPath, VIOCGETVOLSTAT, &blob, 1); if (code) return FALSE; status = (VolumeStatus *)space; if (!status->Online || !status->InService || !status->Blessed || status->NeedsSalvage) return FALSE; return TRUE; }