diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index 27a4c15b..e34b590d 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -137,6 +137,10 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE * @param PFileAttributes * Pointer to a memory location that will receive the file attributes on successful return * from this call. May be NULL. + * + * If this call returns STATUS_REPARSE, the file system MAY place here the index of the + * first reparse point within FileName. The file system MAY also leave this at its default + * value of 0. * @param SecurityDescriptor * Pointer to a buffer that will receive the file security descriptor on successful return * from this call. May be NULL. @@ -152,7 +156,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE * component. */ NTSTATUS (*GetSecurityByName)(FSP_FILE_SYSTEM *FileSystem, - PWSTR FileName, PUINT32 PFileAttributes, + PWSTR FileName, PUINT32 PFileAttributes/* or ReparsePointIndex */, PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize); /** * Create new file or directory. @@ -634,6 +638,8 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE * The file system on which this request is posted. * @param FileName * The name of the file or directory to have its reparse points resolved. + * @param ReparsePointIndex + * The index of the first reparse point within FileName. * @param OpenReparsePoint * If TRUE, the last path component of FileName should not be resolved, even * if it is a reparse point that can be resolved. If FALSE, all path components @@ -653,7 +659,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE * STATUS_REPARSE or error code. */ NTSTATUS (*ResolveReparsePoints)(FSP_FILE_SYSTEM *FileSystem, - PWSTR FileName, BOOLEAN OpenReparsePoint, + PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN OpenReparsePoint, PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize); /** * Get reparse point. @@ -993,6 +999,91 @@ FSP_API NTSTATUS FspFileSystemOpSetSecurity(FSP_FILE_SYSTEM *FileSystem, */ FSP_API BOOLEAN FspFileSystemAddDirInfo(FSP_FSCTL_DIR_INFO *DirInfo, PVOID Buffer, ULONG Length, PULONG PBytesTransferred); +/** + * Find reparse point in file name. + * + * Given a file name this function returns an index to the first path component that is a reparse + * point. The function will call the supplied GetReparsePointByName function for every path + * component until it finds a reparse point or the whole path is processed. + * + * This is a helper for implementing the GetSecurityByName operation in file systems + * that support reparse points. + * + * @param FileSystem + * The file system object. + * @param GetReparsePointByName + * Pointer to function that can retrieve reparse point information by name. The + * FspFileSystemFindReparsePoint will call this function with the Buffer and PSize + * arguments set to NULL. The function should return STATUS_SUCCESS if the passed + * FileName is a reparse point or STATUS_NOT_A_REPARSE_POINT (or other error code) + * otherwise. + * @param Context + * User context to supply to GetReparsePointByName. + * @param FileName + * The name of the file or directory. + * @param PReparsePointIndex + * Pointer to a memory location that will receive the index of the first reparse point + * within FileName. A value is only placed in this memory location if the function returns + * TRUE. May be NULL. + * @return + * TRUE if a reparse point was found, FALSE otherwise. + * @see + * GetSecurityByName + */ +FSP_API BOOLEAN FspFileSystemFindReparsePoint(FSP_FILE_SYSTEM *FileSystem, + NTSTATUS (*GetReparsePointByName)( + FSP_FILE_SYSTEM *FileSystem, PVOID Context, PWSTR FileName, PVOID Buffer, PSIZE_T PSize), + PVOID Context, + PWSTR FileName, PUINT32 PReparsePointIndex); +/** + * Resolve reparse points. + * + * Given a file name (and an index where to start resolving) this function will attempt to + * resolve as many reparse points as possible. The function will call the supplied + * GetReparsePointByName function for every path component until it resolves the reparse points + * or the whole path is processed. + * + * This is a helper for implementing the ResolveReparsePoints operation in file systems + * that support reparse points. + * + * @param FileSystem + * The file system object. + * @param GetReparsePointByName + * Pointer to function that can retrieve reparse point information by name. The function + * should return STATUS_SUCCESS if the passed FileName is a reparse point or + * STATUS_NOT_A_REPARSE_POINT (or other error code) otherwise. + * @param Context + * User context to supply to GetReparsePointByName. + * @param FileName + * The name of the file or directory to have its reparse points resolved. + * @param ReparsePointIndex + * The index of the first reparse point within FileName. + * @param OpenReparsePoint + * If TRUE, the last path component of FileName should not be resolved, even + * if it is a reparse point that can be resolved. If FALSE, all path components + * should be resolved if possible. + * @param PIoStatus + * Pointer to storage that will receive the status to return to the FSD. When + * this function succeeds it must set PIoStatus->Status to STATUS_REPARSE and + * PIoStatus->Information to either IO_REPARSE or the reparse tag. + * @param Buffer + * Pointer to a buffer that will receive the resolved file name (IO_REPARSE) or + * reparse data (reparse tag). If the function returns a file name, it should + * not be NULL terminated. + * @param PSize [in,out] + * Pointer to the buffer size. On input it contains the size of the buffer. + * On output it will contain the actual size of data copied. + * @return + * STATUS_REPARSE or error code. + * @see + * ResolveReparsePoints + */ +FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, + NTSTATUS (*GetReparsePointByName)( + FSP_FILE_SYSTEM *FileSystem, PVOID Context, PWSTR FileName, PVOID Buffer, PSIZE_T PSize), + PVOID Context, + PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN OpenReparsePoint, + PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize); /* * Security @@ -1001,7 +1092,7 @@ FSP_API PGENERIC_MAPPING FspGetFileGenericMapping(VOID); FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, BOOLEAN CheckParentDirectory, BOOLEAN AllowTraverseCheck, - UINT32 DesiredAccess, PUINT32 PGrantedAccess, + UINT32 DesiredAccess, PUINT32 PGrantedAccess/* or ReparsePointIndex */, PSECURITY_DESCRIPTOR *PSecurityDescriptor); FSP_API NTSTATUS FspCreateSecurityDescriptor(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, diff --git a/src/dll/fsop.c b/src/dll/fsop.c index 00a1ccd4..59035e10 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -121,8 +121,9 @@ FSP_API NTSTATUS FspFileSystemOpLeave(FSP_FILE_SYSTEM *FileSystem, } static inline -NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, - FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) +NTSTATUS FspFileSystemCallResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response, + UINT32 ReparsePointIndex) { NTSTATUS Result = STATUS_INVALID_DEVICE_REQUEST; IO_STATUS_BLOCK IoStatus; @@ -134,12 +135,27 @@ NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, Size = FSP_FSCTL_TRANSACT_RSP_SIZEMAX - FIELD_OFFSET(FSP_FSCTL_TRANSACT_RSP, Buffer); Result = FileSystem->Interface->ResolveReparsePoints(FileSystem, (PWSTR)Request->Buffer, + ReparsePointIndex, !!(Request->Req.Create.CreateOptions & FILE_OPEN_REPARSE_POINT), &IoStatus, Response->Buffer, &Size); - if (STATUS_REPARSE == Result) + if (NT_SUCCESS(Result)) + { + Result = STATUS_REPARSE; Response->IoStatus.Information = (UINT32)IoStatus.Information; + + if (0/*IO_REPARSE*/ == IoStatus.Information) + { + Response->Rsp.Create.Reparse.FileName.Offset = 0; + Response->Rsp.Create.Reparse.FileName.Size = (UINT16)Size; + } + else + { + Response->Rsp.Create.Reparse.Data.Offset = 0; + Response->Rsp.Create.Reparse.Data.Size = (UINT16)Size; + } + } } return Result; @@ -167,7 +183,10 @@ NTSTATUS FspFileSystemCreateCheck(FSP_FILE_SYSTEM *FileSystem, FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, PGrantedAccess, PSecurityDescriptor); if (STATUS_REPARSE == Result) - Result = FspFileSystemResolveReparsePoints(FileSystem, Request, Response); + { + Result = FspFileSystemCallResolveReparsePoints(FileSystem, Request, Response, *PGrantedAccess); + *PGrantedAccess = 0; + } else if (NT_SUCCESS(Result)) { *PGrantedAccess = (MAXIMUM_ALLOWED & Request->Req.Create.DesiredAccess) ? @@ -199,7 +218,10 @@ NTSTATUS FspFileSystemOpenCheck(FSP_FILE_SYSTEM *FileSystem, ((Request->Req.Create.CreateOptions & FILE_DELETE_ON_CLOSE) ? DELETE : 0), PGrantedAccess); if (STATUS_REPARSE == Result) - Result = FspFileSystemResolveReparsePoints(FileSystem, Request, Response); + { + Result = FspFileSystemCallResolveReparsePoints(FileSystem, Request, Response, *PGrantedAccess); + *PGrantedAccess = 0; + } else if (NT_SUCCESS(Result)) { if (0 == (Request->Req.Create.DesiredAccess & MAXIMUM_ALLOWED)) @@ -234,7 +256,10 @@ NTSTATUS FspFileSystemOverwriteCheck(FSP_FILE_SYSTEM *FileSystem, ((Request->Req.Create.CreateOptions & FILE_DELETE_ON_CLOSE) ? DELETE : 0), PGrantedAccess); if (STATUS_REPARSE == Result) - Result = FspFileSystemResolveReparsePoints(FileSystem, Request, Response); + { + Result = FspFileSystemCallResolveReparsePoints(FileSystem, Request, Response, *PGrantedAccess); + *PGrantedAccess = 0; + } else if (NT_SUCCESS(Result)) { if (0 == (Request->Req.Create.DesiredAccess & MAXIMUM_ALLOWED)) @@ -260,7 +285,10 @@ NTSTATUS FspFileSystemOpenTargetDirectoryCheck(FSP_FILE_SYSTEM *FileSystem, Result = FspAccessCheck(FileSystem, Request, TRUE, TRUE, Request->Req.Create.DesiredAccess, PGrantedAccess); if (STATUS_REPARSE == Result) - Result = FspFileSystemResolveReparsePoints(FileSystem, Request, Response); + { + Result = FspFileSystemCallResolveReparsePoints(FileSystem, Request, Response, *PGrantedAccess); + *PGrantedAccess = 0; + } return Result; } @@ -894,7 +922,7 @@ FSP_API NTSTATUS FspFileSystemOpFileSystemControl(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) { NTSTATUS Result; - PREPARSE_DATA_BUFFER SymlinkReparseData; + PREPARSE_DATA_BUFFER ReparseData; SIZE_T Offset, Size; Result = STATUS_INVALID_DEVICE_REQUEST; @@ -903,42 +931,39 @@ FSP_API NTSTATUS FspFileSystemOpFileSystemControl(FSP_FILE_SYSTEM *FileSystem, case FSCTL_GET_REPARSE_POINT: if (0 != FileSystem->Interface->GetReparsePoint) { - SymlinkReparseData = (PREPARSE_DATA_BUFFER)Response->Buffer; - memset(SymlinkReparseData, 0, sizeof *SymlinkReparseData); + ReparseData = (PREPARSE_DATA_BUFFER)Response->Buffer; + memset(ReparseData, 0, + FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer)); if (FileSystem->ReparsePointsSymbolicLinks) { Size = FSP_FSCTL_TRANSACT_RSP_SIZEMAX - FIELD_OFFSET(FSP_FSCTL_TRANSACT_RSP, Buffer) - - sizeof(*SymlinkReparseData); + FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer); Result = FileSystem->Interface->GetReparsePoint(FileSystem, Request, (PVOID)Request->Req.FileSystemControl.UserContext, (PWSTR)Request->Buffer, - SymlinkReparseData->SymbolicLinkReparseBuffer.PathBuffer, + ReparseData->SymbolicLinkReparseBuffer.PathBuffer, &Size); if (NT_SUCCESS(Result)) { Offset = 0; if (Size > 4 * sizeof(WCHAR) && - '\\' == SymlinkReparseData->SymbolicLinkReparseBuffer.PathBuffer[0] && - '?' == SymlinkReparseData->SymbolicLinkReparseBuffer.PathBuffer[1] && - '?' == SymlinkReparseData->SymbolicLinkReparseBuffer.PathBuffer[2] && - '\\' == SymlinkReparseData->SymbolicLinkReparseBuffer.PathBuffer[3]) + '\\' == ReparseData->SymbolicLinkReparseBuffer.PathBuffer[0] && + '?' == ReparseData->SymbolicLinkReparseBuffer.PathBuffer[1] && + '?' == ReparseData->SymbolicLinkReparseBuffer.PathBuffer[2] && + '\\' == ReparseData->SymbolicLinkReparseBuffer.PathBuffer[3]) Offset = 4 * sizeof(WCHAR); - SymlinkReparseData->ReparseTag = IO_REPARSE_TAG_SYMLINK; - SymlinkReparseData->ReparseDataLength = (USHORT)( + ReparseData->ReparseTag = IO_REPARSE_TAG_SYMLINK; + ReparseData->ReparseDataLength = (USHORT)( FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) - FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer) + Size); - SymlinkReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset = - 0; - SymlinkReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength = - (USHORT)Size; - SymlinkReparseData->SymbolicLinkReparseBuffer.PrintNameOffset = - (USHORT)Offset; - SymlinkReparseData->SymbolicLinkReparseBuffer.PrintNameLength = - (USHORT)(Size - Offset); - SymlinkReparseData->SymbolicLinkReparseBuffer.Flags = 0 == Offset ? + ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0; + ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength = (USHORT)Size; + ReparseData->SymbolicLinkReparseBuffer.PrintNameOffset = (USHORT)Offset; + ReparseData->SymbolicLinkReparseBuffer.PrintNameLength = (USHORT)(Size - Offset); + ReparseData->SymbolicLinkReparseBuffer.Flags = 0 == Offset ? 0 : SYMLINK_FLAG_RELATIVE; Response->Rsp.FileSystemControl.Buffer.Size = (UINT16)Size; @@ -949,7 +974,7 @@ FSP_API NTSTATUS FspFileSystemOpFileSystemControl(FSP_FILE_SYSTEM *FileSystem, Size = FSP_FSCTL_TRANSACT_RSP_SIZEMAX - FIELD_OFFSET(FSP_FSCTL_TRANSACT_RSP, Buffer); Result = FileSystem->Interface->GetReparsePoint(FileSystem, Request, (PVOID)Request->Req.FileSystemControl.UserContext, - (PWSTR)Request->Buffer, SymlinkReparseData, &Size); + (PWSTR)Request->Buffer, ReparseData, &Size); if (NT_SUCCESS(Result)) Response->Rsp.FileSystemControl.Buffer.Size = (UINT16)Size; } @@ -958,7 +983,7 @@ FSP_API NTSTATUS FspFileSystemOpFileSystemControl(FSP_FILE_SYSTEM *FileSystem, case FSCTL_SET_REPARSE_POINT: if (0 != FileSystem->Interface->SetReparsePoint) { - SymlinkReparseData = (PREPARSE_DATA_BUFFER) + ReparseData = (PREPARSE_DATA_BUFFER) (Request->Buffer + Request->Req.FileSystemControl.Buffer.Offset); if (FileSystem->ReparsePointsSymbolicLinks) @@ -966,15 +991,15 @@ FSP_API NTSTATUS FspFileSystemOpFileSystemControl(FSP_FILE_SYSTEM *FileSystem, Result = FileSystem->Interface->SetReparsePoint(FileSystem, Request, (PVOID)Request->Req.FileSystemControl.UserContext, (PWSTR)Request->Buffer, - SymlinkReparseData->SymbolicLinkReparseBuffer.PathBuffer + - SymlinkReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR), - SymlinkReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength); + ReparseData->SymbolicLinkReparseBuffer.PathBuffer + + ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR), + ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength); } else Result = FileSystem->Interface->SetReparsePoint(FileSystem, Request, (PVOID)Request->Req.FileSystemControl.UserContext, (PWSTR)Request->Buffer, - SymlinkReparseData, + ReparseData, Request->Req.FileSystemControl.Buffer.Size); } break; @@ -1053,3 +1078,204 @@ FSP_API BOOLEAN FspFileSystemAddDirInfo(FSP_FSCTL_DIR_INFO *DirInfo, return TRUE; } + +FSP_API BOOLEAN FspFileSystemFindReparsePoint(FSP_FILE_SYSTEM *FileSystem, + NTSTATUS (*GetReparsePointByName)( + FSP_FILE_SYSTEM *FileSystem, PVOID Context, PWSTR FileName, PVOID Buffer, PSIZE_T PSize), + PVOID Context, + PWSTR FileName, PUINT32 PReparsePointIndex) +{ + PWSTR p, lastp; + NTSTATUS Result; + + p = FileName; + for (;;) + { + while (L'\\' == *p) + p++; + lastp = p; + while (L'\\' != *p) + { + if (L'\0' == *p) + return FALSE; + p++; + } + + *p = L'\0'; + Result = GetReparsePointByName(FileSystem, Context, FileName, 0, 0); + *p = L'\\'; + + if (!NT_SUCCESS(Result)) + return FALSE; + else + { + if (0 != PReparsePointIndex) + *PReparsePointIndex = (ULONG)(lastp - FileName); + + return TRUE; + } + } + + return FALSE; +} + +FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, + NTSTATUS (*GetReparsePointByName)( + FSP_FILE_SYSTEM *FileSystem, PVOID Context, PWSTR FileName, PVOID Buffer, PSIZE_T PSize), + PVOID Context, + PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN OpenReparsePoint, + PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize) +{ + WCHAR c, *p, *lastp; + PWSTR TargetPath, TargetPathEnd, TargetLink; + union + { + REPARSE_DATA_BUFFER V; + UINT8 B[FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + + /* assumption: the substitute and print paths are stored in the same buffer */ + FSP_FSCTL_TRANSACT_PATH_SIZEMAX]; + } ReparseDataBuf; + PREPARSE_DATA_BUFFER ReparseData = &ReparseDataBuf.V; + SIZE_T Size, MaxTries = 32; + NTSTATUS Result; + + TargetPath = Buffer; + TargetPathEnd = TargetPath + *PSize / sizeof(WCHAR); + Size = (lstrlenW(FileName) + 1) * sizeof(WCHAR); + if (Size > *PSize) + return STATUS_OBJECT_NAME_INVALID; + memcpy(TargetPath, FileName, Size); + + p = TargetPath + ReparsePointIndex; + c = *p; + + for (;;) + { + while (L'\\' == *p) + p++; + lastp = p; + while (L'\\' != *p) + { + if (L'\0' == *p) + { + if (!OpenReparsePoint) + goto exit; + OpenReparsePoint = FALSE; + break; + } + p++; + } + + /* handle dot and dotdot! */ + if (L'.' == lastp[0]) + { + if (p == lastp + 1) + /* dot */ + continue; + + if (L'.' == lastp[1] && p == lastp + 1) + { + /* dotdot */ + p = lastp; + while (TargetPath < p) + { + p--; + if (L'\\' != *p) + break; + } + while (TargetPath < p) + { + p--; + if (L'\\' == *p) + break; + } + continue; + } + } + + c = *p; + *p = '\0'; + if (FileSystem->ReparsePointsSymbolicLinks) + { + Size = FSP_FSCTL_TRANSACT_PATH_SIZEMAX; + Result = GetReparsePointByName(FileSystem, Context, TargetPath, + ReparseData->SymbolicLinkReparseBuffer.PathBuffer, &Size); + } + else + { + Size = sizeof ReparseDataBuf; + Result = GetReparsePointByName(FileSystem, Context, TargetPath, + ReparseData, &Size); + } + *p = c; + + if (STATUS_NOT_A_REPARSE_POINT == Result) + /* it was not a reparse point; continue */ + continue; + else if (!NT_SUCCESS(Result)) + return Result; + + /* found a reparse point */ + + if (0 == --MaxTries) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + + if (FileSystem->ReparsePointsSymbolicLinks) + TargetLink = ReparseData->SymbolicLinkReparseBuffer.PathBuffer; + else + { + if (IO_REPARSE_TAG_SYMLINK == ReparseData->ReparseTag) + { + TargetLink = ReparseData->SymbolicLinkReparseBuffer.PathBuffer + + ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset; + Size = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength; + } + else + { + /* not a symlink; return the full reparse point! */ + if (Size > *PSize) + return STATUS_OBJECT_NAME_INVALID; + memcpy(Buffer, ReparseData, Size); + + goto no_symlink_exit; + } + } + + if (Size > 4 * sizeof(WCHAR) && + '\\' == TargetLink[0] && + '?' == TargetLink[1] && + '?' == TargetLink[2] && + '\\' == TargetLink[3]) + { + /* absolute symlink; replace whole path */ + if (Size > *PSize) + return STATUS_OBJECT_NAME_INVALID; + memcpy(TargetPath, TargetLink, Size); + p = TargetPath + Size / sizeof(WCHAR); + + /* absolute symlinks on Windows are in NT namespace; send to FSD for further processing */ + goto exit; + } + else + { + /* relative symlink; replace last path component seen */ + if (Size + sizeof(WCHAR) > (TargetPathEnd - lastp) * sizeof(WCHAR)) + return STATUS_OBJECT_NAME_INVALID; + memcpy(lastp, TargetLink, Size); + lastp[Size / sizeof(WCHAR)] = L'\0'; + p = lastp; + } + } + +exit: + *PSize = (PUINT8)p - (PUINT8)TargetPath; + PIoStatus->Status = STATUS_REPARSE; + PIoStatus->Information = 0/*IO_REPARSE*/; + return STATUS_REPARSE; + +no_symlink_exit: + *PSize = Size; + PIoStatus->Status = STATUS_REPARSE; + PIoStatus->Information = ReparseData->ReparseTag; + return STATUS_REPARSE; +} diff --git a/src/dll/fuse/fuse_intf.c b/src/dll/fuse/fuse_intf.c index 21708d47..31658d80 100644 --- a/src/dll/fuse/fuse_intf.c +++ b/src/dll/fuse/fuse_intf.c @@ -17,14 +17,6 @@ #include -/* - * FSP_FUSE_REPARSE_OPTHACK - * - * Define this macro to enable the ResolveReparsePoints optimization hack. - * See fsp_fuse_intf_GetSecurityByName for details. - */ -#define FSP_FUSE_REPARSE_OPTHACK - NTSTATUS fsp_fuse_op_enter(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) { @@ -314,8 +306,8 @@ exit: return Result; } -static char *fsp_fuse_intf_GetSymlinkPointer(FSP_FILE_SYSTEM *FileSystem, - char *PosixPath) +static BOOLEAN fsp_fuse_intf_FindReparsePoint(FSP_FILE_SYSTEM *FileSystem, + char *PosixPath, PUINT32 PReparsePointIndex) { struct fuse *f = FileSystem->UserContext; char *p, *lastp; @@ -323,7 +315,7 @@ static char *fsp_fuse_intf_GetSymlinkPointer(FSP_FILE_SYSTEM *FileSystem, int err; if (0 == f->ops.getattr) - return 0; + return FALSE; p = PosixPath; for (;;) @@ -334,7 +326,7 @@ static char *fsp_fuse_intf_GetSymlinkPointer(FSP_FILE_SYSTEM *FileSystem, while ('/' != *p) { if ('\0' == *p) - return 0; + return FALSE; p++; } @@ -344,12 +336,17 @@ static char *fsp_fuse_intf_GetSymlinkPointer(FSP_FILE_SYSTEM *FileSystem, *p = '/'; if (0 != err) - return 0; + return FALSE; else if (0120000 == (stbuf.st_mode & 0170000)) - return lastp; + { + if (0 != PReparsePointIndex) + *PReparsePointIndex = (ULONG)(lastp - PosixPath); + + return TRUE; + } } - return 0; + return FALSE; } static NTSTATUS fsp_fuse_intf_GetVolumeInfo(FSP_FILE_SYSTEM *FileSystem, @@ -393,7 +390,7 @@ static NTSTATUS fsp_fuse_intf_GetSecurityByName(FSP_FILE_SYSTEM *FileSystem, PSECURITY_DESCRIPTOR SecurityDescriptorBuf, SIZE_T *PSecurityDescriptorSize) { struct fuse *f = FileSystem->UserContext; - char *PosixPath = 0, *PosixSymlinkPointer; + char *PosixPath = 0; NTSTATUS Result; Result = FspPosixMapWindowsToPosixPath(FileName, &PosixPath); @@ -406,28 +403,8 @@ static NTSTATUS fsp_fuse_intf_GetSecurityByName(FSP_FILE_SYSTEM *FileSystem, goto exit; if (FSP_FUSE_HAS_SYMLINKS(f) && - 0 != (PosixSymlinkPointer = fsp_fuse_intf_GetSymlinkPointer(FileSystem, PosixPath))) - { -#if defined(FSP_FUSE_REPARSE_OPTHACK) - /* OPTHACK: This is a rather gross hack for optimization purposes only. - * - * If the FileName in this GetSecurityByName call and the PosixPath we computed - * matches the one in the FSD request, then remember where the first symlink is - * for use in ResolveReparsePoints later on. This avoids going through every path - * component twice (doing both a getattr and readlink). - */ - - struct fuse_context *context = fsp_fuse_get_context(f->env); - struct fsp_fuse_context_header *contexthdr = FSP_FUSE_HDR_FROM_CONTEXT(context); - - if (FspFsctlTransactCreateKind == contexthdr->Request->Kind && - (PUINT8)FileName == contexthdr->Request->Buffer && - 0 == lstrcmpA(PosixPath, contexthdr->PosixPath)) - contexthdr->SymlinkIndex = PosixSymlinkPointer - PosixPath; -#endif - + fsp_fuse_intf_FindReparsePoint(FileSystem, PosixPath, PFileAttributes)) Result = STATUS_REPARSE; - } else Result = STATUS_SUCCESS; @@ -1510,7 +1487,7 @@ exit: } static NTSTATUS fsp_fuse_intf_ResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, - PWSTR FileName, BOOLEAN OpenReparsePoint, + PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN OpenReparsePoint, PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize) { struct fuse *f = FileSystem->UserContext; @@ -1532,8 +1509,7 @@ static NTSTATUS fsp_fuse_intf_ResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, return STATUS_OBJECT_NAME_INVALID; memcpy(PosixTargetPath, contexthdr->PosixPath, Length + 1); - p = PosixTargetPath + - contexthdr->SymlinkIndex; /* NOTE: if FSP_FUSE_REPARSE_OPTHACK is undefined, this will be 0 */ + p = PosixTargetPath + ReparsePointIndex; c = *p; for (;;) { @@ -1581,7 +1557,7 @@ static NTSTATUS fsp_fuse_intf_ResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, c = *p; *p = '\0'; - err = f->ops.readlink(contexthdr->PosixPath, PosixTargetLink, sizeof PosixTargetLink); + err = f->ops.readlink(PosixTargetPath, PosixTargetLink, sizeof PosixTargetLink); *p = c; if (EINVAL/* same on MSVC and Cygwin */ == err) @@ -1598,10 +1574,7 @@ static NTSTATUS fsp_fuse_intf_ResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, { /* we do not support absolute paths without the rellinks option */ if (!f->rellinks) - { - Result = STATUS_OBJECT_NAME_NOT_FOUND; - goto exit; - } + return STATUS_OBJECT_NAME_NOT_FOUND; /* absolute symlink; replace whole path */ memcpy(PosixTargetPath, PosixTargetLink, Length + 1); diff --git a/src/dll/fuse/library.h b/src/dll/fuse/library.h index fef7b635..7b0084cb 100644 --- a/src/dll/fuse/library.h +++ b/src/dll/fuse/library.h @@ -54,7 +54,6 @@ struct fsp_fuse_context_header FSP_FSCTL_TRANSACT_REQ *Request; FSP_FSCTL_TRANSACT_RSP *Response; char *PosixPath; - ptrdiff_t SymlinkIndex; __declspec(align(MEMORY_ALLOCATION_ALIGNMENT)) UINT8 ContextBuf[]; }; diff --git a/src/dll/security.c b/src/dll/security.c index d20d6ff4..faaa3175 100644 --- a/src/dll/security.c +++ b/src/dll/security.c @@ -48,6 +48,19 @@ static NTSTATUS FspGetSecurityByName(FSP_FILE_SYSTEM *FileSystem, } } +static inline ULONG FspPathSuffixIndex(PWSTR FileName) +{ + WCHAR Root[2] = L"\\"; + PWSTR Remain, Suffix; + ULONG Result; + + FspPathSuffix(FileName, &Remain, &Suffix, Root); + Result = Remain == Root ? 0 : (ULONG)(Suffix - Remain); + FspPathCombine(FileName, Suffix); + + return Result; +} + FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, BOOLEAN CheckParentDirectory, BOOLEAN AllowTraverseCheck, @@ -76,7 +89,7 @@ FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem, NTSTATUS Result; WCHAR Root[2] = L"\\", TraverseCheckRoot[2] = L"\\"; PWSTR FileName, Suffix, Prefix, Remain; - UINT32 FileAttributes; + UINT32 FileAttributes = 0; PSECURITY_DESCRIPTOR SecurityDescriptor = 0; SIZE_T SecurityDescriptorSize; UINT8 PrivilegeSetBuf[sizeof(PRIVILEGE_SET) + 15 * sizeof(LUID_AND_ATTRIBUTES)]; @@ -111,9 +124,15 @@ FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem, break; } + FileAttributes = 0; Result = FspGetSecurityByName(FileSystem, Prefix, &FileAttributes, &SecurityDescriptor, &SecurityDescriptorSize); + /* compute the ReparsePointIndex and place it in FileAttributes now */ + if (NT_SUCCESS(Result) && STATUS_REPARSE != Result && + (FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) + FileAttributes = FspPathSuffixIndex(Prefix); + FspPathCombine(FileName, Remain); if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result) @@ -139,6 +158,7 @@ FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem, */ if (FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + /* ReparsePointIndex already computed after FspGetSecurityByName call above */ Result = STATUS_REPARSE; goto exit; } @@ -165,6 +185,7 @@ FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem, } } + FileAttributes = 0; Result = FspGetSecurityByName(FileSystem, FileName, &FileAttributes, &SecurityDescriptor, &SecurityDescriptorSize); if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result) @@ -237,6 +258,7 @@ FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem, */ if (FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + FileAttributes = FspPathSuffixIndex(FileName); Result = STATUS_REPARSE; goto exit; } @@ -256,6 +278,7 @@ FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem, if (0 != (FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && 0 == (Request->Req.Create.CreateOptions & FILE_OPEN_REPARSE_POINT)) { + FileAttributes = FspPathSuffixIndex(FileName); Result = STATUS_REPARSE; goto exit; } @@ -307,7 +330,9 @@ FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem, Result = STATUS_SUCCESS; exit: - if (0 != PSecurityDescriptor && 0 < SecurityDescriptorSize && NT_SUCCESS(Result)) + if (STATUS_REPARSE == Result) + *PGrantedAccess = FileAttributes; /* FileAttributes contains ReparsePointIndex */ + else if (0 != PSecurityDescriptor && 0 < SecurityDescriptorSize && NT_SUCCESS(Result)) *PSecurityDescriptor = SecurityDescriptor; else MemFree(SecurityDescriptor);