1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/*++
6
7
8
9Module Name:
10
11 directory.c
12
13Abstract:
14
15 Implementation of the file WIN API for the PAL
16
17Revision History:
18
19
20
21--*/
22
23#include "pal/palinternal.h"
24#include "pal/dbgmsg.h"
25#include "pal/file.h"
26#include "pal/stackstring.hpp"
27
28#include <stdlib.h>
29#include <sys/param.h>
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <errno.h>
33
34SET_DEFAULT_DEBUG_CHANNEL(FILE);
35
36
37
38/*++
39Function:
40 CreateDirectoryW
41
42Note:
43 lpSecurityAttributes always NULL.
44
45See MSDN doc.
46--*/
47BOOL
48PALAPI
49CreateDirectoryW(
50 IN LPCWSTR lpPathName,
51 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes)
52{
53 BOOL bRet = FALSE;
54 DWORD dwLastError = 0;
55 int mb_size;
56 char *mb_dir = NULL;
57
58 PERF_ENTRY(CreateDirectoryW);
59 ENTRY("CreateDirectoryW(lpPathName=%p (%S), lpSecurityAttr=%p)\n",
60 lpPathName?lpPathName:W16_NULLSTRING,
61 lpPathName?lpPathName:W16_NULLSTRING, lpSecurityAttributes);
62
63 if ( lpSecurityAttributes )
64 {
65 ASSERT("lpSecurityAttributes is not NULL as it should be\n");
66 dwLastError = ERROR_INVALID_PARAMETER;
67 goto done;
68 }
69
70 /* translate the wide char lpPathName string to multibyte string */
71 if(0 == (mb_size = WideCharToMultiByte( CP_ACP, 0, lpPathName, -1, NULL, 0,
72 NULL, NULL )))
73 {
74 ASSERT("WideCharToMultiByte failure! error is %d\n", GetLastError());
75 dwLastError = ERROR_INTERNAL_ERROR;
76 goto done;
77 }
78
79 if (((mb_dir = (char *)PAL_malloc(mb_size)) == NULL) ||
80 (WideCharToMultiByte( CP_ACP, 0, lpPathName, -1, mb_dir, mb_size, NULL,
81 NULL) != mb_size))
82 {
83 ASSERT("WideCharToMultiByte or PAL_malloc failure! LastError:%d errno:%d\n",
84 GetLastError(), errno);
85 dwLastError = ERROR_INTERNAL_ERROR;
86 goto done;
87 }
88
89 bRet = CreateDirectoryA(mb_dir,NULL);
90done:
91 if( dwLastError )
92 {
93 SetLastError( dwLastError );
94 }
95 if (mb_dir != NULL)
96 {
97 PAL_free(mb_dir);
98 }
99 LOGEXIT("CreateDirectoryW returns BOOL %d\n", bRet);
100 PERF_EXIT(CreateDirectoryW);
101 return bRet;
102}
103
104/*++
105Routine Name:
106
107 RemoveDirectoryHelper
108
109Routine Description:
110
111 Core function which removes a directory. Called by RemoveDirectory[AW]
112
113Parameters:
114
115 LPSTR lpPathName - [in/out]
116 The directory name to remove. It is converted in place to a unix path.
117
118Return Value:
119
120 BOOL -
121 TRUE <=> successful
122
123--*/
124
125static
126BOOL
127RemoveDirectoryHelper (
128 PathCharString& lpPathName,
129 LPDWORD dwLastError
130)
131{
132 BOOL bRet = FALSE;
133 *dwLastError = 0;
134
135 FILEDosToUnixPathA( lpPathName );
136
137 if ( rmdir(lpPathName) != 0 )
138 {
139 TRACE("Removal of directory [%s] was unsuccessful, errno = %d.\n",
140 lpPathName.GetString(), errno);
141
142 switch( errno )
143 {
144 case ENOTDIR:
145 /* FALL THROUGH */
146 case ENOENT:
147 {
148 struct stat stat_data;
149
150 if ( stat( lpPathName, &stat_data) == 0 &&
151 (stat_data.st_mode & S_IFMT) == S_IFREG )
152 {
153 /* Not a directory, it is a file. */
154 *dwLastError = ERROR_DIRECTORY;
155 }
156 else
157 {
158 FILEGetProperNotFoundError( lpPathName, dwLastError );
159 }
160 break;
161 }
162 case ENOTEMPTY:
163 *dwLastError = ERROR_DIR_NOT_EMPTY;
164 break;
165 default:
166 *dwLastError = ERROR_ACCESS_DENIED;
167 }
168 }
169 else {
170 TRACE("Removal of directory [%s] was successful.\n", lpPathName.GetString());
171 bRet = TRUE;
172 }
173
174 return bRet;
175}
176
177/*++
178Function:
179 RemoveDirectoryA
180
181See MSDN doc.
182--*/
183BOOL
184PALAPI
185RemoveDirectoryA(
186 IN LPCSTR lpPathName)
187{
188 DWORD dwLastError = 0;
189 BOOL bRet = FALSE;
190 PathCharString mb_dirPathString;
191
192 PERF_ENTRY(RemoveDirectoryA);
193 ENTRY("RemoveDirectoryA(lpPathName=%p (%s))\n",
194 lpPathName,
195 lpPathName);
196
197 if (lpPathName == NULL)
198 {
199 dwLastError = ERROR_PATH_NOT_FOUND;
200 goto done;
201 }
202
203 if (!mb_dirPathString.Set(lpPathName, strlen(lpPathName)))
204 {
205 WARN("Set failed !\n");
206 dwLastError = ERROR_NOT_ENOUGH_MEMORY;
207 goto done;
208 }
209
210 bRet = RemoveDirectoryHelper (mb_dirPathString, &dwLastError);
211
212done:
213 if( dwLastError )
214 {
215 SetLastError( dwLastError );
216 }
217
218 LOGEXIT("RemoveDirectoryA returns BOOL %d\n", bRet);
219 PERF_EXIT(RemoveDirectoryA);
220 return bRet;
221}
222
223/*++
224Function:
225 RemoveDirectoryW
226
227See MSDN doc.
228--*/
229BOOL
230PALAPI
231RemoveDirectoryW(
232 IN LPCWSTR lpPathName)
233{
234 PathCharString mb_dirPathString;
235 int mb_size;
236 DWORD dwLastError = 0;
237 BOOL bRet = FALSE;
238 size_t length;
239 char * mb_dir = NULL;
240
241 PERF_ENTRY(RemoveDirectoryW);
242 ENTRY("RemoveDirectoryW(lpPathName=%p (%S))\n",
243 lpPathName?lpPathName:W16_NULLSTRING,
244 lpPathName?lpPathName:W16_NULLSTRING);
245
246 if (lpPathName == NULL)
247 {
248 dwLastError = ERROR_PATH_NOT_FOUND;
249 goto done;
250 }
251
252 length = (PAL_wcslen(lpPathName)+1) * 3;
253 mb_dir = mb_dirPathString.OpenStringBuffer(length);
254 if (NULL == mb_dir)
255 {
256 dwLastError = ERROR_NOT_ENOUGH_MEMORY;
257 goto done;
258 }
259
260 mb_size = WideCharToMultiByte( CP_ACP, 0, lpPathName, -1, mb_dir, length,
261 NULL, NULL );
262
263 if( mb_size == 0 )
264 {
265 mb_dirPathString.CloseBuffer(0);
266 ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
267 dwLastError = ERROR_INTERNAL_ERROR;
268 goto done;
269 }
270
271 mb_dirPathString.CloseBuffer(mb_size - 1);
272
273 if ((bRet = RemoveDirectoryHelper (mb_dirPathString, &dwLastError)))
274 {
275 TRACE("Removal of directory [%s] was successful.\n", mb_dir);
276 }
277
278done:
279 if( dwLastError )
280 {
281 SetLastError( dwLastError );
282 }
283
284 LOGEXIT("RemoveDirectoryW returns BOOL %d\n", bRet);
285 PERF_EXIT(RemoveDirectoryW);
286 return bRet;
287}
288
289
290/*++
291Function:
292 GetCurrentDirectoryA
293
294--*/
295DWORD
296GetCurrentDirectoryA(PathCharString& lpBuffer)
297{
298 DWORD dwDirLen = 0;
299 DWORD dwLastError = 0;
300
301 char *current_dir;
302
303 PERF_ENTRY(GetCurrentDirectoryA);
304 ENTRY("GetCurrentDirectoryA(lpBuffer=%p)\n", lpBuffer.GetString());
305
306 current_dir = lpBuffer.OpenStringBuffer(MAX_PATH);
307 /* NULL first arg means getcwd will allocate the string */
308 current_dir = PAL__getcwd( current_dir, MAX_PATH);
309
310 if (current_dir != NULL )
311 {
312 dwDirLen = strlen( current_dir );
313 lpBuffer.CloseBuffer(dwDirLen);
314 goto done;
315 }
316 else if ( errno == ERANGE )
317 {
318 lpBuffer.CloseBuffer(0);
319 current_dir = PAL__getcwd( NULL, 0);
320 }
321
322 if ( !current_dir )
323 {
324 WARN("Getcwd failed with errno=%d [%s]\n", errno, strerror(errno));
325 dwLastError = DIRGetLastErrorFromErrno();
326 dwDirLen = 0;
327 goto done;
328 }
329
330 dwDirLen = strlen( current_dir );
331 lpBuffer.Set(current_dir, dwDirLen);
332 PAL_free(current_dir);
333done:
334
335 if ( dwLastError )
336 {
337 SetLastError(dwLastError);
338 }
339
340 LOGEXIT("GetCurrentDirectoryA returns DWORD %u\n", dwDirLen);
341 PERF_EXIT(GetCurrentDirectoryA);
342 return dwDirLen;
343}
344
345/*++
346Function:
347 GetCurrentDirectoryA
348
349See MSDN doc.
350--*/
351DWORD
352PALAPI
353GetCurrentDirectoryA(
354 IN DWORD nBufferLength,
355 OUT LPSTR lpBuffer)
356{
357
358 PathCharString lpBufferString;
359 DWORD dwDirLen = GetCurrentDirectoryA(lpBufferString);
360
361 /* if the supplied buffer isn't long enough, return the required
362 length, including room for the NULL terminator */
363 if ( nBufferLength <= dwDirLen )
364 {
365 ++dwDirLen; /* include space for the NULL */
366 }
367 else
368 {
369 strcpy_s( lpBuffer, nBufferLength, lpBufferString );
370 }
371
372 return dwDirLen;
373}
374
375/*++
376Function:
377 GetCurrentDirectoryW
378
379See MSDN doc.
380--*/
381DWORD
382PALAPI
383GetCurrentDirectoryW(
384 IN DWORD nBufferLength,
385 OUT LPWSTR lpBuffer)
386{
387 DWORD dwWideLen = 0;
388 DWORD dwLastError = ERROR_BAD_PATHNAME;
389 int dir_len;
390 PathCharString current_dir;
391
392 PERF_ENTRY(GetCurrentDirectoryW);
393 ENTRY("GetCurrentDirectoryW(nBufferLength=%u, lpBuffer=%p)\n",
394 nBufferLength, lpBuffer);
395
396
397 dir_len = GetCurrentDirectoryA(current_dir);
398
399 if( dir_len == 0)
400 {
401 dwLastError = DIRGetLastErrorFromErrno();
402 goto done;
403 }
404
405 dwWideLen = MultiByteToWideChar( CP_ACP, 0,
406 current_dir, dir_len,
407 NULL, 0 );
408
409 /* if the supplied buffer isn't long enough, return the required
410 length, including room for the NULL terminator */
411 if ( nBufferLength > dwWideLen )
412 {
413 if(!MultiByteToWideChar( CP_ACP, 0, current_dir, dir_len + 1,
414 lpBuffer, nBufferLength ))
415 {
416 ASSERT("MultiByteToWideChar failure!\n");
417 dwWideLen = 0;
418 dwLastError = ERROR_INTERNAL_ERROR;
419 }
420 }
421 else
422 {
423 ++dwWideLen; /* include the space for the NULL */
424 }
425
426done:
427
428 if ( dwLastError )
429 {
430 SetLastError(dwLastError);
431 }
432
433 LOGEXIT("GetCurrentDirectoryW returns DWORD %u\n", dwWideLen);
434 PERF_EXIT(GetCurrentDirectoryW);
435 return dwWideLen;
436}
437
438
439/*++
440Function:
441 SetCurrentDirectoryW
442
443See MSDN doc.
444--*/
445BOOL
446PALAPI
447SetCurrentDirectoryW(
448 IN LPCWSTR lpPathName)
449{
450 BOOL bRet;
451 DWORD dwLastError = 0;
452 PathCharString dirPathString;
453 int size;
454 size_t length;
455 char * dir = NULL;
456
457 PERF_ENTRY(SetCurrentDirectoryW);
458 ENTRY("SetCurrentDirectoryW(lpPathName=%p (%S))\n",
459 lpPathName?lpPathName:W16_NULLSTRING,
460 lpPathName?lpPathName:W16_NULLSTRING);
461
462 /*check if the given path is null. If so
463 return FALSE*/
464 if (lpPathName == NULL )
465 {
466 ERROR("Invalid path/directory name\n");
467 dwLastError = ERROR_INVALID_NAME;
468 bRet = FALSE;
469 goto done;
470 }
471
472 length = (PAL_wcslen(lpPathName)+1) * 3;
473 dir = dirPathString.OpenStringBuffer(length);
474 if (NULL == dir)
475 {
476 dwLastError = ERROR_NOT_ENOUGH_MEMORY;
477 bRet = FALSE;
478 goto done;
479 }
480
481 size = WideCharToMultiByte( CP_ACP, 0, lpPathName, -1, dir, length,
482 NULL, NULL );
483
484 if( size == 0 )
485 {
486 dirPathString.CloseBuffer(0);
487 dwLastError = GetLastError();
488 ASSERT("WideCharToMultiByte failure! error is %d\n", dwLastError);
489 dwLastError = ERROR_INTERNAL_ERROR;
490 bRet = FALSE;
491 goto done;
492 }
493
494 dirPathString.CloseBuffer(size - 1);
495 bRet = SetCurrentDirectoryA(dir);
496done:
497 if( dwLastError )
498 {
499 SetLastError(dwLastError);
500 }
501
502 LOGEXIT("SetCurrentDirectoryW returns BOOL %d\n", bRet);
503 PERF_EXIT(SetCurrentDirectoryW);
504 return bRet;
505}
506
507/*++
508Function:
509 CreateDirectoryA
510
511Note:
512 lpSecurityAttributes always NULL.
513
514See MSDN doc.
515--*/
516BOOL
517PALAPI
518CreateDirectoryA(
519 IN LPCSTR lpPathName,
520 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes)
521{
522 BOOL bRet = FALSE;
523 DWORD dwLastError = 0;
524 PathCharString realPath;
525 char* realPathBuf;
526 LPSTR unixPathName = NULL;
527 int pathLength;
528 int i;
529 const int mode = S_IRWXU | S_IRWXG | S_IRWXO;
530
531 PERF_ENTRY(CreateDirectoryA);
532 ENTRY("CreateDirectoryA(lpPathName=%p (%s), lpSecurityAttr=%p)\n",
533 lpPathName?lpPathName:"NULL",
534 lpPathName?lpPathName:"NULL", lpSecurityAttributes);
535
536 if ( lpSecurityAttributes )
537 {
538 ASSERT("lpSecurityAttributes is not NULL as it should be\n");
539 dwLastError = ERROR_INVALID_PARAMETER;
540 goto done;
541 }
542
543 // Windows returns ERROR_PATH_NOT_FOUND when called with NULL.
544 // If we don't have this check, strdup(NULL) segfaults.
545 if (lpPathName == NULL)
546 {
547 ERROR("CreateDirectoryA called with NULL pathname!\n");
548 dwLastError = ERROR_PATH_NOT_FOUND;
549 goto done;
550 }
551
552 unixPathName = PAL__strdup(lpPathName);
553 if (unixPathName == NULL )
554 {
555 ERROR("PAL__strdup() failed\n");
556 dwLastError = ERROR_NOT_ENOUGH_MEMORY;
557 goto done;
558 }
559 FILEDosToUnixPathA( unixPathName );
560 // Remove any trailing slashes at the end because mkdir might not
561 // handle them appropriately on all platforms.
562 pathLength = strlen(unixPathName);
563 i = pathLength;
564 while(i > 1)
565 {
566 if(unixPathName[i - 1] =='/')
567 {
568 unixPathName[i - 1]='\0';
569 i--;
570 }
571 else
572 {
573 break;
574 }
575 }
576
577
578 // Get an absolute path.
579 if (unixPathName[0] == '/')
580 {
581 realPathBuf = unixPathName;
582 }
583 else
584 {
585
586 DWORD len = GetCurrentDirectoryA(realPath);
587 if (len == 0 || !realPath.Reserve(realPath.GetCount() + pathLength + 1 ))
588 {
589 dwLastError = DIRGetLastErrorFromErrno();
590 WARN("Getcwd failed with errno=%d \n", dwLastError);
591 goto done;
592 }
593
594 realPath.Append("/", 1);
595 realPath.Append(unixPathName, pathLength);
596 realPathBuf = realPath.OpenStringBuffer(realPath.GetCount());
597 }
598
599 // Canonicalize the path so we can determine its length.
600 FILECanonicalizePath(realPathBuf);
601
602 if ( mkdir(realPathBuf, mode) != 0 )
603 {
604 TRACE("Creation of directory [%s] was unsuccessful, errno = %d.\n",
605 unixPathName, errno);
606
607 switch( errno )
608 {
609 case ENOTDIR:
610 /* FALL THROUGH */
611 case ENOENT:
612 FILEGetProperNotFoundError( realPathBuf, &dwLastError );
613 goto done;
614 case EEXIST:
615 dwLastError = ERROR_ALREADY_EXISTS;
616 break;
617 default:
618 dwLastError = ERROR_ACCESS_DENIED;
619 }
620 }
621 else
622 {
623 TRACE("Creation of directory [%s] was successful.\n", unixPathName);
624 bRet = TRUE;
625 }
626
627 realPath.CloseBuffer(0); //The PathCharString usage is done
628done:
629 if( dwLastError )
630 {
631 SetLastError( dwLastError );
632 }
633 PAL_free( unixPathName );
634 LOGEXIT("CreateDirectoryA returns BOOL %d\n", bRet);
635 PERF_EXIT(CreateDirectoryA);
636 return bRet;
637}
638
639/*++
640Function:
641 SetCurrentDirectoryA
642
643See MSDN doc.
644--*/
645BOOL
646PALAPI
647SetCurrentDirectoryA(
648 IN LPCSTR lpPathName)
649{
650 BOOL bRet = FALSE;
651 DWORD dwLastError = 0;
652 int result;
653 LPSTR unixPathName = NULL;
654
655 PERF_ENTRY(SetCurrentDirectoryA);
656 ENTRY("SetCurrentDirectoryA(lpPathName=%p (%s))\n",
657 lpPathName?lpPathName:"NULL",
658 lpPathName?lpPathName:"NULL");
659
660 /*check if the given path is null. If so
661 return FALSE*/
662 if (lpPathName == NULL )
663 {
664 ERROR("Invalid path/directory name\n");
665 dwLastError = ERROR_INVALID_NAME;
666 goto done;
667 }
668
669 unixPathName = PAL__strdup(lpPathName);
670 if (unixPathName == NULL )
671 {
672 ERROR("PAL__strdup() failed\n");
673 dwLastError = ERROR_NOT_ENOUGH_MEMORY;
674 goto done;
675 }
676 FILEDosToUnixPathA( unixPathName );
677
678 TRACE("Attempting to open Unix dir [%s]\n", unixPathName);
679 result = chdir(unixPathName);
680
681 if ( result == 0 )
682 {
683 bRet = TRUE;
684 }
685 else
686 {
687 if ( errno == ENOTDIR || errno == ENOENT )
688 {
689 struct stat stat_data;
690
691 if ( stat( unixPathName, &stat_data) == 0 &&
692 (stat_data.st_mode & S_IFMT) == S_IFREG )
693 {
694 /* Not a directory, it is a file. */
695 dwLastError = ERROR_DIRECTORY;
696 }
697 else
698 {
699 FILEGetProperNotFoundError( unixPathName, &dwLastError );
700 }
701 TRACE("chdir() failed, path was invalid.\n");
702 }
703 else
704 {
705 dwLastError = ERROR_ACCESS_DENIED;
706 ERROR("chdir() failed; errno is %d (%s)\n", errno, strerror(errno));
707 }
708 }
709
710
711done:
712 if( dwLastError )
713 {
714 SetLastError(dwLastError);
715 }
716
717 if(unixPathName != NULL)
718 {
719 PAL_free( unixPathName );
720 }
721
722 LOGEXIT("SetCurrentDirectoryA returns BOOL %d\n", bRet);
723 PERF_EXIT(SetCurrentDirectoryA);
724 return bRet;
725}
726