/* copyright (c) 2005 * the regents of the university of michigan * all rights reserved * * permission is granted to use, copy, create derivative works and * redistribute this software and such derivative works for any purpose, * so long as the name of the university of michigan is not used in * any advertising or publicity pertaining to the use or distribution * of this software without specific, written prior authorization. if * the above copyright notice or any other identification of the * university of michigan is included in any copy of any portion of * this software, then the disclaimer below must also be included. * * this software is provided as is, without representation from the * university of michigan as to its fitness for any purpose, and without * warranty by the university of michigan of any kind, either express * or implied, including without limitation the implied warranties of * merchantability and fitness for a particular purpose. the regents * of the university of michigan shall not be liable for any damages, * including special, indirect, incidental, or consequential damages, * with respect to any claim arising out or in connection with the use * of the software, even if it has been or is hereafter advised of the * possibility of such damages. */ #include #include "afsd.h" #include #include "..\afsrdr\kif.h" #include "..\afsrdr\ifs_rpc.h" #include "afsdifs.h" /****************************/ /* parameters, macros, etc. */ /****************************/ #define IFSL_SUCCEEDED(st) (!(st & IFSL_FAIL_BASE)) #define MAP_RETURN(code) if (code) return ifs_MapCmError(code); /* defined in multiple places (search source) */ #define BUF_FILEHASH(fidp) ((((fidp)->vnode+((fidp)->unique << 13) + ((fidp)->unique >> (32-13)) \ +(fidp)->volume+(fidp)->cell) \ /*& 0xffffffff*/)) #define ROOTPATH "\\" #define TRANSFER_BUF_SIZE (2*1024*1024) #define SCPL_LOCK EnterCriticalSection(&scp_list_lock); #define SCPL_UNLOCK LeaveCriticalSection(&scp_list_lock); #define MAX_USERS 32 /****************************/ /* structs */ /****************************/ struct user_map_entry /* how we keep users straight. total of MAX_USERS of these */ { LARGE_INTEGER id; /* internal id created by kernel */ cm_user_t *creds; /* global (thread-specific) var userp is set to this */ }; struct scp_status /* one for each unique file in afs */ { struct scp_status *next; /* stored in a global chain in a chain locked by SCPL_[UN]LOCK */ cm_scache_t *scp; /* file handle used with cm_ fns */ ULONG fid; /* internal id generated by BUF_FILEHASH from AFS's 128-bit FID */ }; typedef struct scp_status scp_status_t; struct readdir_context /* temporary struct, allocated as necessary, for cm_Apply callback */ { char *matchString; /* for matching against */ char *buf, *buf_pos; /* filling buffer to length, currently at buf_pos */ ULONG length; ULONG count; /* number of entries packed so far */ }; typedef struct readdir_context readdir_context_t; /****************************/ /* global vars */ /****************************/ __declspec(thread) cm_user_t *userp; struct user_map_entry user_map[MAX_USERS]; CRITICAL_SECTION mapLock, scp_list_lock; extern HANDLE DoTerminate; scp_status_t *scp_list_head = NULL; /****************************/ /* error functions */ /****************************/ char *IfslErrorToText(unsigned long ifsl) { switch (ifsl) { case IFSL_SUCCESS: return "success"; case IFSL_DOES_NOT_EXIST: return "does not exist"; case IFSL_NOT_IMPLEMENTED: return "not implemented"; case IFSL_END_OF_ENUM: return "end of enum"; case IFSL_CANNOT_MAKE: return "cannot make"; case IFSL_END_OF_FILE: return "end of file"; case IFSL_NO_ACCESS: return "no access"; case IFSL_BUFFER_TOO_SMALL: return "buffer too small"; case IFSL_SHARING_VIOLATION: return "sharing violation"; case IFSL_BAD_INPUT: return "bad input"; case IFSL_GENERIC_FAILURE: return "generic failure"; case IFSL_OPEN_CREATED: return "open created"; case IFSL_OPEN_EXISTS: return "open exists"; case IFSL_OPEN_OPENED: return "opened"; case IFSL_OPEN_OVERWRITTEN: return "overwritten"; case IFSL_OPEN_SUPERSCEDED: return "supersceded"; case IFSL_BADFILENAME: return "bad filename"; case IFSL_READONLY: return "read only"; case IFSL_IS_A_DIR: return "is a dir"; case IFSL_PATH_DOES_NOT_EXIST: return "path does not exist"; case IFSL_IS_A_FILE: return "is a file"; case IFSL_NOT_EMPTY: return "dir not empty"; case IFSL_UNSPEC: return "unspecified error"; default: return "NOT FOUND"; } } unsigned long ifs_MapCmError(unsigned long code) { switch (code) { case CM_ERROR_STOPNOW: case 0: return IFSL_SUCCESS; case CM_ERROR_NOSUCHCELL: case CM_ERROR_NOSUCHVOLUME: case CM_ERROR_NOSUCHFILE: // x return IFSL_DOES_NOT_EXIST; case CM_ERROR_NOSUCHPATH: // x return IFSL_PATH_DOES_NOT_EXIST; case CM_ERROR_BADNTFILENAME: return IFSL_BADFILENAME; case CM_ERROR_TIMEDOUT: case CM_ERROR_ALLOFFLINE: case CM_ERROR_CLOCKSKEW: case CM_ERROR_REMOTECONN: case CM_ERROR_ALLBUSY: return IFSL_GENERIC_FAILURE; case CM_ERROR_NOACCESS: return IFSL_NO_ACCESS; case CM_ERROR_RETRY: case CM_ERROR_TOOBIG: case CM_ERROR_BADFD: case CM_ERROR_BADFDOP: case CM_ERROR_CROSSDEVLINK: return IFSL_GENERIC_FAILURE; case CM_ERROR_EXISTS: return IFSL_OPEN_EXISTS; case CM_ERROR_BADOP: case CM_ERROR_INVAL: case CM_ERROR_UNKNOWN: case CM_ERROR_BADSMB: return IFSL_GENERIC_FAILURE;//TODO:? ERR - STATUS_NO_MORE_FILES; case CM_ERROR_NOTDIR: case CM_ERROR_ISDIR: case CM_ERROR_READONLY: return IFSL_BAD_INPUT; case CM_ERROR_BUFFERTOOSMALL: return IFSL_BUFFER_TOO_SMALL; case CM_ERROR_WOULDBLOCK: case CM_ERROR_BADSHARENAME: case CM_ERROR_NOMORETOKENS: case CM_ERROR_NOTEMPTY: case CM_ERROR_USESTD: case CM_ERROR_ATSYS: return IFSL_GENERIC_FAILURE; case CM_ERROR_NOFILES: case CM_ERROR_BADTID: return IFSL_END_OF_ENUM; case CM_ERROR_PARTIALWRITE: case CM_ERROR_NOIPC: case CM_ERROR_RENAME_IDENTICAL: case CM_ERROR_AMBIGUOUS_FILENAME: return IFSL_GENERIC_FAILURE; case IFSL_SHARING_VIOLATION: return IFSL_SHARING_VIOLATION; case IFSL_NOT_EMPTY: return IFSL_NOT_EMPTY; case CM_ERROR_SPACE: case CM_ERROR_QUOTA: return IFSL_OVERQUOTA; } return IFSL_GENERIC_FAILURE; } /****************************/ /* support fns */ /****************************/ cm_scache_t *ifs_FindScp(ULONG fid) /* walk list to find scp<->fid mapping */ { scp_status_t *curr; SCPL_LOCK; curr = scp_list_head; while (curr) { if (curr->fid == fid) { SCPL_UNLOCK; return curr->scp; } curr = curr->next; } SCPL_UNLOCK; return NULL; } /* must call with scp write-locked. will always return correct results unless network fails (it loops properly). */ ifs_CheckAcl(cm_scache_t *scp, ULONG access, ULONG *granted) { long outRights, code; cm_req_t req; cm_InitReq(&req); /* ripped from cm_scache.c */ while (1) { if (cm_HaveAccessRights(scp, userp, access, granted)) { return 0; } else { /* we don't know the required access rights */ code = cm_GetAccessRights(scp, userp, &req); MAP_RETURN(code); continue; } } return 0; } /* extract data from scp. in ifs_ support function to centralize changes. */ ifs_CopyInfo(cm_scache_t *scp, ULONG *attribs, LARGE_INTEGER *size, LARGE_INTEGER *creation, LARGE_INTEGER *access, LARGE_INTEGER *change, LARGE_INTEGER *written) { access->QuadPart = 0; /* these mappings are not quite correct. we have the */ change->QuadPart = scp->clientModTime; /* right to leave them zero, if necessary. */ written->QuadPart = scp->clientModTime; creation->QuadPart = scp->serverModTime; *attribs = 0; if (scp->fileType == CM_SCACHETYPE_DIRECTORY || scp->fileType == CM_SCACHETYPE_SYMLINK || scp->fileType == CM_SCACHETYPE_MOUNTPOINT/* || scp->fileType == 0*/) *attribs |= FILE_ATTRIBUTE_DIRECTORY; /*if (!attribs && scp->fileType == CM_SCACHETYPE_FILE) *attribs |= FILE_ATTRIBUTE_NORMAL;*/ if (*attribs == FILE_ATTRIBUTE_DIRECTORY) size->QuadPart = 0; else *size = scp->length; return 0; } /* close and zero scp pointer. zeroing pointer should help eliminate accessing discarded cache entries. */ void ifs_InternalClose(cm_scache_t **scp) { osi_assert(scp && *scp); lock_ObtainMutex(&((*scp)->mx)); cm_ReleaseSCache(*scp); if ((*scp)->refCount == 0) /* we haven't held scache for external use yet */ cm_DiscardSCache(*scp); lock_ReleaseMutex(&((*scp)->mx)); *scp = NULL; } /* normalizes path by removing trailing slashes. separates last path component with a null, so that *dirp points to parent path and *filep points to filename. modifies string path. */ BOOLEAN ifs_FindComponents(char *path, const char **dirp, const char **filep) { char *lastSep; BOOLEAN removed; static char emptyPath[] = "\\"; /* if the path contains only one component, this is the parent. */ osi_assert(path); if (strlen(path)) removed = (path[strlen(path)-1] == '\\'); else removed = 1; lastSep = strrchr(path, '\\'); while (lastSep == path + strlen(path) - 1) { *lastSep = '\0'; lastSep = strrchr(path, '\\'); } if (lastSep) { *lastSep = '\0'; *dirp = path; *filep = lastSep + 1; } else { lastSep = path + strlen(path); *dirp = emptyPath; *filep = path; } return removed; } /* here to make maintenance easy */ unsigned long ifs_ConvertFileName(wchar_t *in, unsigned int inchars, char *out, unsigned int outchars) { unsigned long code; code = WideCharToMultiByte(CP_UTF8, 0/*WC_NO_BEST_FIT_CHARS*/, in, inchars, out, outchars-1, NULL, NULL); if (!code) return IFSL_BADFILENAME; return 0; } /* called by rpc_ library to let us initialize environment. call with id of zero to clear current thread auth. */ ifs_ImpersonateClient(LARGE_INTEGER user_id) { int x, empty; if (!user_id.QuadPart) { userp = NULL; return 0; } empty = -1; EnterCriticalSection(&mapLock); for (x = 0; x < MAX_USERS; x++) { if (user_map[x].id.QuadPart == 0) empty = x; if (user_map[x].id.QuadPart == user_id.QuadPart) goto done; } if (empty == -1) { LeaveCriticalSection(&mapLock); return -1; } user_map[empty].id = user_id; user_map[empty].creds = cm_NewUser(); x = empty; done: userp = user_map[x].creds; LeaveCriticalSection(&mapLock); return 0; } /****************************/ /* upcalls */ /****************************/ uc_namei(WCHAR *name, ULONG *fid) /* performs name<->fid mapping, and enters it into table */ { char *buffer; /* we support semi-infinite path lengths */ long code; cm_scache_t *scp, *dscp; char *dirp, *filep; cm_req_t req; scp_status_t *st; short len; cm_InitReq(&req); len = wcslen(name)+20; /* characters *should* map 1<->1, but in case */ buffer = malloc(len); code = ifs_ConvertFileName(name, -1, buffer, len); if (code) { free(buffer); MAP_RETURN(code); } ifs_FindComponents(buffer, &dirp, &filep); code = cm_NameI(cm_data.rootSCachep, dirp, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH, userp, ROOTPATH, &req, &dscp); if (code) { free(buffer); MAP_RETURN(code); } if (*filep) code = cm_Lookup(dscp, filep, 0, userp, &req, &scp); else cm_HoldSCache(scp = dscp); cm_ReleaseSCache(dscp); if (code) { free(buffer); MAP_RETURN(code); } SCPL_LOCK; st = malloc(sizeof(scp_status_t)); st->scp = scp; st->fid = BUF_FILEHASH(&scp->fid); st->next = scp_list_head; scp_list_head = st; SCPL_UNLOCK; *fid = st->fid; free(buffer); return 0; } /* this should only be called right after open, so we do not need to stat file. * we only check the server's restrictions. sharing violations are handled in the * kernel. the access mode we grant sticks with the file_object until its death. */ uc_check_access(ULONG fid, ULONG access, ULONG *granted) { ULONG afs_acc, afs_gr; cm_scache_t *scp; ULONG gr; BOOLEAN file, dir; gr = 0; scp = ifs_FindScp(fid); if (!scp) return IFSL_BAD_INPUT; file = (scp->fileType == CM_SCACHETYPE_FILE); dir = !file; /* access definitions from prs_fs.h */ afs_acc = 0; if (access & FILE_READ_DATA) afs_acc |= PRSFS_READ; if (file && ((access & FILE_WRITE_DATA) || (access & FILE_APPEND_DATA))) afs_acc |= PRSFS_WRITE; if (access & FILE_WRITE_EA || access & FILE_WRITE_ATTRIBUTES) afs_acc |= PRSFS_WRITE; if (dir && ((access & FILE_ADD_FILE) || (access & FILE_ADD_SUBDIRECTORY))) afs_acc |= PRSFS_INSERT; if (dir && (access & FILE_LIST_DIRECTORY)) afs_acc |= PRSFS_LOOKUP; if (access & FILE_READ_EA || access & FILE_READ_ATTRIBUTES) afs_acc |= PRSFS_LOOKUP; if (file && (access & FILE_EXECUTE)) /* look at making this require write access */ afs_acc |= PRSFS_WRITE; if (dir && (access & FILE_TRAVERSE)) afs_acc |= PRSFS_READ; if (dir && (access & FILE_DELETE_CHILD)) afs_acc |= PRSFS_DELETE; if (/*file && */(access & DELETE)) afs_acc |= PRSFS_DELETE; /* check ACL with server */ lock_ObtainMutex(&(scp->mx)); ifs_CheckAcl(scp, afs_acc, &afs_gr); lock_ReleaseMutex(&(scp->mx)); *granted = 0; if (afs_gr & PRSFS_READ) *granted |= FILE_READ_DATA | FILE_EXECUTE; if (afs_gr & PRSFS_WRITE) *granted |= FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | FILE_EXECUTE; // last one hack if (afs_gr & PRSFS_INSERT) *granted |= (dir ? FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY : 0) | (file ? FILE_ADD_SUBDIRECTORY : 0); if (afs_gr & PRSFS_LOOKUP) *granted |= (dir ? FILE_LIST_DIRECTORY : 0) | FILE_READ_EA | FILE_READ_ATTRIBUTES; if (afs_gr & PRSFS_DELETE) *granted |= FILE_DELETE_CHILD | DELETE; if (afs_gr & PRSFS_LOCK) *granted |= 0; if (afs_gr & PRSFS_ADMINISTER) *granted |= 0; * granted |= SYNCHRONIZE | READ_CONTROL; return 0; } uc_create(WCHAR *name, ULONG attribs, LARGE_INTEGER alloc, ULONG access, ULONG *granted, ULONG *fid) { char *buffer; /* we support semi-infinite path lengths */ long code; cm_scache_t *scp, *dscp; char *dirp, *filep; unsigned char removed; cm_req_t req; scp_status_t *st; cm_attr_t attr; short len; cm_InitReq(&req); len = wcslen(name)+20; /* characters *should* map 1<->1, but in case */ buffer = malloc(len); code = ifs_ConvertFileName(name, -1, buffer, len); if (code) { free(buffer); MAP_RETURN(code); } removed = ifs_FindComponents(buffer, &dirp, &filep); /* lookup the parent directory, which must exist */ code = cm_NameI(cm_data.rootSCachep, dirp, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH, userp, ROOTPATH, &req, &dscp); if (code) { free(buffer); MAP_RETURN(code); } osi_assert(filep); if (*filep) { attr.mask = CM_ATTRMASK_LENGTH; attr.length = alloc; if (attribs & FILE_ATTRIBUTE_DIRECTORY) { code = cm_MakeDir(dscp, filep, 0, &attr, userp, &req); if (!code) code = cm_Lookup(dscp, filep, 0, userp, &req, &scp); } else { /* for debugging strange error */ /*if (!strcmp(filep+strlen(filep)-3, "478") || !strcmp(filep+strlen(filep)-3, "503")) _asm int 3;*/ code = cm_Create(dscp, filep, 0, &attr, &scp, userp, &req); } } cm_ReleaseSCache(dscp); if (code) { free(buffer); MAP_RETURN(code); } SCPL_LOCK; st = malloc(sizeof(scp_status_t)); st->scp = scp; st->fid = BUF_FILEHASH(&scp->fid); st->next = scp_list_head; scp_list_head = st; SCPL_UNLOCK; *fid = st->fid; *granted = access; return 0; } /* this does not fill the attribs member completely. additional flags must be added in the kernel, such as read-only. */ uc_stat(ULONG fid, ULONG *attribs, LARGE_INTEGER *size, LARGE_INTEGER *creation, LARGE_INTEGER *access, LARGE_INTEGER *change, LARGE_INTEGER *written) { cm_scache_t *scp; cm_req_t req; ULONG code; scp = ifs_FindScp(fid); if (!scp) return IFSL_BAD_INPUT; /* stat file; don't want callback */ cm_InitReq(&req); lock_ObtainMutex(&(scp->mx)); cm_HoldUser(userp); code = cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_GETSTATUS); cm_ReleaseUser(userp); if (code) lock_ReleaseMutex(&(scp->mx)); MAP_RETURN(code); code = ifs_CopyInfo(scp, attribs, size, creation, access, change, written); lock_ReleaseMutex(&(scp->mx)); MAP_RETURN(code); return 0; } /* set atime, mtime, etc. */ uc_setinfo(ULONG fid, ULONG attribs, LARGE_INTEGER creation, LARGE_INTEGER access, LARGE_INTEGER change, LARGE_INTEGER written) { return IFSL_GENERIC_FAILURE; } //FIX/quota errors /* truncate or extend file, in cache and on server */ uc_trunc(ULONG fid, LARGE_INTEGER size) { ULONG code, gr; cm_scache_t *scp; cm_req_t req; osi_hyper_t oldLen, writePos; long written; scp = ifs_FindScp(fid); if (!scp) return IFSL_BAD_INPUT; /*code = ifs_CheckAcl(scp, FILE_WRITE_DATA, &gr); if (code) return code; if (!(gr & FILE_WRITE_DATA)) return IFSL_NO_ACCESS;*/ cm_InitReq(&req); lock_ObtainMutex(&(scp->mx)); code = cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_GETSTATUS); if (code) lock_ReleaseMutex(&(scp->mx)); MAP_RETURN(code); oldLen = scp->length; lock_ReleaseMutex(&(scp->mx)); code = cm_SetLength(scp, &size, userp, &req); MAP_RETURN(code); /*code = cm_FSync(scp, userp, &req); MAP_RETURN(code);*/ /*code = cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_GETSTATUS); MAP_RETURN(code);*/ #if 0 /* attempt to write last byte of file. fails to help because of delayed writing. */ if (oldLen.QuadPart < size.QuadPart) { writePos.QuadPart = size.QuadPart - 1; WriteData(scp, writePos, 1, &"\0\0\0", userp, &written); MAP_RETURN(code); if (written != 1) return IFSL_UNSPEC; } #endif /*cm_SyncOp(scp, NULL, cm_Flush(*/ //MAP_RETURN(code); return 0; } /* read data from a file */ uc_read(ULONG fid, LARGE_INTEGER offset, ULONG length, ULONG *read, char *data) { ULONG code; cm_scache_t *scp; cm_req_t req; //_asm int 3; *read = 0; scp = ifs_FindScp(fid); if (!scp) return IFSL_BAD_INPUT; if (scp->fileType == CM_SCACHETYPE_DIRECTORY) return IFSL_IS_A_DIR; code = ReadData(scp, offset, (unsigned long)length, data, userp, read); MAP_RETURN(code); return 0; } //FIX/ handle quota errors properly /* write data to a file */ uc_write(ULONG fid, LARGE_INTEGER offset, ULONG length, ULONG *written, char *data) { ULONG code, gr; cm_scache_t *scp; cm_req_t req; scp = ifs_FindScp(fid); if (!scp) return IFSL_BAD_INPUT; /*code = ifs_CheckAcl(scp, FILE_WRITE_DATA, &gr); if (code) return code; if (!(gr & FILE_WRITE_DATA)) return IFSL_NO_ACCESS;*/ if (offset.QuadPart == -1) // perhaps re-stat here? offset = scp->length; code = WriteData(scp, offset, (unsigned long)length, data, userp, written); MAP_RETURN(code); return 0; } //need downcall for new length uc_rename(ULONG fid, WCHAR *curr, WCHAR *new_dir, WCHAR *new_name, ULONG *new_fid) { int code; cm_req_t req; //struct vnode *node; cm_attr_t attr; wchar_t *buf; char *curdir, *curfile, *newdir, *newfile; cm_scache_t *dscp1, *dscp2, *scp; char b1[MAX_PATH], b2[MAX_PATH], b3[MAX_PATH]; ULONG fid2; code = !(scp = ifs_FindScp(fid)); if (!code) code = ifs_ConvertFileName(curr, -1, b1, MAX_PATH); if (!code) code = ifs_ConvertFileName(new_name, -1, b2, MAX_PATH); if (!code) code = ifs_ConvertFileName(new_dir, -1, b3, MAX_PATH); if (!code) { ifs_FindComponents(b1, &curdir, &curfile); ifs_FindComponents(b2, &newdir, &newfile); newdir = b3; /*lock_ReleaseMutex(&scp->mx); cm_FSync(scp, userp, &req); if (scp->refCount != 1) _asm int 3; ifs_InternalClose(&scp);*/ uc_close(fid); code = cm_NameI(cm_data.rootSCachep, curdir, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH, userp, ROOTPATH, &req, &dscp1); } if (!code) { if (!strcmp(curdir, newdir)) { dscp2 = dscp1; dscp1->refCount++; } else code = cm_NameI(cm_data.rootSCachep, newdir, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH, userp, ROOTPATH, &req, &dscp2); if (!code) { code = cm_Rename(dscp1, curfile, dscp2, newfile, userp, &req); //ifs_InternalClose(&dscp2); //cm_InitReq(&req); //code = cm_NameI(cm_rootSCachep, newdir, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH, userp, ROOTPATH, &req, &dscp2); if (!code) { code = ifs_ConvertFileName(curr, -1, b1, MAX_PATH); code = uc_namei(b1, new_fid); //if (fid != fid2) // _asm int 3; //code = cm_Lookup(dscp2, newfile, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW, userp, &req, &node->scp); } ifs_InternalClose(&dscp2); } else { //code = cm_Lookup(dscp1, curfile, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW, userp, &req, &node->scp); code = ifs_ConvertFileName(curr, -1, b1, MAX_PATH); code = uc_namei(b1, new_fid); //if (fid != fid2) // _asm int 3; } ifs_InternalClose(&dscp1); } return 0; } ifs_ReaddirCallback(cm_scache_t *scp, cm_dirEntry_t *entry, void *param, osi_hyper_t *offset) { readdir_context_t *context; ULONG name_len, gr; readdir_data_t *info; char short_name[14], *endp; ULONG code; cm_req_t req; cm_scache_t *child_scp; cm_fid_t child_fid; int t; context = param; name_len = strlen(entry->name); info = (readdir_data_t *)context->buf_pos; if (context->length - (context->buf_pos - context->buf) < sizeof(readdir_data_t) + name_len * sizeof(WCHAR) + sizeof(LARGE_INTEGER)) { if (context->count == 0) return CM_ERROR_BUFFERTOOSMALL; info->cookie = *offset; return CM_ERROR_STOPNOW; } if ((context->matchString && context->matchString[0] && (!strcmp(context->matchString, entry->name) || context->matchString[0]=='*')) || !(context->matchString && context->matchString[0])) ; else return 0; cm_InitReq(&req); cm_HoldUser(userp); child_scp = NULL; child_fid.cell = scp->fid.cell; child_fid.volume = scp->fid.volume; child_fid.vnode = ntohl(entry->fid.vnode); child_fid.unique = ntohl(entry->fid.unique); code = cm_GetSCache(&child_fid, &child_scp, userp, &req); //code = cm_Lookup(scp, entry->name, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW, userp, &req, &child_scp); if (code || !child_scp) { cm_ReleaseUser(userp); return 0; } //if (child_scp->refCount == 1) { lock_ObtainMutex(&child_scp->mx); code = cm_SyncOp(child_scp, NULL, userp, &req, 0, CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK); // do not need callback lock_ReleaseMutex(&child_scp->mx); } if (code) /* perhaps blank fields we do not know, and continue. bad filents should not prevent readdirs. */ ; info->cookie = *offset; lock_ObtainMutex(&(child_scp->mx)); code = ifs_CopyInfo(child_scp, &info->attribs, &info->size, &info->creation, &info->access, &info->change, &info->write); //ifs_CheckAcl(child_scp, FILE_WRITE_DATA, &gr); /* perhaps add flag to not loop, to avoid network traffic if not found*/ //if (gr & FILE_READ_DATA && !(gr & FILE_WRITE_DATA)) // info->attribs |= FILE_ATTRIBUTE_READONLY; lock_ReleaseMutex(&(child_scp->mx)); ifs_InternalClose(&child_scp); MAP_RETURN(code); cm_Gen8Dot3Name(entry, short_name, &endp); *endp = '\0'; info->short_name_length = sizeof(WCHAR)*((t=MultiByteToWideChar(CP_UTF8, 0, short_name, -1, info->short_name, 14))?t-1:0); info->name_length = sizeof(WCHAR)*((t=MultiByteToWideChar(CP_UTF8, 0, entry->name, -1, info->name, 600))?t-1:0); context->buf_pos = ((char*)info) + sizeof(readdir_data_t) + info->name_length; context->count++; info = (readdir_data_t *)context->buf_pos; info->cookie.QuadPart = -1; return 0; } uc_readdir(ULONG fid, LARGE_INTEGER cookie_in, WCHAR *filter, ULONG *count, char *data, ULONG *len) { ULONG code; char buffer[2048]; cm_req_t req; cm_scache_t *scp, *child_scp; readdir_context_t context; LARGE_INTEGER cookie; if (cookie_in.QuadPart == -1) { *len = 0; *count = 0; return 0; } scp = ifs_FindScp(fid); if (!scp) return IFSL_BAD_INPUT; code = ifs_ConvertFileName(filter, -1, buffer, 2048); if (code) return code; cm_InitReq(&req); cm_HoldUser(userp); cookie = cookie_in; context.matchString = buffer; context.buf_pos = context.buf = data; context.length = *len; context.count = 0; *count = 0; //restart: ((LARGE_INTEGER *)context.buf)->QuadPart = -1; code = cm_ApplyDir(scp, ifs_ReaddirCallback, &context, &cookie, userp, &req, NULL); context.buf_pos += sizeof(LARGE_INTEGER); if (code != CM_ERROR_STOPNOW) goto done; //(*count)++; if (code) goto done; //goto restart; code = 0; done: *count = context.count; cm_ReleaseUser(userp); *len = context.buf_pos - context.buf; code = ifs_MapCmError(code); return code; } uc_close(ULONG fid) { ULONG code; cm_scache_t *scp; cm_req_t req; scp_status_t *prev, *curr; scp = ifs_FindScp(fid); if (!scp) return IFSL_BAD_INPUT; cm_InitReq(&req); cm_FSync(scp, userp, &req); SCPL_LOCK; /* perhaps this should be earlier */ lock_ObtainMutex(&(scp->mx)); cm_ReleaseSCache(scp); if (!scp->refCount) cm_DiscardSCache(scp); lock_ReleaseMutex(&(scp->mx)); prev = NULL, curr = scp_list_head; while (curr) { if (curr->fid == fid) { if (prev) prev->next = curr->next; else scp_list_head = curr->next; free(curr); break; } prev = curr; curr = curr->next; } SCPL_UNLOCK; return 0; } uc_unlink(WCHAR *name) { char buffer[2048]; long code; cm_scache_t *scp, *dscp; char *dirp, *filep; unsigned char removed; cm_req_t req; scp_status_t *st; cm_InitReq(&req); code = ifs_ConvertFileName(name, -1, buffer, 2048); MAP_RETURN(code); removed = ifs_FindComponents(buffer, &dirp, &filep); code = cm_NameI(cm_data.rootSCachep, dirp, CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH, userp, ROOTPATH, &req, &dscp); MAP_RETURN(code); if (*filep) { code = cm_Unlink(dscp, filep, userp, &req); if (code) code = cm_RemoveDir(dscp, filep, userp, &req); } else ; /* problem */ cm_ReleaseSCache(dscp); MAP_RETURN(code); return 0; } int uc_ioctl_write(ULONG length, char *data, ULONG *key) { int code; cm_req_t req; smb_ioctl_t *iop; iop = malloc(sizeof(smb_ioctl_t)); memset(iop, 0, sizeof(smb_ioctl_t)); smb_IoctlPrepareWrite(NULL, iop); memcpy(iop->inDatap + iop->inCopied, data, length); iop->inCopied += length; *key = (ULONG)iop; return 0; } int uc_ioctl_read(ULONG key, ULONG *length, char *data) { int code; cm_req_t req; smb_ioctl_t *iop; iop = key; osi_assert(iop); cm_HoldUser(userp); smb_IoctlPrepareRead(NULL, iop, userp); cm_ReleaseUser(userp); *length = iop->outDatap - iop->outAllocp; memcpy(data, iop->outAllocp, *length); free(iop); return 0; } int ifs_Init(char **reason) { HANDLE kcom; //_asm int 3; kcom = CreateFile("\\\\.\\afscom\\upcallhook", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, /*FILE_FLAG_OVERLAPPED*/0, NULL); if (kcom == INVALID_HANDLE_VALUE) { *reason = "error creating communications file"; return CM_ERROR_REMOTECONN; } CloseHandle(kcom); memset(user_map, 0, 32*sizeof(struct user_map_entry)); InitializeCriticalSection(&mapLock); InitializeCriticalSection(&scp_list_lock); return 0; } ifs_TransactRpc(char *outbuf, int outlen, char *inbuf, int *inlen) { HANDLE hf; int ret; DWORD err, read = 0; DWORD inmax; if (!outbuf || !inbuf) return IFSL_GENERIC_FAILURE; hf = CreateFile("\\\\.\\afscom\\downcall", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hf == INVALID_HANDLE_VALUE) return 0; inmax = *inlen; if (!DeviceIoControl(hf, IOCTL_AFSRDR_DOWNCALL, outbuf, outlen, inbuf, inmax, inlen, NULL)) { CloseHandle(hf); return IFSL_GENERIC_FAILURE; } CloseHandle(hf); return inlen ? IFSL_SUCCESS : IFSL_GENERIC_FAILURE; } DWORD WINAPI ifs_MainLoop(LPVOID param) { HANDLE pipe; DWORD written; unsigned char *bufIn, *bufOut; DWORD lenIn, lenOut, status; DWORD err; OVERLAPPED olp; rpc_t rpc; BOOL st; bufIn = VirtualAlloc(NULL, TRANSFER_BUF_SIZE, MEM_COMMIT, PAGE_READWRITE); bufOut = VirtualAlloc(NULL, TRANSFER_BUF_SIZE, MEM_COMMIT, PAGE_READWRITE); if (!bufIn || !bufOut) { if (bufIn) VirtualFree(bufIn, 0, MEM_RELEASE); if (bufOut) VirtualFree(bufOut, 0, MEM_RELEASE); printf("could not allocate transfer bufs\n"); PostMessage(NULL, WM_CLOSE, 0, 0); return 1; } //_asm int 3; pipe = CreateFile("\\\\.\\afscom\\upcallhook", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, /*FILE_FLAG_OVERLAPPED*/0, NULL); if (pipe == INVALID_HANDLE_VALUE) { VirtualFree(bufIn, 0, MEM_RELEASE); VirtualFree(bufOut, 0, MEM_RELEASE); printf("error creating communications file\n"); PostMessage(NULL, WM_CLOSE, 0, 0); return 1; } //_asm int 3; while (1) { if (WaitForSingleObject(DoTerminate, 0) == WAIT_OBJECT_0) break; st = ReadFile(pipe, bufIn, TRANSFER_BUF_SIZE, &lenIn, NULL); if (!st) if (GetLastError() == ERROR_INVALID_HANDLE) break; else continue; ZeroMemory(&rpc, sizeof(rpc)); rpc.in_buf = rpc.in_pos = bufIn; rpc.out_buf = rpc.out_pos = bufOut; rpc_parse(&rpc); st = WriteFile(pipe, rpc.out_buf, rpc.out_pos - rpc.out_buf, &written, NULL); if (!st) if (GetLastError() == ERROR_INVALID_HANDLE) break; else continue; } return 1; }