VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/posix/path-posix.cpp@ 78253

Last change on this file since 78253 was 78178, checked in by vboxsync, 6 years ago

IPRT: Relaxing RTPATH_MAX in a couple of places. bugref:9172

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 14.5 KB
Line 
1/* $Id: path-posix.cpp 78178 2019-04-17 18:32:29Z vboxsync $ */
2/** @file
3 * IPRT - Path Manipulation, POSIX, Part 1.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_PATH
32#include <stdlib.h>
33#include <limits.h>
34#include <errno.h>
35#include <unistd.h>
36#include <sys/stat.h>
37#include <sys/time.h>
38#include <stdio.h>
39#include <sys/types.h>
40#include <pwd.h>
41
42#include <iprt/path.h>
43#include <iprt/env.h>
44#include <iprt/assert.h>
45#include <iprt/mem.h>
46#include <iprt/string.h>
47#include <iprt/err.h>
48#include <iprt/log.h>
49#include "internal/path.h"
50#include "internal/process.h"
51#include "internal/fs.h"
52
53
54
55RTDECL(int) RTPathReal(const char *pszPath, char *pszRealPath, size_t cchRealPath)
56{
57 /*
58 * Convert input.
59 */
60 char const *pszNativePath;
61 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
62 if (RT_SUCCESS(rc))
63 {
64 /*
65 * On POSIX platforms the API doesn't take a length parameter, which makes it
66 * a little bit more work.
67 */
68 char szTmpPath[PATH_MAX + 1];
69 const char *psz = realpath(pszNativePath, szTmpPath);
70 if (psz)
71 rc = rtPathFromNativeCopy(pszRealPath, cchRealPath, szTmpPath, NULL);
72 else
73 rc = RTErrConvertFromErrno(errno);
74 rtPathFreeNative(pszNativePath, pszPath);
75 }
76
77 LogFlow(("RTPathReal(%p:{%s}, %p:{%s}, %u): returns %Rrc\n", pszPath, pszPath,
78 pszRealPath, RT_SUCCESS(rc) ? pszRealPath : "<failed>", cchRealPath, rc));
79 return rc;
80}
81
82
83RTR3DECL(int) RTPathSetMode(const char *pszPath, RTFMODE fMode)
84{
85 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
86 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
87
88 int rc;
89 fMode = rtFsModeNormalize(fMode, pszPath, 0);
90 if (rtFsModeIsValidPermissions(fMode))
91 {
92 char const *pszNativePath;
93 rc = rtPathToNative(&pszNativePath, pszPath, NULL);
94 if (RT_SUCCESS(rc))
95 {
96 if (chmod(pszNativePath, fMode & RTFS_UNIX_MASK) != 0)
97 rc = RTErrConvertFromErrno(errno);
98 rtPathFreeNative(pszNativePath, pszPath);
99 }
100 }
101 else
102 {
103 AssertMsgFailed(("Invalid file mode! %RTfmode\n", fMode));
104 rc = VERR_INVALID_FMODE;
105 }
106 return rc;
107}
108
109
110/**
111 * Checks if two files are the one and same file.
112 */
113static bool rtPathSame(const char *pszNativeSrc, const char *pszNativeDst)
114{
115 struct stat SrcStat;
116 if (lstat(pszNativeSrc, &SrcStat))
117 return false;
118 struct stat DstStat;
119 if (lstat(pszNativeDst, &DstStat))
120 return false;
121 Assert(SrcStat.st_dev && DstStat.st_dev);
122 Assert(SrcStat.st_ino && DstStat.st_ino);
123 if ( SrcStat.st_dev == DstStat.st_dev
124 && SrcStat.st_ino == DstStat.st_ino
125 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
126 return true;
127 return false;
128}
129
130
131/**
132 * Worker for RTPathRename, RTDirRename, RTFileRename.
133 *
134 * @returns IPRT status code.
135 * @param pszSrc The source path.
136 * @param pszDst The destination path.
137 * @param fRename The rename flags.
138 * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
139 * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
140 * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
141 * not a directory (we are NOT checking whether it's a file).
142 */
143DECLHIDDEN(int) rtPathPosixRename(const char *pszSrc, const char *pszDst, unsigned fRename, RTFMODE fFileType)
144{
145 /*
146 * Convert the paths.
147 */
148 char const *pszNativeSrc;
149 int rc = rtPathToNative(&pszNativeSrc, pszSrc, NULL);
150 if (RT_SUCCESS(rc))
151 {
152 char const *pszNativeDst;
153 rc = rtPathToNative(&pszNativeDst, pszDst, NULL);
154 if (RT_SUCCESS(rc))
155 {
156 /*
157 * Check that the source exists and that any types that's specified matches.
158 * We have to check this first to avoid getting errnous VERR_ALREADY_EXISTS
159 * errors from the next step.
160 *
161 * There are race conditions here (perhaps unlikely ones, but still), but I'm
162 * afraid there is little with can do to fix that.
163 */
164 struct stat SrcStat;
165 if (lstat(pszNativeSrc, &SrcStat))
166 rc = RTErrConvertFromErrno(errno);
167 else if (!fFileType)
168 rc = VINF_SUCCESS;
169 else if (RTFS_IS_DIRECTORY(fFileType))
170 rc = S_ISDIR(SrcStat.st_mode) ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
171 else
172 rc = S_ISDIR(SrcStat.st_mode) ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
173 if (RT_SUCCESS(rc))
174 {
175 bool fSameFile = false;
176
177 /*
178 * Check if the target exists, rename is rather destructive.
179 * We'll have to make sure we don't overwrite the source!
180 * Another race condition btw.
181 */
182 struct stat DstStat;
183 if (lstat(pszNativeDst, &DstStat))
184 rc = errno == ENOENT ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
185 else
186 {
187 Assert(SrcStat.st_dev && DstStat.st_dev);
188 Assert(SrcStat.st_ino && DstStat.st_ino);
189 if ( SrcStat.st_dev == DstStat.st_dev
190 && SrcStat.st_ino == DstStat.st_ino
191 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
192 {
193 /*
194 * It's likely that we're talking about the same file here.
195 * We should probably check paths or whatever, but for now this'll have to be enough.
196 */
197 fSameFile = true;
198 }
199 if (fSameFile)
200 rc = VINF_SUCCESS;
201 else if (S_ISDIR(DstStat.st_mode) || !(fRename & RTPATHRENAME_FLAGS_REPLACE))
202 rc = VERR_ALREADY_EXISTS;
203 else
204 rc = VINF_SUCCESS;
205
206 }
207 if (RT_SUCCESS(rc))
208 {
209 if (!rename(pszNativeSrc, pszNativeDst))
210 rc = VINF_SUCCESS;
211 else if ( (fRename & RTPATHRENAME_FLAGS_REPLACE)
212 && (errno == ENOTDIR || errno == EEXIST))
213 {
214 /*
215 * Check that the destination isn't a directory.
216 * Yet another race condition.
217 */
218 if (rtPathSame(pszNativeSrc, pszNativeDst))
219 {
220 rc = VINF_SUCCESS;
221 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): appears to be the same file... (errno=%d)\n",
222 pszSrc, pszDst, fRename, fFileType, errno));
223 }
224 else
225 {
226 if (lstat(pszNativeDst, &DstStat))
227 rc = errno != ENOENT ? RTErrConvertFromErrno(errno) : VINF_SUCCESS;
228 else if (S_ISDIR(DstStat.st_mode))
229 rc = VERR_ALREADY_EXISTS;
230 else
231 rc = VINF_SUCCESS;
232 if (RT_SUCCESS(rc))
233 {
234 if (!unlink(pszNativeDst))
235 {
236 if (!rename(pszNativeSrc, pszNativeDst))
237 rc = VINF_SUCCESS;
238 else
239 {
240 rc = RTErrConvertFromErrno(errno);
241 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
242 pszSrc, pszDst, fRename, fFileType, rc, errno));
243 }
244 }
245 else
246 {
247 rc = RTErrConvertFromErrno(errno);
248 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): failed to unlink dst rc=%Rrc errno=%d\n",
249 pszSrc, pszDst, fRename, fFileType, rc, errno));
250 }
251 }
252 else
253 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): dst !dir check failed rc=%Rrc\n",
254 pszSrc, pszDst, fRename, fFileType, rc));
255 }
256 }
257 else
258 {
259 rc = RTErrConvertFromErrno(errno);
260 if (errno == ENOTDIR)
261 rc = VERR_ALREADY_EXISTS; /* unless somebody is racing us, this is the right interpretation */
262 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
263 pszSrc, pszDst, fRename, fFileType, rc, errno));
264 }
265 }
266 else
267 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): destination check failed rc=%Rrc errno=%d\n",
268 pszSrc, pszDst, fRename, fFileType, rc, errno));
269 }
270 else
271 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): source type check failed rc=%Rrc errno=%d\n",
272 pszSrc, pszDst, fRename, fFileType, rc, errno));
273
274 rtPathFreeNative(pszNativeDst, pszDst);
275 }
276 rtPathFreeNative(pszNativeSrc, pszSrc);
277 }
278 return rc;
279}
280
281
282RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
283{
284 /*
285 * Validate input.
286 */
287 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
288 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
289 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
290 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
291 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
292
293 /*
294 * Hand it to the worker.
295 */
296 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, 0);
297
298 Log(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
299 return rc;
300}
301
302
303RTR3DECL(int) RTPathUnlink(const char *pszPath, uint32_t fUnlink)
304{
305 RT_NOREF_PV(pszPath); RT_NOREF_PV(fUnlink);
306 return VERR_NOT_IMPLEMENTED;
307}
308
309
310RTDECL(bool) RTPathExists(const char *pszPath)
311{
312 return RTPathExistsEx(pszPath, RTPATH_F_FOLLOW_LINK);
313}
314
315
316RTDECL(bool) RTPathExistsEx(const char *pszPath, uint32_t fFlags)
317{
318 /*
319 * Validate input.
320 */
321 AssertPtrReturn(pszPath, false);
322 AssertReturn(*pszPath, false);
323 Assert(RTPATH_F_IS_VALID(fFlags, 0));
324
325 /*
326 * Convert the path and check if it exists using stat().
327 */
328 char const *pszNativePath;
329 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
330 if (RT_SUCCESS(rc))
331 {
332 struct stat Stat;
333 if (fFlags & RTPATH_F_FOLLOW_LINK)
334 rc = stat(pszNativePath, &Stat);
335 else
336 rc = lstat(pszNativePath, &Stat);
337 if (!rc)
338 rc = VINF_SUCCESS;
339 else
340 rc = VERR_GENERAL_FAILURE;
341 rtPathFreeNative(pszNativePath, pszPath);
342 }
343 return RT_SUCCESS(rc);
344}
345
346
347RTDECL(int) RTPathGetCurrent(char *pszPath, size_t cchPath)
348{
349 /*
350 * Try with a reasonably sized buffer first.
351 */
352 char szNativeCurDir[RTPATH_MAX];
353 if (getcwd(szNativeCurDir, sizeof(szNativeCurDir)) != NULL)
354 return rtPathFromNativeCopy(pszPath, cchPath, szNativeCurDir, NULL);
355
356 /*
357 * Retry a few times with really big buffers if we failed because CWD is unreasonably long.
358 */
359 int iErr = errno;
360 if (iErr != ERANGE)
361 return RTErrConvertFromErrno(iErr);
362
363 size_t cbNativeTmp = RTPATH_BIG_MAX;
364 for (;;)
365 {
366 char *pszNativeTmp = (char *)RTMemTmpAlloc(cbNativeTmp);
367 if (!pszNativeTmp)
368 return VERR_NO_TMP_MEMORY;
369 if (getcwd(pszNativeTmp, cbNativeTmp) != NULL)
370 {
371 int rc = rtPathFromNativeCopy(pszPath, cchPath, pszNativeTmp, NULL);
372 RTMemTmpFree(pszNativeTmp);
373 return rc;
374 }
375 iErr = errno;
376 RTMemTmpFree(pszNativeTmp);
377 if (iErr != ERANGE)
378 return RTErrConvertFromErrno(iErr);
379
380 cbNativeTmp += RTPATH_BIG_MAX;
381 if (cbNativeTmp > RTPATH_BIG_MAX * 4)
382 return VERR_FILENAME_TOO_LONG;
383 }
384}
385
386
387RTDECL(int) RTPathSetCurrent(const char *pszPath)
388{
389 /*
390 * Validate input.
391 */
392 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
393 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
394
395 /*
396 * Change the directory.
397 */
398 char const *pszNativePath;
399 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
400 if (RT_SUCCESS(rc))
401 {
402 if (chdir(pszNativePath))
403 rc = RTErrConvertFromErrno(errno);
404 rtPathFreeNative(pszNativePath, pszPath);
405 }
406 return rc;
407}
408
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette