[BTRFS] Upgrade to 1.4
[reactos.git] / drivers / filesystems / btrfs / boot.c
1 /* Copyright (c) Mark Harmstone 2019
2 *
3 * This file is part of WinBtrfs.
4 *
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
9 *
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
17
18 #include "btrfs_drv.h"
19
20 #ifndef __REACTOS__
21 #ifdef _MSC_VER
22 #include <ntstrsafe.h>
23 #endif
24 #else
25 #include <ntstrsafe.h>
26 #endif
27
28 extern ERESOURCE pdo_list_lock;
29 extern LIST_ENTRY pdo_list;
30
31 #ifndef _MSC_VER
32 NTSTATUS RtlUnicodeStringPrintf(PUNICODE_STRING DestinationString, const WCHAR* pszFormat, ...); // not in mingw
33 #endif
34
35 static bool get_system_root_partition(uint32_t* disk_num, uint32_t* partition_num) {
36 NTSTATUS Status;
37 HANDLE h;
38 UNICODE_STRING us, target;
39 OBJECT_ATTRIBUTES objatt;
40 WCHAR* s;
41 ULONG retlen = 0, left;
42
43 static const WCHAR system_root[] = L"\\SystemRoot";
44 static const WCHAR arc_prefix[] = L"\\ArcName\\multi(0)disk(0)rdisk(";
45 static const WCHAR arc_middle[] = L")partition(";
46
47 us.Buffer = (WCHAR*)system_root;
48 us.Length = us.MaximumLength = sizeof(system_root) - sizeof(WCHAR);
49
50 InitializeObjectAttributes(&objatt, &us, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
51
52 Status = ZwOpenSymbolicLinkObject(&h, GENERIC_READ, &objatt);
53 if (!NT_SUCCESS(Status)) {
54 ERR("ZwOpenSymbolicLinkObject returned %08x\n", Status);
55 return false;
56 }
57
58 target.Length = target.MaximumLength = 0;
59
60 Status = ZwQuerySymbolicLinkObject(h, &target, &retlen);
61 if (Status != STATUS_BUFFER_TOO_SMALL) {
62 ERR("ZwQuerySymbolicLinkObject returned %08x\n", Status);
63 NtClose(h);
64 return false;
65 }
66
67 if (retlen == 0) {
68 NtClose(h);
69 return false;
70 }
71
72 target.Buffer = ExAllocatePoolWithTag(NonPagedPool, retlen, ALLOC_TAG);
73 if (!target.Buffer) {
74 ERR("out of memory\n");
75 NtClose(h);
76 return false;
77 }
78
79 target.Length = target.MaximumLength = (USHORT)retlen;
80
81 Status = ZwQuerySymbolicLinkObject(h, &target, NULL);
82 if (!NT_SUCCESS(Status)) {
83 ERR("ZwQuerySymbolicLinkObject returned %08x\n", Status);
84 NtClose(h);
85 ExFreePool(target.Buffer);
86 return false;
87 }
88
89 NtClose(h);
90
91 TRACE("system root is %.*S\n", target.Length / sizeof(WCHAR), target.Buffer);
92
93 if (target.Length <= sizeof(arc_prefix) - sizeof(WCHAR) ||
94 RtlCompareMemory(target.Buffer, arc_prefix, sizeof(arc_prefix) - sizeof(WCHAR)) != sizeof(arc_prefix) - sizeof(WCHAR)) {
95 ExFreePool(target.Buffer);
96 return false;
97 }
98
99 s = &target.Buffer[(sizeof(arc_prefix) / sizeof(WCHAR)) - 1];
100 left = ((target.Length - sizeof(arc_prefix)) / sizeof(WCHAR)) + 1;
101
102 if (left == 0 || s[0] < '0' || s[0] > '9') {
103 ExFreePool(target.Buffer);
104 return false;
105 }
106
107 *disk_num = 0;
108
109 while (left > 0 && s[0] >= '0' && s[0] <= '9') {
110 *disk_num *= 10;
111 *disk_num += s[0] - '0';
112 s++;
113 left--;
114 }
115
116 if (left <= (sizeof(arc_middle) / sizeof(WCHAR)) - 1 ||
117 RtlCompareMemory(s, arc_middle, sizeof(arc_middle) - sizeof(WCHAR)) != sizeof(arc_middle) - sizeof(WCHAR)) {
118 ExFreePool(target.Buffer);
119 return false;
120 }
121
122 s = &s[(sizeof(arc_middle) / sizeof(WCHAR)) - 1];
123 left -= (sizeof(arc_middle) / sizeof(WCHAR)) - 1;
124
125 if (left == 0 || s[0] < '0' || s[0] > '9') {
126 ExFreePool(target.Buffer);
127 return false;
128 }
129
130 *partition_num = 0;
131
132 while (left > 0 && s[0] >= '0' && s[0] <= '9') {
133 *partition_num *= 10;
134 *partition_num += s[0] - '0';
135 s++;
136 left--;
137 }
138
139 ExFreePool(target.Buffer);
140
141 return true;
142 }
143
144 static void change_symlink(uint32_t disk_num, uint32_t partition_num, BTRFS_UUID* uuid) {
145 NTSTATUS Status;
146 UNICODE_STRING us, us2;
147 WCHAR symlink[60], target[(sizeof(BTRFS_VOLUME_PREFIX) / sizeof(WCHAR)) + 36], *w;
148 #ifdef __REACTOS__
149 unsigned int i;
150 #endif
151
152 us.Buffer = symlink;
153 us.Length = 0;
154 us.MaximumLength = sizeof(symlink);
155
156 Status = RtlUnicodeStringPrintf(&us, L"\\Device\\Harddisk%u\\Partition%u", disk_num, partition_num);
157 if (!NT_SUCCESS(Status)) {
158 ERR("RtlUnicodeStringPrintf returned %08x\n", Status);
159 return;
160 }
161
162 Status = IoDeleteSymbolicLink(&us);
163 if (!NT_SUCCESS(Status))
164 ERR("IoDeleteSymbolicLink returned %08x\n", Status);
165
166 RtlCopyMemory(target, BTRFS_VOLUME_PREFIX, sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR));
167
168 w = &target[(sizeof(BTRFS_VOLUME_PREFIX) / sizeof(WCHAR)) - 1];
169
170 #ifndef __REACTOS__
171 for (unsigned int i = 0; i < 16; i++) {
172 #else
173 for (i = 0; i < 16; i++) {
174 #endif
175 *w = hex_digit(uuid->uuid[i] >> 4); w++;
176 *w = hex_digit(uuid->uuid[i] & 0xf); w++;
177
178 if (i == 3 || i == 5 || i == 7 || i == 9) {
179 *w = L'-';
180 w++;
181 }
182 }
183
184 *w = L'}';
185
186 us2.Buffer = target;
187 us2.Length = us2.MaximumLength = sizeof(target);
188
189 Status = IoCreateSymbolicLink(&us, &us2);
190 if (!NT_SUCCESS(Status))
191 ERR("IoCreateSymbolicLink returned %08x\n", Status);
192 }
193
194 /* If booting from Btrfs, Windows will pass the device object for the raw partition to
195 * mount_vol - which is no good to us, as we only use the \Device\Btrfs{} devices we
196 * create so that RAID works correctly.
197 * At the time check_system_root gets called, \SystemRoot is a symlink to the ARC device,
198 * e.g. \ArcName\multi(0)disk(0)rdisk(0)partition(1)\Windows. We can't change the symlink,
199 * as it gets clobbered by IopReassignSystemRoot shortly afterwards, and we can't touch
200 * the \ArcName symlinks as they haven't been created yet. Instead, we need to change the
201 * symlink \Device\HarddiskX\PartitionY, which is what the ArcName symlink will shortly
202 * point to.
203 */
204 void __stdcall check_system_root(PDRIVER_OBJECT DriverObject, PVOID Context, ULONG Count) {
205 uint32_t disk_num, partition_num;
206 LIST_ENTRY* le;
207 bool done = false;
208
209 TRACE("(%p, %p, %u)\n", DriverObject, Context, Count);
210
211 if (!get_system_root_partition(&disk_num, &partition_num))
212 return;
213
214 TRACE("system boot partition is disk %u, partition %u\n", disk_num, partition_num);
215
216 ExAcquireResourceSharedLite(&pdo_list_lock, true);
217
218 le = pdo_list.Flink;
219 while (le != &pdo_list) {
220 LIST_ENTRY* le2;
221 pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry);
222
223 ExAcquireResourceSharedLite(&pdode->child_lock, true);
224
225 le2 = pdode->children.Flink;
226
227 while (le2 != &pdode->children) {
228 volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry);
229
230 if (vc->disk_num == disk_num && vc->part_num == partition_num) {
231 change_symlink(disk_num, partition_num, &pdode->uuid);
232 done = true;
233 break;
234 }
235
236 le2 = le2->Flink;
237 }
238
239 ExReleaseResourceLite(&pdode->child_lock);
240
241 if (done)
242 break;
243
244 le = le->Flink;
245 }
246
247 ExReleaseResourceLite(&pdo_list_lock);
248 }