xref: /reactos/sdk/lib/rtl/env.c (revision 8a978a17)
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