VirtualBox

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

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

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 13.6 KB
Line 
1/* $Id: path-posix.cpp 76553 2019-01-01 01:45:53Z 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/string.h>
46#include <iprt/err.h>
47#include <iprt/log.h>
48#include "internal/path.h"
49#include "internal/process.h"
50#include "internal/fs.h"
51
52
53
54RTDECL(int) RTPathReal(const char *pszPath, char *pszRealPath, size_t cchRealPath)
55{
56 /*
57 * Convert input.
58 */
59 char const *pszNativePath;
60 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
61 if (RT_SUCCESS(rc))
62 {
63 /*
64 * On POSIX platforms the API doesn't take a length parameter, which makes it
65 * a little bit more work.
66 */
67 char szTmpPath[PATH_MAX + 1];
68 const char *psz = realpath(pszNativePath, szTmpPath);
69 if (psz)
70 rc = rtPathFromNativeCopy(pszRealPath, cchRealPath, szTmpPath, NULL);
71 else
72 rc = RTErrConvertFromErrno(errno);
73 rtPathFreeNative(pszNativePath, pszPath);
74 }
75
76 LogFlow(("RTPathReal(%p:{%s}, %p:{%s}, %u): returns %Rrc\n", pszPath, pszPath,
77 pszRealPath, RT_SUCCESS(rc) ? pszRealPath : "<failed>", cchRealPath, rc));
78 return rc;
79}
80
81
82RTR3DECL(int) RTPathSetMode(const char *pszPath, RTFMODE fMode)
83{
84 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
85 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
86
87 int rc;
88 fMode = rtFsModeNormalize(fMode, pszPath, 0);
89 if (rtFsModeIsValidPermissions(fMode))
90 {
91 char const *pszNativePath;
92 rc = rtPathToNative(&pszNativePath, pszPath, NULL);
93 if (RT_SUCCESS(rc))
94 {
95 if (chmod(pszNativePath, fMode & RTFS_UNIX_MASK) != 0)
96 rc = RTErrConvertFromErrno(errno);
97 rtPathFreeNative(pszNativePath, pszPath);
98 }
99 }
100 else
101 {
102 AssertMsgFailed(("Invalid file mode! %RTfmode\n", fMode));
103 rc = VERR_INVALID_FMODE;
104 }
105 return rc;
106}
107
108
109/**
110 * Checks if two files are the one and same file.
111 */
112static bool rtPathSame(const char *pszNativeSrc, const char *pszNativeDst)
113{
114 struct stat SrcStat;
115 if (lstat(pszNativeSrc, &SrcStat))
116 return false;
117 struct stat DstStat;
118 if (lstat(pszNativeDst, &DstStat))
119 return false;
120 Assert(SrcStat.st_dev && DstStat.st_dev);
121 Assert(SrcStat.st_ino && DstStat.st_ino);
122 if ( SrcStat.st_dev == DstStat.st_dev
123 && SrcStat.st_ino == DstStat.st_ino
124 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
125 return true;
126 return false;
127}
128
129
130/**
131 * Worker for RTPathRename, RTDirRename, RTFileRename.
132 *
133 * @returns IPRT status code.
134 * @param pszSrc The source path.
135 * @param pszDst The destination path.
136 * @param fRename The rename flags.
137 * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
138 * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
139 * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
140 * not a directory (we are NOT checking whether it's a file).
141 */
142DECLHIDDEN(int) rtPathPosixRename(const char *pszSrc, const char *pszDst, unsigned fRename, RTFMODE fFileType)
143{
144 /*
145 * Convert the paths.
146 */
147 char const *pszNativeSrc;
148 int rc = rtPathToNative(&pszNativeSrc, pszSrc, NULL);
149 if (RT_SUCCESS(rc))
150 {
151 char const *pszNativeDst;
152 rc = rtPathToNative(&pszNativeDst, pszDst, NULL);
153 if (RT_SUCCESS(rc))
154 {
155 /*
156 * Check that the source exists and that any types that's specified matches.
157 * We have to check this first to avoid getting errnous VERR_ALREADY_EXISTS
158 * errors from the next step.
159 *
160 * There are race conditions here (perhaps unlikely ones, but still), but I'm
161 * afraid there is little with can do to fix that.
162 */
163 struct stat SrcStat;
164 if (lstat(pszNativeSrc, &SrcStat))
165 rc = RTErrConvertFromErrno(errno);
166 else if (!fFileType)
167 rc = VINF_SUCCESS;
168 else if (RTFS_IS_DIRECTORY(fFileType))
169 rc = S_ISDIR(SrcStat.st_mode) ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
170 else
171 rc = S_ISDIR(SrcStat.st_mode) ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
172 if (RT_SUCCESS(rc))
173 {
174 bool fSameFile = false;
175
176 /*
177 * Check if the target exists, rename is rather destructive.
178 * We'll have to make sure we don't overwrite the source!
179 * Another race condition btw.
180 */
181 struct stat DstStat;
182 if (lstat(pszNativeDst, &DstStat))
183 rc = errno == ENOENT ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
184 else
185 {
186 Assert(SrcStat.st_dev && DstStat.st_dev);
187 Assert(SrcStat.st_ino && DstStat.st_ino);
188 if ( SrcStat.st_dev == DstStat.st_dev
189 && SrcStat.st_ino == DstStat.st_ino
190 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
191 {
192 /*
193 * It's likely that we're talking about the same file here.
194 * We should probably check paths or whatever, but for now this'll have to be enough.
195 */
196 fSameFile = true;
197 }
198 if (fSameFile)
199 rc = VINF_SUCCESS;
200 else if (S_ISDIR(DstStat.st_mode) || !(fRename & RTPATHRENAME_FLAGS_REPLACE))
201 rc = VERR_ALREADY_EXISTS;
202 else
203 rc = VINF_SUCCESS;
204
205 }
206 if (RT_SUCCESS(rc))
207 {
208 if (!rename(pszNativeSrc, pszNativeDst))
209 rc = VINF_SUCCESS;
210 else if ( (fRename & RTPATHRENAME_FLAGS_REPLACE)
211 && (errno == ENOTDIR || errno == EEXIST))
212 {
213 /*
214 * Check that the destination isn't a directory.
215 * Yet another race condition.
216 */
217 if (rtPathSame(pszNativeSrc, pszNativeDst))
218 {
219 rc = VINF_SUCCESS;
220 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): appears to be the same file... (errno=%d)\n",
221 pszSrc, pszDst, fRename, fFileType, errno));
222 }
223 else
224 {
225 if (lstat(pszNativeDst, &DstStat))
226 rc = errno != ENOENT ? RTErrConvertFromErrno(errno) : VINF_SUCCESS;
227 else if (S_ISDIR(DstStat.st_mode))
228 rc = VERR_ALREADY_EXISTS;
229 else
230 rc = VINF_SUCCESS;
231 if (RT_SUCCESS(rc))
232 {
233 if (!unlink(pszNativeDst))
234 {
235 if (!rename(pszNativeSrc, pszNativeDst))
236 rc = VINF_SUCCESS;
237 else
238 {
239 rc = RTErrConvertFromErrno(errno);
240 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
241 pszSrc, pszDst, fRename, fFileType, rc, errno));
242 }
243 }
244 else
245 {
246 rc = RTErrConvertFromErrno(errno);
247 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): failed to unlink dst rc=%Rrc errno=%d\n",
248 pszSrc, pszDst, fRename, fFileType, rc, errno));
249 }
250 }
251 else
252 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): dst !dir check failed rc=%Rrc\n",
253 pszSrc, pszDst, fRename, fFileType, rc));
254 }
255 }
256 else
257 {
258 rc = RTErrConvertFromErrno(errno);
259 if (errno == ENOTDIR)
260 rc = VERR_ALREADY_EXISTS; /* unless somebody is racing us, this is the right interpretation */
261 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
262 pszSrc, pszDst, fRename, fFileType, rc, errno));
263 }
264 }
265 else
266 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): destination check failed rc=%Rrc errno=%d\n",
267 pszSrc, pszDst, fRename, fFileType, rc, errno));
268 }
269 else
270 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): source type check failed rc=%Rrc errno=%d\n",
271 pszSrc, pszDst, fRename, fFileType, rc, errno));
272
273 rtPathFreeNative(pszNativeDst, pszDst);
274 }
275 rtPathFreeNative(pszNativeSrc, pszSrc);
276 }
277 return rc;
278}
279
280
281RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
282{
283 /*
284 * Validate input.
285 */
286 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
287 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
288 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
289 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
290 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
291
292 /*
293 * Hand it to the worker.
294 */
295 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, 0);
296
297 Log(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
298 return rc;
299}
300
301
302RTR3DECL(int) RTPathUnlink(const char *pszPath, uint32_t fUnlink)
303{
304 RT_NOREF_PV(pszPath); RT_NOREF_PV(fUnlink);
305 return VERR_NOT_IMPLEMENTED;
306}
307
308
309RTDECL(bool) RTPathExists(const char *pszPath)
310{
311 return RTPathExistsEx(pszPath, RTPATH_F_FOLLOW_LINK);
312}
313
314
315RTDECL(bool) RTPathExistsEx(const char *pszPath, uint32_t fFlags)
316{
317 /*
318 * Validate input.
319 */
320 AssertPtrReturn(pszPath, false);
321 AssertReturn(*pszPath, false);
322 Assert(RTPATH_F_IS_VALID(fFlags, 0));
323
324 /*
325 * Convert the path and check if it exists using stat().
326 */
327 char const *pszNativePath;
328 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
329 if (RT_SUCCESS(rc))
330 {
331 struct stat Stat;
332 if (fFlags & RTPATH_F_FOLLOW_LINK)
333 rc = stat(pszNativePath, &Stat);
334 else
335 rc = lstat(pszNativePath, &Stat);
336 if (!rc)
337 rc = VINF_SUCCESS;
338 else
339 rc = VERR_GENERAL_FAILURE;
340 rtPathFreeNative(pszNativePath, pszPath);
341 }
342 return RT_SUCCESS(rc);
343}
344
345
346RTDECL(int) RTPathGetCurrent(char *pszPath, size_t cchPath)
347{
348 int rc;
349 char szNativeCurDir[RTPATH_MAX];
350 if (getcwd(szNativeCurDir, sizeof(szNativeCurDir)) != NULL)
351 rc = rtPathFromNativeCopy(pszPath, cchPath, szNativeCurDir, NULL);
352 else
353 rc = RTErrConvertFromErrno(errno);
354 return rc;
355}
356
357
358RTDECL(int) RTPathSetCurrent(const char *pszPath)
359{
360 /*
361 * Validate input.
362 */
363 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
364 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
365
366 /*
367 * Change the directory.
368 */
369 char const *pszNativePath;
370 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
371 if (RT_SUCCESS(rc))
372 {
373 if (chdir(pszNativePath))
374 rc = RTErrConvertFromErrno(errno);
375 rtPathFreeNative(pszNativePath, pszPath);
376 }
377 return rc;
378}
379
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