/* * 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 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include long fs_ExtractDriveLetter(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(char *inPathp, char *outPathp, size_t outSize) { char tempBuffer[1000]; if( FAILED(StringCbCopy(tempBuffer, sizeof(tempBuffer), inPathp))) { fprintf (stderr, "fs_StripDriveLetter - not enough space on input"); exit(1); } if (tempBuffer[0] != 0 && tempBuffer[1] == ':') { /* drive letter present */ if( FAILED(StringCbCopy(outPathp, outSize, tempBuffer+2))) { fprintf (stderr, "fs_StripDriveLetter - not enough space on output"); exit(1); } } else { /* no drive letter present */ if( FAILED(StringCbCopy(outPathp, outSize, tempBuffer))) { fprintf (stderr, "fs_StripDriveLetter - not enough space on output"); exit(1); } } return 0; } /* take a path with a drive letter, possibly relative, and return a full path * without the drive letter. This is the full path relative to the working * dir for that drive letter. The input and output paths can be the same. */ long fs_GetFullPath(char *pathp, char *outPathp, size_t outSize) { char tpath[1000]; char origPath[1000]; char *firstp; long code; int pathHasDrive; int doSwitch; char newPath[3]; if (pathp[0] != 0 && pathp[1] == ':') { /* there's a drive letter there */ firstp = pathp+2; pathHasDrive = 1; } else { firstp = pathp; pathHasDrive = 0; } if (*firstp == '\\' || *firstp == '/') { /* already an absolute pathname, just copy it back */ if( FAILED(StringCbCopy(outPathp, outSize, firstp))) { fprintf (stderr, "fs_GetFullPath - not enough space on output"); exit(1); } return 0; } GetCurrentDirectoryA(sizeof(origPath), origPath); doSwitch = 0; if (pathHasDrive && (*pathp & ~0x20) != (origPath[0] & ~0x20)) { /* a drive has been specified and it isn't our current drive. * to get path, switch to it first. Must case-fold drive letters * for user convenience. */ doSwitch = 1; newPath[0] = *pathp; newPath[1] = ':'; newPath[2] = 0; if (!SetCurrentDirectoryA(newPath)) { code = GetLastError(); return code; } } /* now get the absolute path to the current wdir in this drive */ GetCurrentDirectoryA(sizeof(tpath), tpath); if( FAILED(StringCbCopy(outPathp, outSize, tpath+2))) { fprintf (stderr, "fs_GetFullPath - not enough space on output"); exit(1); } /* if there is a non-null name after the drive, append it */ if (*firstp != 0) { if( FAILED(StringCbCat(outPathp, outSize, "\\"))) { fprintf (stderr, "fs_GetFullPath - not enough space on output"); exit(1); } if( FAILED(StringCbCat(outPathp, outSize, firstp))) { fprintf (stderr, "fs_GetFullPath - not enough space on output"); exit(1); } } /* finally, if necessary, switch back to our home drive letter */ if (doSwitch) { SetCurrentDirectoryA(origPath); } return 0; } /* is this a digit or a digit-like thing? */ static int ismeta(int abase, int ac) { /* if (ac == '-' || ac == 'x' || ac == 'X') return 1; */ if (ac >= '0' && ac <= '7') return 1; if (abase <= 8) return 0; if (ac >= '8' && ac <= '9') return 1; if (abase <= 10) return 0; if (ac >= 'a' && ac <= 'f') return 1; if (ac >= 'A' && ac <= 'F') return 1; return 0; } /* given that this is a digit or a digit-like thing, compute its value */ static int getmeta(int ac) { if (ac >= '0' && ac <= '9') return ac - '0'; if (ac >= 'a' && ac <= 'f') return ac - 'a' + 10; if (ac >= 'A' && ac <= 'F') return ac - 'A' + 10; return 0; } char *cm_mount_root="afs"; char *cm_slash_mount_root="/afs"; char *cm_back_slash_mount_root="\\afs"; void fs_utils_InitMountRoot() { HKEY parmKey; char mountRoot[MAX_PATH+1]; char *pmount=mountRoot; DWORD len=sizeof(mountRoot)-1; if ((RegOpenKeyExA(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY, 0, (IsWow64()?KEY_WOW64_64KEY:0)|KEY_QUERY_VALUE, &parmKey)!= ERROR_SUCCESS) || (RegQueryValueExA(parmKey, "Mountroot", NULL, NULL,(LPBYTE)(mountRoot), &len)!= ERROR_SUCCESS) || (len==sizeof(mountRoot)-1) ) { if( FAILED(StringCbCopy(mountRoot, sizeof(mountRoot), "\\afs"))) { fprintf (stderr, "fs_InitMountRoot - not enough space on output"); exit(1); } } RegCloseKey(parmKey); mountRoot[len]=0; /*safety see ms-help://MS.MSDNQTR.2002OCT.1033/sysinfo/base/regqueryvalueex.htm*/ cm_mount_root=malloc(len+1); cm_slash_mount_root=malloc(len+2); cm_back_slash_mount_root=malloc(len+2); if ((*pmount=='/') || (*pmount='\\')) pmount++; if( FAILED(StringCbCopy(cm_mount_root, len+1, pmount))) { fprintf (stderr, "fs_InitMountRoot - not enough space on output"); exit(1); } cm_slash_mount_root[0]='/'; if( FAILED(StringCbCopy(cm_slash_mount_root+1, len+1, pmount))) { fprintf (stderr, "fs_InitMountRoot - not enough space on output"); exit(1); } cm_back_slash_mount_root[0]='\\'; if( FAILED(StringCbCopy(cm_back_slash_mount_root+1, len+1, pmount))) { fprintf (stderr, "fs_InitMountRoot - not enough space on output"); exit(1); } } /* return a static pointer to a buffer */ char * fs_GetParent(char *apath) { static char tspace[1024]; char *tp; if( FAILED(StringCbCopy(tspace, sizeof(tspace), apath))) { fprintf (stderr, "tspace - not enough space"); exit(1); } tp = strrchr(tspace, '\\'); if (tp) { if (tp - tspace > 2 && tspace[1] == ':' && &tspace[2] == tp) *(tp+1) = 0; /* lv trailing slash so Parent("k:\foo") is "k:\" not "k:" */ else *tp = 0; } else { fs_ExtractDriveLetter(apath, tspace); if( FAILED(StringCbCat(tspace, sizeof(tspace), "."))) { fprintf (stderr, "tspace - not enough space"); exit(1); } } return tspace; } #if (_WINNT_WIN32 < 0x0600) typedef struct FILE_NAME_INFO { DWORD FileNameLength; WCHAR FileName[1]; } FILE_NAME_INFO, *PFILE_NAME_INFO; typedef enum _FILE_INFO_BY_HANDLE_CLASS { FileNameInfo = 2 } FILE_INFO_BY_HANDLE_CLASS, *PFILE_INFO_BY_HANDLE_CLASS; typedef BOOL (WINAPI * LPFN_GetFileInformationByHandleEx)( __in HANDLE hFile, __in FILE_INFO_BY_HANDLE_CLASS FileInformationClass, __out LPVOID lpFileInformation, __in DWORD dwBufferSize ); #endif static long GetFileSystemNameInfo(const char *fileNamep, char *outNamep, size_t outSize) { HANDLE hf; union { FILE_NAME_INFO nameInfo; CHAR buffer[2048]; } u; DWORD rc; #if (_WIN32_WINNT < 0x0600) HANDLE hKernel32; static LPFN_GetFileInformationByHandleEx GetFileInformationByHandleEx = NULL; if (!GetFileInformationByHandleEx) { hKernel32 = GetModuleHandle("kernel32.dll"); /* no refcount increase */ GetFileInformationByHandleEx = (LPFN_GetFileInformationByHandleEx)GetProcAddress(hKernel32, "GetFileInformationByHandleEx"); } #endif if (!GetFileInformationByHandleEx) return -1; hf = CreateFile( fileNamep, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); if (hf == INVALID_HANDLE_VALUE) return -1; rc = GetFileInformationByHandleEx( hf, FileNameInfo, &u, sizeof(u)); CloseHandle(hf); if (rc == FALSE) return -1; rc = WideCharToMultiByte(CP_UTF8, 0, u.nameInfo.FileName, u.nameInfo.FileNameLength, outNamep, outSize, NULL, FALSE); if (rc == 0) /* failure */ return -1; if (outNamep[0] == '\\' && outNamep[u.nameInfo.FileNameLength/2 - 1] == '\\') outNamep[u.nameInfo.FileNameLength/2 - 1] = 0; else outNamep[min(u.nameInfo.FileNameLength/2, outSize)] = 0; /* make sure we are nul terminated */ return 0; } /* this function returns TRUE (1) if the file is in AFS, otherwise false (0) */ int fs_InAFS(char *apath) { char fsname[MAX_PATH+2]="\\"; struct ViceIoctl blob; cm_ioctlQueryOptions_t options; cm_fid_t fid; afs_int32 code; /* * If the operating system supports the name information query * use it to resolve the full file path. This will take care * of any mappings, ntfs junctions, etc. */ if (GetFileSystemNameInfo(apath, &fsname[1], sizeof(fsname)-1) == 0) apath = fsname; memset(&options, 0, sizeof(options)); options.size = sizeof(options); options.field_flags |= CM_IOCTL_QOPTS_FIELD_LITERAL; options.literal = 1; 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_utf8(apath, VIOCGETFID, &blob, 1); if (code) { if ((errno == EINVAL) || (errno == ENOENT)) return 0; } return 1; } int fs_IsFreelanceRoot(char *apath) { struct ViceIoctl blob; afs_int32 code; char space[AFS_PIOCTL_MAXSIZE]; blob.in_size = 0; blob.out_size = AFS_PIOCTL_MAXSIZE; blob.out = space; code = pioctl_utf8(apath, VIOC_FILE_CELL_NAME, &blob, 1); if (code == 0) { return !cm_strnicmp_utf8N("Freelance.Local.Root",space, blob.out_size); } return 1; /* assume it is because it is more restrictive that way */ } const char * fs_NetbiosName(void) { static char buffer[NETBIOSNAMESZ] = "AFS"; HKEY parmKey; DWORD code; DWORD dummyLen; DWORD enabled = 0; code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY, 0, (IsWow64()?KEY_WOW64_64KEY:0)|KEY_QUERY_VALUE, &parmKey); if (code == ERROR_SUCCESS) { dummyLen = sizeof(buffer); code = RegQueryValueEx(parmKey, "NetbiosName", NULL, NULL, buffer, &dummyLen); RegCloseKey (parmKey); } else { if( FAILED(StringCbCopy(buffer, sizeof(buffer), "AFS"))) { fprintf (stderr, "buffer - not enough space"); exit(1); } } return buffer; } #define AFSCLIENT_ADMIN_GROUPNAME "AFS Client Admins" BOOL fs_IsAdmin (void) { static BOOL fAdmin = FALSE; static BOOL fTested = FALSE; if (!fTested) { /* Obtain the SID for the AFS client admin group. If the group does * not exist, then assume we have AFS client admin privileges. */ PSID psidAdmin = NULL; DWORD dwSize, dwSize2; char pszAdminGroup[ MAX_COMPUTERNAME_LENGTH + sizeof(AFSCLIENT_ADMIN_GROUPNAME) + 2 ]; char *pszRefDomain = NULL; SID_NAME_USE snu = SidTypeGroup; dwSize = sizeof(pszAdminGroup); if (!GetComputerName(pszAdminGroup, &dwSize)) { /* Can't get computer name. We return false in this case. Retain fAdmin and fTested. This shouldn't happen.*/ return FALSE; } dwSize = 0; dwSize2 = 0; if( FAILED(StringCbCat(pszAdminGroup, MAX_COMPUTERNAME_LENGTH + sizeof(AFSCLIENT_ADMIN_GROUPNAME) + 2,"\\"))) { fprintf (stderr, "pszAdminGroup - not enough space"); exit(1); } if( FAILED(StringCbCat(pszAdminGroup, MAX_COMPUTERNAME_LENGTH + sizeof(AFSCLIENT_ADMIN_GROUPNAME) + 2, AFSCLIENT_ADMIN_GROUPNAME))) { fprintf (stderr, "pszAdminGroup - not enough space"); exit(1); } LookupAccountName(NULL, pszAdminGroup, NULL, &dwSize, NULL, &dwSize2, &snu); /* that should always fail. */ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { /* if we can't find the group, then we allow the operation */ fAdmin = TRUE; return TRUE; } if (dwSize == 0 || dwSize2 == 0) { /* Paranoia */ fAdmin = TRUE; return TRUE; } psidAdmin = (PSID)malloc(dwSize); memset(psidAdmin,0,dwSize); assert(psidAdmin); pszRefDomain = (char *)malloc(dwSize2); assert(pszRefDomain); if (!LookupAccountName(NULL, pszAdminGroup, psidAdmin, &dwSize, pszRefDomain, &dwSize2, &snu)) { /* We can't lookup the group now even though we looked it up earlier. Could this happen? */ fAdmin = TRUE; } else { /* Then open our current ProcessToken */ HANDLE hToken; if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken)) { if (!CheckTokenMembership(hToken, psidAdmin, &fAdmin)) { /* We'll have to allocate a chunk of memory to store the list of * groups to which this user belongs; find out how much memory * we'll need. */ DWORD dwSize = 0; PTOKEN_GROUPS pGroups; GetTokenInformation (hToken, TokenGroups, NULL, dwSize, &dwSize); pGroups = (PTOKEN_GROUPS)malloc(dwSize); assert(pGroups); /* Allocate that buffer, and read in the list of groups. */ if (GetTokenInformation (hToken, TokenGroups, pGroups, dwSize, &dwSize)) { /* Look through the list of group SIDs and see if any of them * matches the AFS Client Admin group SID. */ size_t iGroup = 0; for (; (!fAdmin) && (iGroup < pGroups->GroupCount); ++iGroup) { if (EqualSid (psidAdmin, pGroups->Groups[ iGroup ].Sid)) { fAdmin = TRUE; } } } if (pGroups) free(pGroups); } /* if do not have permission because we were not explicitly listed * in the Admin Client Group let's see if we are the SYSTEM account */ if (!fAdmin) { PTOKEN_USER pTokenUser; SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY; PSID pSidLocalSystem = 0; DWORD gle; GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize); pTokenUser = (PTOKEN_USER)malloc(dwSize); assert(pTokenUser); if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize)) gle = GetLastError(); if (AllocateAndInitializeSid( &SIDAuth, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSidLocalSystem)) { if (EqualSid(pTokenUser->User.Sid, pSidLocalSystem)) { fAdmin = TRUE; } FreeSid(pSidLocalSystem); } if ( pTokenUser ) free(pTokenUser); } } } free(psidAdmin); free(pszRefDomain); fTested = TRUE; } return fAdmin; } void fs_FreeUtf8CmdLine(int argc, char ** argv) { int i; for (i=0; i < argc; i++) { if (argv[i]) free(argv[i]); } free(argv); } char ** fs_MakeUtf8Cmdline(int argc, const wchar_t **wargv) { char ** argv; int i; argv = calloc(argc, sizeof(argv[0])); if (argv == NULL) return NULL; for (i=0; i < argc; i++) { int s; s = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, NULL, 0, NULL, FALSE); if (s == 0 || (argv[i] = calloc(s+1, sizeof(char))) == NULL) { break; } s = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], s+1, NULL, FALSE); if (s == 0) { break; } } if (i < argc) { fs_FreeUtf8CmdLine(argc, argv); return NULL; } return argv; } static const char *pn = ""; void fs_SetProcessName(const char *name) { pn = name; } void fs_Die(int code, char *filename) { if (code == EINVAL) { if (filename) fprintf(stderr,"%s: Invalid argument; it is possible that %s is not in AFS.\n", pn, filename); else fprintf(stderr,"%s: Invalid argument.\n", pn); } else if (code == ENOENT) { if (filename) fprintf(stderr,"%s: File '%s' doesn't exist\n", pn, filename); else fprintf(stderr,"%s: no such file returned\n", pn); } else if (code == EEXIST) { if (filename) fprintf(stderr,"%s: File '%s' already exists.\n", pn, filename); else fprintf(stderr,"%s: the specified file already exists.\n", pn); } else if (code == EROFS) fprintf(stderr,"%s: You can not change a backup or readonly volume\n", pn); else if (code == EACCES || code == EPERM) { if (filename) fprintf(stderr,"%s: You don't have the required access rights on '%s'\n", pn, filename); else fprintf(stderr,"%s: You do not have the required rights to do this operation\n", pn); } else if (code == ENODEV) { fprintf(stderr,"%s: AFS service may not have started.\n", pn); } else if (code == ESRCH) { /* hack */ fprintf(stderr,"%s: Cell name not recognized.\n", pn); } else if (code == EPIPE) { /* hack */ fprintf(stderr,"%s: Volume name or ID not recognized.\n", pn); } else if (code == EFBIG) { fprintf(stderr,"%s: Cache size too large.\n", pn); } else if (code == ETIMEDOUT) { if (filename) fprintf(stderr,"%s:'%s': Connection timed out", pn, filename); else fprintf(stderr,"%s: Connection timed out", pn); } else if (code == EBUSY) { if (filename) fprintf(stderr,"%s: All servers are busy on which '%s' resides\n", pn, filename); else fprintf(stderr,"%s: All servers are busy\n", pn); } else if (code == ENXIO) { if (filename) fprintf(stderr,"%s: All volume instances are offline on which '%s' resides\n", pn, filename); else fprintf(stderr,"%s: All volume instances are offline\n", pn); } else if (code == ENOSYS) { if (filename) fprintf(stderr,"%s: All servers are down on which '%s' resides\n", pn, filename); else fprintf(stderr,"%s: All servers are down\n", pn); } else if (code == ECHILD) { /* hack */ if (filename) fprintf(stderr,"%s: Invalid owner specified for '%s'\n", pn, filename); else fprintf(stderr,"%s: Invalid owner specified\n", pn); } else { if (filename) fprintf(stderr,"%s:'%s'", pn, filename); else fprintf(stderr,"%s", pn); fprintf(stderr, ": code 0x%x\n", code); } } /* values match cache manager File Types */ const char * fs_filetypestr(afs_uint32 type) { char * s = "Object"; switch (type) { case 1: s = "File"; break; case 2: s = "Directory"; break; case 3: s = "Symlink"; break; case 4: s = "Mountpoint"; break; case 5: s = "DfsLink"; break; } return s; }