1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: lib/rtl/env.c 5 * PURPOSE: Environment functions 6 * PROGRAMMER: Eric Kohl 7 */ 8 9 /* INCLUDES ******************************************************************/ 10 11 #include <rtl.h> 12 13 #define NDEBUG 14 #include <debug.h> 15 16 /* FUNCTIONS *****************************************************************/ 17 18 NTSTATUS 19 NTAPI 20 RtlSetEnvironmentStrings(IN PWCHAR NewEnvironment, IN ULONG NewEnvironmentSize) 21 { 22 UNIMPLEMENTED; 23 return STATUS_NOT_IMPLEMENTED; 24 } 25 26 /* 27 * @implemented 28 */ 29 NTSTATUS 30 NTAPI 31 RtlCreateEnvironment( 32 BOOLEAN Inherit, 33 PWSTR *OutEnvironment) 34 { 35 MEMORY_BASIC_INFORMATION MemInfo; 36 PVOID CurrentEnvironment, NewEnvironment = NULL; 37 NTSTATUS Status = STATUS_SUCCESS; 38 SIZE_T RegionSize = PAGE_SIZE; 39 40 /* Check if we should inherit the current environment */ 41 if (Inherit) 42 { 43 /* In this case we need to lock the PEB */ 44 RtlAcquirePebLock(); 45 46 /* Get a pointer to the current Environment and check if it's not NULL */ 47 CurrentEnvironment = NtCurrentPeb()->ProcessParameters->Environment; 48 if (CurrentEnvironment != NULL) 49 { 50 /* Query the size of the current environment allocation */ 51 Status = NtQueryVirtualMemory(NtCurrentProcess(), 52 CurrentEnvironment, 53 MemoryBasicInformation, 54 &MemInfo, 55 sizeof(MEMORY_BASIC_INFORMATION), 56 NULL); 57 if (!NT_SUCCESS(Status)) 58 { 59 RtlReleasePebLock(); 60 *OutEnvironment = NULL; 61 return Status; 62 } 63 64 /* Allocate a new region of the same size */ 65 RegionSize = MemInfo.RegionSize; 66 Status = NtAllocateVirtualMemory(NtCurrentProcess(), 67 &NewEnvironment, 68 0, 69 &RegionSize, 70 MEM_RESERVE | MEM_COMMIT, 71 PAGE_READWRITE); 72 if (!NT_SUCCESS(Status)) 73 { 74 RtlReleasePebLock(); 75 *OutEnvironment = NULL; 76 return Status; 77 } 78 79 /* Copy the current environment */ 80 RtlCopyMemory(NewEnvironment, 81 CurrentEnvironment, 82 MemInfo.RegionSize); 83 } 84 85 /* We are done with the PEB, release the lock */ 86 RtlReleasePebLock (); 87 } 88 89 /* Check if we still need an environment */ 90 if (NewEnvironment == NULL) 91 { 92 /* Allocate a new environment */ 93 Status = NtAllocateVirtualMemory(NtCurrentProcess(), 94 &NewEnvironment, 95 0, 96 &RegionSize, 97 MEM_RESERVE | MEM_COMMIT, 98 PAGE_READWRITE); 99 if (NT_SUCCESS(Status)) 100 { 101 RtlZeroMemory(NewEnvironment, RegionSize); 102 } 103 } 104 105 *OutEnvironment = NewEnvironment; 106 107 return Status; 108 } 109 110 111 /* 112 * @implemented 113 */ 114 VOID NTAPI 115 RtlDestroyEnvironment(PWSTR Environment) 116 { 117 SIZE_T Size = 0; 118 119 NtFreeVirtualMemory(NtCurrentProcess(), 120 (PVOID)&Environment, 121 &Size, 122 MEM_RELEASE); 123 } 124 125 126 /* 127 * @implemented 128 */ 129 NTSTATUS NTAPI 130 RtlExpandEnvironmentStrings_U(PWSTR Environment, 131 PUNICODE_STRING Source, 132 PUNICODE_STRING Destination, 133 PULONG Length) 134 { 135 UNICODE_STRING Variable; 136 UNICODE_STRING Value; 137 NTSTATUS ReturnStatus = STATUS_SUCCESS; 138 NTSTATUS Status; 139 PWSTR SourceBuffer; 140 PWSTR DestBuffer; 141 PWSTR CopyBuffer; 142 PWSTR VariableEnd; 143 ULONG SourceLength; 144 ULONG DestMax; 145 ULONG CopyLength; 146 ULONG Tail; 147 ULONG TotalLength = 1; /* for terminating NULL */ 148 149 DPRINT("RtlExpandEnvironmentStrings_U %p %wZ %p %p\n", 150 Environment, Source, Destination, Length); 151 152 SourceLength = Source->Length / sizeof(WCHAR); 153 SourceBuffer = Source->Buffer; 154 DestMax = Destination->MaximumLength / sizeof(WCHAR); 155 DestBuffer = Destination->Buffer; 156 157 while (SourceLength) 158 { 159 if (*SourceBuffer != L'%') 160 { 161 CopyBuffer = SourceBuffer; 162 CopyLength = 0; 163 while (SourceLength != 0 && *SourceBuffer != L'%') 164 { 165 SourceBuffer++; 166 CopyLength++; 167 SourceLength--; 168 } 169 } 170 else 171 { 172 /* Process environment variable. */ 173 174 VariableEnd = SourceBuffer + 1; 175 Tail = SourceLength - 1; 176 while (*VariableEnd != L'%' && Tail != 0) 177 { 178 VariableEnd++; 179 Tail--; 180 } 181 182 if (Tail != 0) 183 { 184 Variable.MaximumLength = 185 Variable.Length = (USHORT)(VariableEnd - (SourceBuffer + 1)) * sizeof(WCHAR); 186 Variable.Buffer = SourceBuffer + 1; 187 188 Value.Length = 0; 189 Value.MaximumLength = (USHORT)DestMax * sizeof(WCHAR); 190 Value.Buffer = DestBuffer; 191 192 Status = RtlQueryEnvironmentVariable_U(Environment, &Variable, 193 &Value); 194 if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_TOO_SMALL) 195 { 196 SourceBuffer = VariableEnd + 1; 197 SourceLength = Tail - 1; 198 TotalLength += Value.Length / sizeof(WCHAR); 199 if (Status != STATUS_BUFFER_TOO_SMALL) 200 { 201 DestBuffer += Value.Length / sizeof(WCHAR); 202 DestMax -= Value.Length / sizeof(WCHAR); 203 } 204 else 205 { 206 DestMax = 0; 207 ReturnStatus = STATUS_BUFFER_TOO_SMALL; 208 } 209 continue; 210 } 211 else 212 { 213 /* Variable not found. */ 214 CopyBuffer = SourceBuffer; 215 CopyLength = SourceLength - Tail + 1; 216 SourceLength -= CopyLength; 217 SourceBuffer += CopyLength; 218 } 219 } 220 else 221 { 222 /* Unfinished variable name. */ 223 CopyBuffer = SourceBuffer; 224 CopyLength = SourceLength; 225 SourceLength = 0; 226 } 227 } 228 229 TotalLength += CopyLength; 230 if (DestMax) 231 { 232 if (DestMax < CopyLength) 233 { 234 CopyLength = DestMax; 235 ReturnStatus = STATUS_BUFFER_TOO_SMALL; 236 } 237 RtlCopyMemory(DestBuffer, CopyBuffer, CopyLength * sizeof(WCHAR)); 238 DestMax -= CopyLength; 239 DestBuffer += CopyLength; 240 } 241 } 242 243 /* NULL-terminate the buffer. */ 244 if (DestMax) 245 *DestBuffer = 0; 246 else 247 ReturnStatus = STATUS_BUFFER_TOO_SMALL; 248 249 Destination->Length = (USHORT)(DestBuffer - Destination->Buffer) * sizeof(WCHAR); 250 if (Length != NULL) 251 *Length = TotalLength * sizeof(WCHAR); 252 253 DPRINT("Destination %wZ\n", Destination); 254 255 return ReturnStatus; 256 } 257 258 259 /* 260 * @implemented 261 */ 262 VOID NTAPI 263 RtlSetCurrentEnvironment(PWSTR NewEnvironment, 264 PWSTR *OldEnvironment) 265 { 266 PVOID EnvPtr; 267 268 DPRINT("NewEnvironment 0x%p OldEnvironment 0x%p\n", 269 NewEnvironment, OldEnvironment); 270 271 RtlAcquirePebLock(); 272 273 EnvPtr = NtCurrentPeb()->ProcessParameters->Environment; 274 NtCurrentPeb()->ProcessParameters->Environment = NewEnvironment; 275 276 if (OldEnvironment != NULL) 277 *OldEnvironment = EnvPtr; 278 279 RtlReleasePebLock(); 280 } 281 282 283 /* 284 * @implemented 285 */ 286 NTSTATUS NTAPI 287 RtlSetEnvironmentVariable(PWSTR *Environment, 288 PUNICODE_STRING Name, 289 PUNICODE_STRING Value) 290 { 291 MEMORY_BASIC_INFORMATION mbi; 292 UNICODE_STRING var; 293 size_t hole_len, new_len, env_len = 0; 294 WCHAR *new_env = 0, *env_end = 0, *wcs, *env, *val = 0, *tail = 0, *hole = 0; 295 PWSTR head = NULL; 296 SIZE_T size = 0, new_size; 297 LONG f = 1; 298 NTSTATUS Status = STATUS_SUCCESS; 299 300 DPRINT("RtlSetEnvironmentVariable(Environment %p Name %wZ Value %wZ)\n", 301 Environment, Name, Value); 302 303 /* Variable name must not be empty */ 304 if (Name->Length < sizeof(WCHAR)) 305 return STATUS_INVALID_PARAMETER; 306 307 /* Variable names can't contain a '=' except as a first character. */ 308 for (wcs = Name->Buffer + 1; 309 wcs < Name->Buffer + (Name->Length / sizeof(WCHAR)); 310 wcs++) 311 { 312 if (*wcs == L'=') 313 return STATUS_INVALID_PARAMETER; 314 } 315 316 if (Environment) 317 { 318 env = *Environment; 319 } 320 else 321 { 322 RtlAcquirePebLock(); 323 env = NtCurrentPeb()->ProcessParameters->Environment; 324 } 325 326 if (env) 327 { 328 /* get environment length */ 329 wcs = env_end = env; 330 do 331 { 332 env_end += wcslen(env_end) + 1; 333 } 334 while (*env_end); 335 env_end++; 336 env_len = env_end - env; 337 DPRINT("environment length %lu characters\n", env_len); 338 339 /* find where to insert */ 340 while (*wcs) 341 { 342 var.Buffer = wcs++; 343 wcs = wcschr(wcs, L'='); 344 if (wcs == NULL) 345 { 346 wcs = var.Buffer + wcslen(var.Buffer); 347 } 348 if (*wcs) 349 { 350 var.Length = (USHORT)(wcs - var.Buffer) * sizeof(WCHAR); 351 var.MaximumLength = var.Length; 352 val = ++wcs; 353 wcs += wcslen(wcs); 354 f = RtlCompareUnicodeString(&var, Name, TRUE); 355 if (f >= 0) 356 { 357 if (f) /* Insert before found */ 358 { 359 hole = tail = var.Buffer; 360 } 361 else /* Exact match */ 362 { 363 head = var.Buffer; 364 tail = ++wcs; 365 hole = val; 366 } 367 goto found; 368 } 369 } 370 wcs++; 371 } 372 hole = tail = wcs; /* Append to environment */ 373 } 374 375 found: 376 if (Value != NULL && Value->Buffer != NULL) 377 { 378 hole_len = tail - hole; 379 /* calculate new environment size */ 380 new_size = Value->Length + sizeof(WCHAR); 381 /* adding new variable */ 382 if (f) 383 new_size += Name->Length + sizeof(WCHAR); 384 new_len = new_size / sizeof(WCHAR); 385 if (hole_len < new_len) 386 { 387 /* enlarge environment size */ 388 /* check the size of available memory */ 389 new_size += (env_len - hole_len) * sizeof(WCHAR); 390 new_size = ROUND_UP(new_size, PAGE_SIZE); 391 mbi.RegionSize = 0; 392 DPRINT("new_size %lu\n", new_size); 393 394 if (env) 395 { 396 Status = NtQueryVirtualMemory(NtCurrentProcess(), 397 env, 398 MemoryBasicInformation, 399 &mbi, 400 sizeof(MEMORY_BASIC_INFORMATION), 401 NULL); 402 if (!NT_SUCCESS(Status)) 403 { 404 if (Environment == NULL) 405 { 406 RtlReleasePebLock(); 407 } 408 return(Status); 409 } 410 } 411 412 if (new_size > mbi.RegionSize) 413 { 414 /* reallocate memory area */ 415 Status = NtAllocateVirtualMemory(NtCurrentProcess(), 416 (PVOID)&new_env, 417 0, 418 &new_size, 419 MEM_RESERVE | MEM_COMMIT, 420 PAGE_READWRITE); 421 if (!NT_SUCCESS(Status)) 422 { 423 if (Environment == NULL) 424 { 425 RtlReleasePebLock(); 426 } 427 return(Status); 428 } 429 430 if (env) 431 { 432 memmove(new_env, 433 env, 434 (hole - env) * sizeof(WCHAR)); 435 hole = new_env + (hole - env); 436 } 437 else 438 { 439 /* absolutely new environment */ 440 tail = hole = new_env; 441 *hole = 0; 442 env_end = hole + 1; 443 } 444 } 445 } 446 447 /* move tail */ 448 memmove (hole + new_len, tail, (env_end - tail) * sizeof(WCHAR)); 449 450 if (new_env) 451 { 452 /* we reallocated environment, let's free the old one */ 453 if (Environment) 454 *Environment = new_env; 455 else 456 NtCurrentPeb()->ProcessParameters->Environment = new_env; 457 458 if (env) 459 { 460 size = 0; 461 NtFreeVirtualMemory(NtCurrentProcess(), 462 (PVOID)&env, 463 &size, 464 MEM_RELEASE); 465 } 466 } 467 468 /* and now copy given stuff */ 469 if (f) 470 { 471 /* copy variable name and '=' character */ 472 memmove(hole, 473 Name->Buffer, 474 Name->Length); 475 hole += Name->Length / sizeof(WCHAR); 476 *hole++ = L'='; 477 } 478 479 /* copy value */ 480 memmove(hole, 481 Value->Buffer, 482 Value->Length); 483 hole += Value->Length / sizeof(WCHAR); 484 *hole = 0; 485 } 486 else 487 { 488 /* remove the environment variable */ 489 if (f == 0) 490 { 491 memmove(head, 492 tail, 493 (env_end - tail) * sizeof(WCHAR)); 494 } 495 } 496 497 if (Environment == NULL) 498 { 499 RtlReleasePebLock(); 500 } 501 502 return(Status); 503 } 504 505 506 /* 507 * @implemented 508 */ 509 NTSTATUS NTAPI 510 RtlQueryEnvironmentVariable_U(PWSTR Environment, 511 PCUNICODE_STRING Name, 512 PUNICODE_STRING Value) 513 { 514 NTSTATUS Status; 515 PWSTR wcs; 516 UNICODE_STRING var; 517 PWSTR val; 518 BOOLEAN SysEnvUsed = FALSE; 519 520 DPRINT("RtlQueryEnvironmentVariable_U Environment %p Variable %wZ Value %p\n", 521 Environment, Name, Value); 522 523 if (Environment == NULL) 524 { 525 PPEB Peb = RtlGetCurrentPeb(); 526 if (Peb) { 527 RtlAcquirePebLock(); 528 Environment = Peb->ProcessParameters->Environment; 529 SysEnvUsed = TRUE; 530 } 531 } 532 533 if (Environment == NULL) 534 { 535 if (SysEnvUsed) 536 RtlReleasePebLock(); 537 return(STATUS_VARIABLE_NOT_FOUND); 538 } 539 540 Value->Length = 0; 541 542 wcs = Environment; 543 DPRINT("Starting search at :%p\n", wcs); 544 while (*wcs) 545 { 546 var.Buffer = wcs++; 547 wcs = wcschr(wcs, L'='); 548 if (wcs == NULL) 549 { 550 wcs = var.Buffer + wcslen(var.Buffer); 551 DPRINT("Search at :%S\n", wcs); 552 } 553 if (*wcs) 554 { 555 var.Length = var.MaximumLength = (USHORT)(wcs - var.Buffer) * sizeof(WCHAR); 556 val = ++wcs; 557 wcs += wcslen(wcs); 558 DPRINT("Search at :%S\n", wcs); 559 560 if (RtlEqualUnicodeString(&var, Name, TRUE)) 561 { 562 Value->Length = (USHORT)(wcs - val) * sizeof(WCHAR); 563 if (Value->Length <= Value->MaximumLength) 564 { 565 memcpy(Value->Buffer, val, 566 min(Value->Length + sizeof(WCHAR), Value->MaximumLength)); 567 DPRINT("Value %S\n", val); 568 DPRINT("Return STATUS_SUCCESS\n"); 569 Status = STATUS_SUCCESS; 570 } 571 else 572 { 573 DPRINT("Return STATUS_BUFFER_TOO_SMALL\n"); 574 Status = STATUS_BUFFER_TOO_SMALL; 575 } 576 577 if (SysEnvUsed) 578 RtlReleasePebLock(); 579 580 return(Status); 581 } 582 } 583 wcs++; 584 } 585 586 if (SysEnvUsed) 587 RtlReleasePebLock(); 588 589 DPRINT("Return STATUS_VARIABLE_NOT_FOUND: %wZ\n", Name); 590 return(STATUS_VARIABLE_NOT_FOUND); 591 } 592 593 /* EOF */ 594