Goal: To create symbolic link using DeviceIoControl with FSCTL_SET_REPARSE_POINT
I know there is CreateSymbolicLink WinApi call, it works, but I cannot use it for my purpose.
Problem:
I keep getting the 0x80071128 (The data present in the reparse point buffer is invalid) when I call DeviceIOControl(hNewSymLink,FSCTL_SET_REPARSE_POINT ...
the 'c:' drive is NTFS, OS is Windows 10, I am debugging as Admin.
Initially I tried to put together the REPARSE_DATA_BUFFER, as per https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/b41f1cbf-10df-4a47-98d4-1c52a833d913 to the above effect.
In the test3() I read the structure of good symlink and try to use it to create new one, but the structure gets rejected, again with the same error.
void test3()
{
using (SafeFileHandle hSymLink = NativeMethods.CreateFile(
#"c:\Temp\link2",
FileAccess.Read,
FileShare.Read,
IntPtr.Zero,
FileMode.Open,
(FileAttributes)(NativeMethods.EFileAttributes.FILE_FLAG_OPEN_REPARSE_POINT),
IntPtr.Zero))
{
var reparseDataSize = Marshal.SizeOf(typeof(NativeMethods.REPARSE_DATA_BUFFER));
var reparseData = Marshal.AllocHGlobal(reparseDataSize);
try
{
int bytesReturned = 0;
var result = NativeMethods.DeviceIoControl(hSymLink, NativeMethods.DeviceIOControlCode.FSCTL_GET_REPARSE_POINT,
IntPtr.Zero, 0,
reparseData, reparseDataSize,
ref bytesReturned, IntPtr.Zero);
if (!result)
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
var reparseDataBuffer = (NativeMethods.REPARSE_DATA_BUFFER)
Marshal.PtrToStructure(reparseData, typeof(NativeMethods.REPARSE_DATA_BUFFER));
var printNameDir = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer,
reparseDataBuffer.PrintNameOffset, reparseDataBuffer.PrintNameLength);
//value: "c:\\temp\\target.txt"
var substituteNameDir = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer,
reparseDataBuffer.SubstituteNameOffset, reparseDataBuffer.SubstituteNameLength);
//value: "\\??\\c:\\temp\\target.txt"
using (SafeFileHandle hNewSymLink = NativeMethods.CreateFile(
#"c:\Temp\linkNew",
FileAccess.Write,
FileShare.Read,
IntPtr.Zero,
FileMode.Create,
(FileAttributes)(NativeMethods.EFileAttributes.FILE_FLAG_OPEN_REPARSE_POINT),
IntPtr.Zero))
{
var result2 = NativeMethods.DeviceIoControl(hNewSymLink, NativeMethods.DeviceIOControlCode.FSCTL_SET_REPARSE_POINT,
reparseData, reparseDataSize, IntPtr.Zero, 0, IntPtr.Zero, IntPtr.Zero);
if (!result2)
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
//error 0x80071128: wrong reparse buffer
}
}
finally
{
Marshal.FreeHGlobal(reparseData);
}
}
}
When I inspect the structure it looks fine.
The docs state the same REPARSE_DATA_BUFFER is used for get and set, so I expect to be able to use well-formed structure retrieved from existing symlink (created with MKLINK system command) to create another one.
Obviously I am missing something - any hints would be much appreciated.
One needs to pass exact size of the structure, not the size of entire allocated buffer. Thanks #RbMm
The call should look like
var result2 = NativeMethods.DeviceIoControl(hNewSymLink,
NativeMethods.DeviceIOControlCode.FSCTL_SET_REPARSE_POINT,
reparseData, bytesReturned, IntPtr.Zero, 0, IntPtr.Zero, IntPtr.Zero);
fix the disk drive using the command-
Open Windows PowerShell as admin
Run "chkdsk /f"
Drive will be lock schedule it for next restart in next step
Related
I am trying to create a program that any normal user can run on windows and generate a process list of all processes, including the executable location. I have used CreateToolhelp32Snapshot() to get all process names, pid, ppid. But having issues getting the image path. Everything I do results in pretty much Access Denied.
I have tried ZwQueryInformationProcess, GetProcessImageFileName, etc. and also using OpenProcess to get the handle to each process. I can get the handle by using PROCESS_QUERY_LIMITED_INFORMATION, but any other option doesn't work. I am lost and have been at this for a few days. Can anyone point me in the right direction?
This is the code that works for non-admin user on Windows. Use the szExeFile member of PROCESSENTRY32 to get the path:
HANDLE hProcessSnap = NULL;
HANDLE hProcess = NULL;
PROCESSENTRY32 pe32;
DWORD dwPriorityClass = 0;
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
return;
}
// Set the size of the structure before using it.
pe32.dwSize = sizeof(PROCESSENTRY32);
// Retrieve information about the first process,
// and exit if unsuccessful
if (!Process32First(hProcessSnap, &pe32))
{
CloseHandle(hProcessSnap); // clean the snapshot object
return;
}
// Now walk the snapshot of processes, and
// display information about each process in turn
do
{
// do something with the pe32 struct.
// pe32.szExeFile -> path of the file
} while (Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
I am aware that GetFinalPathNameByHandle can be used to obtain the target of a symbolic link or a reparse point, but there are situations where its use is not desirable:
If the target is not available, doesn't exist or cannot be opened, CreateFile on a symlink fails, and thus the path cannot be obtained.
If I point a symlink "a" to file "b" and create a symlink "b" to file "c", the function follows the whole chain, returning "c".
The function is not useful much when I already have the handle to the actual symlink at hand.
It seems that DeviceIoControl can be used together with FSCTL_GET_REPARSE_POINT to obtain the actual reparse data of the file, but that gets me the REPARSE_DATA_BUFFER, and I would have to parse that.
I don't know how the system actually processes reparse points, but I think that the target location is a piece of information that should be available at some point. The dir command, for example, can display the target path correctly for any reparse point... well I have already seen it handle just symlinks and mount points (junctions).
how the system actually processes reparse points
this is done inside file system and file system filter drivers. result depend from are FILE_FLAG_OPEN_REPARSE_POINT option used in call CreateFile (or FILE_OPEN_REPARSE_POINT in NT calls).
when FILE_FLAG_OPEN_REPARSE_POINT is specified - file system bypass normal reparse point processing for the file and attempts to directly open the reparse point file as is.
If the FILE_OPEN_REPARSE_POINT flag is not specified - file-system attempts to open a file to which reparse point is point (if fs understand format of reparse point - primary only Microsoft reparse points)
the data format saved in reparse point is REPARSE_DATA_BUFFER (Microsoft reparse point format) or REPARSE_GUID_DATA_BUFFER - need look for ReparseTag at begin.
to determine whether a reparse point tag corresponds to a tag owned by Microsoft we use IsReparseTagMicrosoft macro.
code for test/print reparse point data:
volatile UCHAR guz;
ULONG TestReparsePoint(PCWSTR FileName)
{
HANDLE hFile = CreateFile(FileName, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
union {
PVOID pv;
PULONG ReparseTag;
PREPARSE_DATA_BUFFER prdb;
PREPARSE_GUID_DATA_BUFFER prgdb;
};
PVOID stack = alloca(guz);
ULONG cb = 0, rcb = sizeof(REPARSE_DATA_BUFFER) + 0x100, BytesReturned;
ULONG dwError;
do
{
if (cb < rcb) cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
if (DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, 0, 0, pv, cb, &BytesReturned, 0))
{
dwError = NOERROR;
if (IsReparseTagMicrosoft(*ReparseTag))
{
char cc[16];
LPCSTR name;
switch (*ReparseTag)
{
case IO_REPARSE_TAG_SYMLINK:
name = " SYMLINK";
stack = prdb->SymbolicLinkReparseBuffer.PathBuffer;
break;
case IO_REPARSE_TAG_MOUNT_POINT:
name = " MOUNT_POINT";
stack = prdb->MountPointReparseBuffer.PathBuffer;
break;
default:
sprintf(cc, " %08x", prdb->ReparseTag);
name = cc;
}
DbgPrint(" %s->%.*S <%.*S>\n", name,
prdb->MountPointReparseBuffer.SubstituteNameLength >> 1,
RtlOffsetToPointer(stack, prdb->MountPointReparseBuffer.SubstituteNameOffset),
prdb->MountPointReparseBuffer.PrintNameLength >> 1,
RtlOffsetToPointer(stack, prdb->MountPointReparseBuffer.PrintNameOffset)
);
}
else
{
PGUID g = &prgdb->ReparseGuid;
DbgPrint(" tag=%x {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x} size=%x\n", *ReparseTag,
g->Data1, g->Data2, g->Data3,
g->Data4[0],g->Data4[1],g->Data4[2],g->Data4[3],g->Data4[4],g->Data4[5],g->Data4[6],g->Data4[7],
prgdb->ReparseDataLength);
}
break;
}
rcb = IsReparseTagMicrosoft(*ReparseTag)
? REPARSE_DATA_BUFFER_HEADER_SIZE + prdb->ReparseDataLength
: REPARSE_GUID_DATA_BUFFER_HEADER_SIZE + prgdb->ReparseDataLength;
} while((dwError = GetLastError()) == ERROR_MORE_DATA);
CloseHandle(hFile);
return dwError;
}
Microsoft reparse points can be read with REPARSE_DATA_BUFFER instead. The MS open protocol specification might also be useful.
Parsing other GUID based tags can only be done if you know the format.
I deleted my other question. Thanks all for helping me realize how to post.
I have a file on my desktop named rawr.txt. The file is locked. I would like to open it for the sole reason of getting a filehandle to it. (i will search a list of enumerated filed handles to identify what process id's are locking this file).
This is my code:
Cu.import("resource://gre/modules/ctypes.jsm");
var lib_kernel32 = ctypes.open("kernel32.dll");
//var INVALID_HANDLE_VALUE = ctypes.voidptr_t(-1);
var GENERIC_READ = 0x80000000;
var GENERIC_WRITE = 0x40000000;
var OPEN_EXISTING = 3;
var FILE_ATTRIBUTE_NORMAL = 0x80;
var FILE_FLAG_OVERLAPPED = 0x40000000;
var OPEN_ALWAYS = 4;
var INVALID_HANDLE_VALUE = new ctypes.Int64(-1);
var FSCTL_SET_SPARSE = 0x900c4;
var FSCTL_SET_ZERO_DATA = 0x980c8;
var FILE_BEGIN = 0;
let CreateFile = lib_kernel32.declare(
"CreateFileW",
ctypes.winapi_abi,
ctypes.voidptr_t, // return type: handle to the file
ctypes.jschar.ptr, // in: lpFileName
ctypes.uint32_t, // in: dwDesiredAccess
ctypes.uint32_t, // in: dwShareMode
ctypes.voidptr_t, // in, optional: lpSecurityAttributes (note that
// we're cheating here by not declaring a
// SECURITY_ATTRIBUTES structure -- that's because
// we're going to pass in null anyway)
ctypes.uint32_t, // in: dwCreationDisposition
ctypes.uint32_t, // in: dwFlagsAndAttributes
ctypes.voidptr_t // in, optional: hTemplateFile
);
let CloseHandle = lib_kernel32.declare(
"CloseHandle",
ctypes.winapi_abi,
ctypes.int32_t, //bool // return type: 1 indicates success, 0 failure
ctypes.voidptr_t // in: hObject
);
var aFile = FileUtils.getFile('Desk', ['rawr.txt']);
let filePath = aFile.path;
let hFile = CreateFile(filePath, GENERIC_READ, 0, null, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, null);
let hFileInt = ctypes.cast(hFile, ctypes.intptr_t);
if (ctypes.Int64.compare(hFileInt.value, INVALID_HANDLE_VALUE) == 0) {
throw new Error("CreateFile failed for " + filePath + ", error " +
ctypes.winLastError);
}
CloseHandle(hFile);
lib_kernel32.close();
Problem with this is I always get some exception on the throw new Error line. I get error 32 most commonly and sometimes 87 when experimenting with flags.
Thanks
Since all you want is a handle to the file, you shouldn't be using GENERIC_READ. That requires that the other process opened the file with FILE_SHARE_READ.
Also, you need to allow other processes to have the file open, by specifying FILE_SHARE_READ, FILE_SHARE_WRITE, and FILE_SHARE_DELETE:
CreateFile(filepath, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
...)
... and after all this, you've got a handle to the file with no access, which is pretty much useless. (The handle you get has no relationship to the handles of other processes, so having a handle yourself does not help you search a list of existing handles in any way I can see.)
let hFile = CreateFile(filePath, GENERIC_READ, 0, ...)
Passing 0 for the dwShareMode argument is not going to get you anywhere. That asks for exclusive access to the file, you cannot get that because another process already obtained read or write access. Usually GENERIC_WRITE access in the case of a log file. You'll need FILE_SHARE_READ | FILE_SHARE_WRITE to ask humbly.
If that still doesn't work then the other process was adamant about you not messing with the file. It intentionally omitted FILE_SHARE_READ. Not very common for a text file, but done when the programmer deems it impossible to read the file correctly because he is constantly changing it. You cannot override that decision, other than by picking up the phone and calling the programmer.
I am a new member and joined this site after referring to it loads of times when i was stuck with some programming problems. I am trying to code a media player (Win32 SDK VC++ 6.0) for my college project and I am stuck. I have searched on various forums and msdn and finally landed on the function GetShortPathName which enables me to play through folders and files which have a whitespace in their names. I will paste the code here so it will be much more clearer as to what i am trying to do.
case IDM_FILE_OPEN :
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hwnd;
ofn.lpstrFilter = "Media Files (All Supported Types)\0*.avi;*.mpg;*.mpeg;*.asf;*.wmv;*.mp2;*.mp3\0"
"Movie File (*.avi;*.mpg;*.mpeg)\0*.avi;*.mpg;*.mpeg\0"
"Windows Media File (*.asf;*.wmv)\0*.asf;*.wmv\0"
"Audio File (*.mp2;*.mp3)\0*.mp2;*.mp3\0"
"All Files(*.*)\0*.*\0";
ofn.lpstrFile = szFileName;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_ALLOWMULTISELECT | OFN_CREATEPROMPT;
ofn.lpstrDefExt = "mp3";
if(GetOpenFileName(&ofn))
{
length = GetShortPathName(szFileName, NULL, 0);
buffer = (TCHAR *) malloc (sizeof(length));
length = GetShortPathName(szFileName, buffer, length);
for(i = 0 ; i < MAX_PATH ; i++)
{
if(buffer[i] == '\\')
buffer[i] = '/';
}
SendMessage(hList,LB_ADDSTRING,0,(LPARAM)buffer);
mciSendString("open buffer alias myFile", NULL, 0, NULL);
mciSendString("play buffer", NULL, 0, NULL);
}
return 0;
using the GetShortPathName function i get the path as : D:/Mp3z/DEEPBL~1/03SLEE~1.mp3
Putting this path directly in Play button case
mciSendString("open D:/Mp3jh/DEEPBL~1/03SLEE~1.mp3 alias myFile", NULL, 0, NULL);
mciSendString("play myFile", NULL, 0, NULL);
the file opens and plays fine. But as soon as i try to open and play it through the open file dialog box, nothing happens. Any input appreciated.
It looks like the problem is that you're passing the name of the buffer variable to the mciSendString function as a string, rather than passing the contents of the buffer.
You need to concatenate the arguments you want to pass (open and alias myFile) with the contents of buffer.
The code can also be much simplified by replacing malloc with an automatic array. You don't need to malloc it because you don't need it outside of the block scope. (And you shouldn't be using malloc in C++ code anyway; use new[] instead.)
Here's a modified snippet of the code shown in your question:
(Warning: changes made using only my eyes as a compiler! Handle with care.)
if(GetOpenFileName(&ofn))
{
// Get the short path name, and place it in the buffer array.
// We know that a short path won't be any longer than MAX_PATH, so we can
// simply allocate a statically-sized array without futzing with new[].
//
// Note: In production code, you should probably check the return value
// of the GetShortPathName function to make sure it succeeded.
TCHAR buffer[MAX_PATH];
GetShortPathName(szFileName, buffer, MAX_PATH);
// Add the short path name to your ListBox control.
//
// Note: In C++ code, you should probably use C++-style casts like
// reinterpret_cast, rather than C-style casts!
SendMessage(hList, LB_ADDSTRING, 0, reinterpret_cast<LPARAM>(buffer));
// Build the argument string to pass to the mciSendString function.
//
// Note: In production code, you probably want to use the more secure
// alternatives to the string concatenation functions.
// See the documentation for more details.
// And, as before, you should probably check return values for error codes.
TCHAR arguments[MAX_PATH * 2]; // this will definitely be large enough
lstrcat(arguments, TEXT("open"));
lstrcat(arguments, buffer);
lstrcat(arguments, TEXT("alias myFile"));
// Or, better yet, use a string formatting function, like StringCbPrintf:
// StringCbPrintf(arguments, MAX_PATH * 2, TEXT("open %s alias myFile"),
// buffer);
// Call the mciSendString function with the argument string we just built.
mciSendString(arguments, NULL, 0, NULL);
mciSendString("play myFile", NULL, 0, NULL);
}
Do note that, as the above code shows, working with C-style strings (character arrays) is a real pain in the ass. C++ provides a better alternative, in the form of the std::string class. You should strongly consider using that instead. To call Windows API functions, you'll still need a C-style string, but you can get one of those by using the c_str method of the std::string class.
In short, I want to write a Gnome-Shell-style window switcher. So I need to fetch snapshots of all the windows. My current program looks like this:
char filename[101];
sprintf(filename, "%d.png", (int)win_list[i]);
GdkWindow *win_gdk = gdk_x11_window_foreign_new_for_display
(gdk_display_get_default(), win_list[i]);
gint _w, _h;
gdk_drawable_get_size(GDK_DRAWABLE(win_gdk), &_w, &_h);
XEvent _xevent;
_xevent.xexpose =
(XExposeEvent)
{
.type = Expose,
.send_event = True,
.display = xsu_vars.dpy,
.window = win_list[i],
.x = 0, .y = 0, .width = _w, .height = _h,
.count = 0
};
XSendEvent(xsu_vars.dpy, win_list[i], False, 0, &_xevent);
GdkPixbuf *_pb = gdk_pixbuf_get_from_drawable(
NULL, GDK_DRAWABLE(win_gdk), NULL, 0, 0, 0, 0, _w, _h);
if(_pb != NULL) {
cairo_surface_t *_surf_cairo = cairo_image_surface_create(
CAIRO_FORMAT_RGB24, _w, _h);
cairo_t *_cr = cairo_create(_surf_cairo);
gdk_cairo_set_source_pixbuf(_cr, _pb, 0, 0);
cairo_paint(_cr);
cairo_surface_write_to_png(_surf_cairo, filename);
printf("%s saved successfully!\n", filename);
} else {
printf("failed...\n");
}
The program works well well, but it will not generate correct images for those windows which are on a different desktop of minimized -- they would look like this:
Note that I send a expose event to all windows before generating pixbufs of them.
UPDATE:
It seems that xlib doesn't support that. So the only way may be creating cache manually.
This is possible with Composite extension - see "Preventing the backing pixmap from being freed when the window is hidden/destroyed" section in tutorial.
Yes, your update is correct. When a window is unmapped (or covered up), X just discards its contents; they don't exist anywhere in order to be snapshotted.
I believe libwnck contains code to do this and other parts of writing a switcher, btw. It's basically a library for writing things like window switchers.