VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use