VirtualBox

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

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

IPRT: Implemented RTFileOpenEx for posix too. ticketref:9276

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 26.4 KB
Line 
1/* $Id: fileio-posix.cpp 77684 2019-03-13 16:39:57Z vboxsync $ */
2/** @file
3 * IPRT - File I/O, 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_FILE
32
33#include <errno.h>
34#include <sys/stat.h>
35#include <sys/types.h>
36#include <sys/ioctl.h>
37#include <fcntl.h>
38#ifdef _MSC_VER
39# include <io.h>
40# include <stdio.h>
41#else
42# include <unistd.h>
43# include <sys/time.h>
44#endif
45#ifdef RT_OS_LINUX
46# include <sys/file.h>
47#endif
48#if defined(RT_OS_OS2) && (!defined(__INNOTEK_LIBC__) || __INNOTEK_LIBC__ < 0x006)
49# include <io.h>
50#endif
51#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
52# include <sys/disk.h>
53#endif
54#ifdef RT_OS_SOLARIS
55# include <stropts.h>
56# include <sys/dkio.h>
57# include <sys/vtoc.h>
58#endif /* RT_OS_SOLARIS */
59
60#include <iprt/file.h>
61#include <iprt/path.h>
62#include <iprt/assert.h>
63#include <iprt/string.h>
64#include <iprt/err.h>
65#include <iprt/log.h>
66#include <iprt/thread.h>
67#include "internal/file.h"
68#include "internal/fs.h"
69#include "internal/path.h"
70
71
72
73/*********************************************************************************************************************************
74* Defined Constants And Macros *
75*********************************************************************************************************************************/
76/** Default file permissions for newly created files. */
77#if defined(S_IRUSR) && defined(S_IWUSR)
78# define RT_FILE_PERMISSION (S_IRUSR | S_IWUSR)
79#else
80# define RT_FILE_PERMISSION (00600)
81#endif
82
83
84/*********************************************************************************************************************************
85* Defined Constants And Macros *
86*********************************************************************************************************************************/
87#ifdef O_CLOEXEC
88static int volatile g_fHave_O_CLOEXEC = 0; /* {-1,0,1}; since Linux 2.6.23 */
89#endif
90
91
92
93RTDECL(bool) RTFileExists(const char *pszPath)
94{
95 bool fRc = false;
96 char const *pszNativePath;
97 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
98 if (RT_SUCCESS(rc))
99 {
100 struct stat s;
101 fRc = !stat(pszNativePath, &s)
102 && S_ISREG(s.st_mode);
103
104 rtPathFreeNative(pszNativePath, pszPath);
105 }
106
107 LogFlow(("RTFileExists(%p={%s}): returns %RTbool\n", pszPath, pszPath, fRc));
108 return fRc;
109}
110
111
112#ifdef O_CLOEXEC
113/** Worker for RTFileOpenEx that detects whether the kernel supports
114 * O_CLOEXEC or not, setting g_fHave_O_CLOEXEC to 1 or -1 accordingly. */
115static int rtFileOpenExDetectCloExecSupport(void)
116{
117 /*
118 * Open /dev/null with O_CLOEXEC and see if FD_CLOEXEC is set or not.
119 */
120 int fHave_O_CLOEXEC = -1;
121 int fd = open("/dev/null", O_RDONLY | O_CLOEXEC, 0);
122 if (fd >= 0)
123 {
124 int fFlags = fcntl(fd, F_GETFD, 0);
125 fHave_O_CLOEXEC = fFlags > 0 && (fFlags & FD_CLOEXEC) ? 1 : -1;
126 close(fd);
127 }
128 else
129 AssertMsg(errno == EINVAL, ("%d\n", errno));
130 g_fHave_O_CLOEXEC = fHave_O_CLOEXEC;
131 return fHave_O_CLOEXEC;
132}
133#endif
134
135
136RTR3DECL(int) RTFileOpen(PRTFILE pFile, const char *pszFilename, uint64_t fOpen)
137{
138 return RTFileOpenEx(pszFilename, fOpen, pFile, NULL);
139}
140
141
142RTDECL(int) RTFileOpenEx(const char *pszFilename, uint64_t fOpen, PRTFILE phFile, PRTFILEACTION penmActionTaken)
143{
144 /*
145 * Validate input.
146 */
147 AssertPtrReturn(phFile, VERR_INVALID_POINTER);
148 *phFile = NIL_RTFILE;
149 if (penmActionTaken)
150 *penmActionTaken = RTFILEACTION_INVALID;
151 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
152
153 /*
154 * Merge forced open flags and validate them.
155 */
156 int rc = rtFileRecalcAndValidateFlags(&fOpen);
157 if (RT_FAILURE(rc))
158 return rc;
159#ifndef O_NONBLOCK
160 AssertReturn(!(fOpen & RTFILE_O_NON_BLOCK), VERR_INVALID_FLAGS);
161#endif
162
163 /*
164 * Calculate open mode flags.
165 */
166 int fOpenMode = 0;
167#ifdef O_BINARY
168 fOpenMode |= O_BINARY; /* (pc) */
169#endif
170#ifdef O_LARGEFILE
171 fOpenMode |= O_LARGEFILE; /* (linux, solaris) */
172#endif
173#ifdef O_NOINHERIT
174 if (!(fOpen & RTFILE_O_INHERIT))
175 fOpenMode |= O_NOINHERIT;
176#endif
177#ifdef O_CLOEXEC
178 int fHave_O_CLOEXEC = g_fHave_O_CLOEXEC;
179 if ( !(fOpen & RTFILE_O_INHERIT)
180 && ( fHave_O_CLOEXEC > 0
181 || ( fHave_O_CLOEXEC == 0
182 && (fHave_O_CLOEXEC = rtFileOpenExDetectCloExecSupport()) > 0)))
183 fOpenMode |= O_CLOEXEC;
184#endif
185#ifdef O_NONBLOCK
186 if (fOpen & RTFILE_O_NON_BLOCK)
187 fOpenMode |= O_NONBLOCK;
188#endif
189#ifdef O_SYNC
190 if (fOpen & RTFILE_O_WRITE_THROUGH)
191 fOpenMode |= O_SYNC;
192#endif
193#if defined(O_DIRECT) && defined(RT_OS_LINUX)
194 /* O_DIRECT is mandatory to get async I/O working on Linux. */
195 if (fOpen & RTFILE_O_ASYNC_IO)
196 fOpenMode |= O_DIRECT;
197#endif
198#if defined(O_DIRECT) && (defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD))
199 /* Disable the kernel cache. */
200 if (fOpen & RTFILE_O_NO_CACHE)
201 fOpenMode |= O_DIRECT;
202#endif
203
204 /* create/truncate file */
205 switch (fOpen & RTFILE_O_ACTION_MASK)
206 {
207 case RTFILE_O_OPEN: break;
208 case RTFILE_O_OPEN_CREATE: fOpenMode |= O_CREAT; break;
209 case RTFILE_O_CREATE: fOpenMode |= O_CREAT | O_EXCL; break;
210 case RTFILE_O_CREATE_REPLACE: fOpenMode |= O_CREAT | O_TRUNC; break; /** @todo replacing needs fixing, this is *not* a 1:1 mapping! */
211 default:
212 AssertMsgFailed(("fOpen=%#llx\n", fOpen));
213 fOpen = (fOpen & ~RTFILE_O_ACTION_MASK) | RTFILE_O_OPEN;
214 break;
215
216 }
217 if ( (fOpen & RTFILE_O_TRUNCATE)
218 && (fOpen & RTFILE_O_ACTION_MASK) != RTFILE_O_CREATE)
219 fOpenMode |= O_TRUNC;
220
221 switch (fOpen & RTFILE_O_ACCESS_MASK)
222 {
223 case RTFILE_O_READ:
224 fOpenMode |= O_RDONLY; /* RTFILE_O_APPEND is ignored. */
225 break;
226 case RTFILE_O_WRITE:
227 fOpenMode |= fOpen & RTFILE_O_APPEND ? O_APPEND | O_WRONLY : O_WRONLY;
228 break;
229 case RTFILE_O_READWRITE:
230 fOpenMode |= fOpen & RTFILE_O_APPEND ? O_APPEND | O_RDWR : O_RDWR;
231 break;
232 default:
233 AssertMsgFailedReturn(("RTFileOpen received an invalid RW value, fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS);
234 }
235
236 /* File mode. */
237 int fMode = (fOpen & RTFILE_O_CREATE_MODE_MASK)
238 ? (fOpen & RTFILE_O_CREATE_MODE_MASK) >> RTFILE_O_CREATE_MODE_SHIFT
239 : RT_FILE_PERMISSION;
240
241 /** @todo sharing? */
242
243 /*
244 * Open/create the file.
245 */
246 char const *pszNativeFilename;
247 rc = rtPathToNative(&pszNativeFilename, pszFilename, NULL);
248 if (RT_FAILURE(rc))
249 return (rc);
250
251 int fh;
252 int iErr;
253 if (!penmActionTaken)
254 {
255 fh = open(pszNativeFilename, fOpenMode, fMode);
256 iErr = errno;
257 }
258 else
259 {
260 /* We need to know exactly which action was taken by open, Windows &
261 OS/2 style. Can be tedious and subject to races: */
262 switch (fOpen & RTFILE_O_ACTION_MASK)
263 {
264 case RTFILE_O_OPEN:
265 Assert(!(fOpenMode & O_CREAT));
266 Assert(!(fOpenMode & O_EXCL));
267 fh = open(pszNativeFilename, fOpenMode, fMode);
268 iErr = errno;
269 if (fh >= 0)
270 *penmActionTaken = fOpenMode & O_TRUNC ? RTFILEACTION_TRUNCATED : RTFILEACTION_OPENED;
271 break;
272
273 case RTFILE_O_CREATE:
274 Assert(fOpenMode & O_CREAT);
275 Assert(fOpenMode & O_EXCL);
276 fh = open(pszNativeFilename, fOpenMode, fMode);
277 iErr = errno;
278 if (fh >= 0)
279 *penmActionTaken = RTFILEACTION_CREATED;
280 else if (iErr == EEXIST)
281 *penmActionTaken = RTFILEACTION_ALREADY_EXISTS;
282 break;
283
284 case RTFILE_O_OPEN_CREATE:
285 case RTFILE_O_CREATE_REPLACE:
286 {
287 Assert(fOpenMode & O_CREAT);
288 Assert(!(fOpenMode & O_EXCL));
289 int iTries = 64;
290 while (iTries-- > 0)
291 {
292 /* Yield the CPU if we've raced too long. */
293 if (iTries < 4)
294 RTThreadSleep(2 - (iTries & 1));
295
296 /* Try exclusive creation first: */
297 fh = open(pszNativeFilename, fOpenMode | O_EXCL, fMode);
298 iErr = errno;
299 if (fh >= 0)
300 {
301 *penmActionTaken = RTFILEACTION_CREATED;
302 break;
303 }
304 if (iErr != EEXIST)
305 break;
306
307 /* If the file exists, try open it: */
308 fh = open(pszNativeFilename, fOpenMode & ~O_CREAT, fMode);
309 iErr = errno;
310 if (fh >= 0)
311 {
312 if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
313 *penmActionTaken = fOpenMode & O_TRUNC ? RTFILEACTION_TRUNCATED : RTFILEACTION_OPENED;
314 else
315 *penmActionTaken = RTFILEACTION_REPLACED;
316 break;
317 }
318 if (iErr != ENOENT)
319 break;
320 }
321 Assert(iTries >= 0);
322 if (iTries < 0)
323 {
324 /* Thanks for the race, but we need to get on with things. */
325 fh = open(pszNativeFilename, fOpenMode, fMode);
326 iErr = errno;
327 if (fh >= 0)
328 *penmActionTaken = RTFILEACTION_OPENED;
329 }
330 break;
331 }
332
333 default:
334 AssertMsgFailed(("fOpen=%#llx fOpenMode=%#x\n", fOpen, fOpenMode));
335 iErr = EINVAL;
336 fh = -1;
337 break;
338 }
339 }
340
341 rtPathFreeNative(pszNativeFilename, pszFilename);
342 if (fh >= 0)
343 {
344 iErr = 0;
345
346 /*
347 * Mark the file handle close on exec, unless inherit is specified.
348 */
349 if ( !(fOpen & RTFILE_O_INHERIT)
350#ifdef O_NOINHERIT
351 && !(fOpenMode & O_NOINHERIT) /* Take care since it might be a zero value dummy. */
352#endif
353#ifdef O_CLOEXEC
354 && fHave_O_CLOEXEC <= 0
355#endif
356 )
357 iErr = fcntl(fh, F_SETFD, FD_CLOEXEC) >= 0 ? 0 : errno;
358
359 /*
360 * Switch direct I/O on now if requested and required.
361 */
362#if defined(RT_OS_DARWIN) \
363 || (defined(RT_OS_SOLARIS) && !defined(IN_GUEST))
364 if (iErr == 0 && (fOpen & RTFILE_O_NO_CACHE))
365 {
366# if defined(RT_OS_DARWIN)
367 iErr = fcntl(fh, F_NOCACHE, 1) >= 0 ? 0 : errno;
368# else
369 iErr = directio(fh, DIRECTIO_ON) >= 0 ? 0 : errno;
370# endif
371 }
372#endif
373
374 /*
375 * Implement / emulate file sharing.
376 *
377 * We need another mode which allows skipping this stuff completely
378 * and do things the UNIX way. So for the present this is just a debug
379 * aid that can be enabled by developers too lazy to test on Windows.
380 */
381#if 0 && defined(RT_OS_LINUX)
382 if (iErr == 0)
383 {
384 /* This approach doesn't work because only knfsd checks for these
385 buggers. :-( */
386 int iLockOp;
387 switch (fOpen & RTFILE_O_DENY_MASK)
388 {
389 default:
390 AssertFailed();
391 case RTFILE_O_DENY_NONE:
392 case RTFILE_O_DENY_NOT_DELETE:
393 iLockOp = LOCK_MAND | LOCK_READ | LOCK_WRITE;
394 break;
395 case RTFILE_O_DENY_READ:
396 case RTFILE_O_DENY_READ | RTFILE_O_DENY_NOT_DELETE:
397 iLockOp = LOCK_MAND | LOCK_WRITE;
398 break;
399 case RTFILE_O_DENY_WRITE:
400 case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_NOT_DELETE:
401 iLockOp = LOCK_MAND | LOCK_READ;
402 break;
403 case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ:
404 case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ | RTFILE_O_DENY_NOT_DELETE:
405 iLockOp = LOCK_MAND;
406 break;
407 }
408 iErr = flock(fh, iLockOp | LOCK_NB);
409 if (iErr != 0)
410 iErr = errno == EAGAIN ? ETXTBSY : 0;
411 }
412#endif /* 0 && RT_OS_LINUX */
413#if defined(DEBUG_bird) && !defined(RT_OS_SOLARIS)
414 if (iErr == 0)
415 {
416 /* This emulation is incomplete but useful. */
417 switch (fOpen & RTFILE_O_DENY_MASK)
418 {
419 default:
420 AssertFailed();
421 case RTFILE_O_DENY_NONE:
422 case RTFILE_O_DENY_NOT_DELETE:
423 case RTFILE_O_DENY_READ:
424 case RTFILE_O_DENY_READ | RTFILE_O_DENY_NOT_DELETE:
425 break;
426 case RTFILE_O_DENY_WRITE:
427 case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_NOT_DELETE:
428 case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ:
429 case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ | RTFILE_O_DENY_NOT_DELETE:
430 if (fOpen & RTFILE_O_WRITE)
431 {
432 iErr = flock(fh, LOCK_EX | LOCK_NB);
433 if (iErr != 0)
434 iErr = errno == EAGAIN ? ETXTBSY : 0;
435 }
436 break;
437 }
438 }
439#endif
440#ifdef RT_OS_SOLARIS
441 /** @todo Use fshare_t and associates, it's a perfect match. see sys/fcntl.h */
442#endif
443
444 /*
445 * We're done.
446 */
447 if (iErr == 0)
448 {
449 *phFile = (RTFILE)(uintptr_t)fh;
450 Assert((intptr_t)*phFile == fh);
451 LogFlow(("RTFileOpen(%p:{%RTfile}, %p:{%s}, %#llx): returns %Rrc\n",
452 phFile, *phFile, pszFilename, pszFilename, fOpen, rc));
453 return VINF_SUCCESS;
454 }
455
456 close(fh);
457 }
458 return RTErrConvertFromErrno(iErr);
459}
460
461
462RTR3DECL(int) RTFileOpenBitBucket(PRTFILE phFile, uint64_t fAccess)
463{
464 AssertReturn( fAccess == RTFILE_O_READ
465 || fAccess == RTFILE_O_WRITE
466 || fAccess == RTFILE_O_READWRITE,
467 VERR_INVALID_PARAMETER);
468 return RTFileOpen(phFile, "/dev/null", fAccess | RTFILE_O_DENY_NONE | RTFILE_O_OPEN);
469}
470
471
472RTR3DECL(int) RTFileClose(RTFILE hFile)
473{
474 if (hFile == NIL_RTFILE)
475 return VINF_SUCCESS;
476 if (close(RTFileToNative(hFile)) == 0)
477 return VINF_SUCCESS;
478 return RTErrConvertFromErrno(errno);
479}
480
481
482RTR3DECL(int) RTFileFromNative(PRTFILE pFile, RTHCINTPTR uNative)
483{
484 AssertCompile(sizeof(uNative) == sizeof(*pFile));
485 if (uNative < 0)
486 {
487 AssertMsgFailed(("%p\n", uNative));
488 *pFile = NIL_RTFILE;
489 return VERR_INVALID_HANDLE;
490 }
491 *pFile = (RTFILE)uNative;
492 return VINF_SUCCESS;
493}
494
495
496RTR3DECL(RTHCINTPTR) RTFileToNative(RTFILE hFile)
497{
498 AssertReturn(hFile != NIL_RTFILE, -1);
499 return (intptr_t)hFile;
500}
501
502
503RTFILE rtFileGetStandard(RTHANDLESTD enmStdHandle)
504{
505 int fd;
506 switch (enmStdHandle)
507 {
508 case RTHANDLESTD_INPUT: fd = 0; break;
509 case RTHANDLESTD_OUTPUT: fd = 1; break;
510 case RTHANDLESTD_ERROR: fd = 2; break;
511 default:
512 AssertFailedReturn(NIL_RTFILE);
513 }
514
515 struct stat st;
516 int rc = fstat(fd, &st);
517 if (rc == -1)
518 return NIL_RTFILE;
519 return (RTFILE)(intptr_t)fd;
520}
521
522
523RTR3DECL(int) RTFileDelete(const char *pszFilename)
524{
525 char const *pszNativeFilename;
526 int rc = rtPathToNative(&pszNativeFilename, pszFilename, NULL);
527 if (RT_SUCCESS(rc))
528 {
529 if (unlink(pszNativeFilename) != 0)
530 rc = RTErrConvertFromErrno(errno);
531 rtPathFreeNative(pszNativeFilename, pszFilename);
532 }
533 return rc;
534}
535
536
537RTR3DECL(int) RTFileSeek(RTFILE hFile, int64_t offSeek, unsigned uMethod, uint64_t *poffActual)
538{
539 static const unsigned aSeekRecode[] =
540 {
541 SEEK_SET,
542 SEEK_CUR,
543 SEEK_END,
544 };
545
546 /*
547 * Validate input.
548 */
549 if (uMethod > RTFILE_SEEK_END)
550 {
551 AssertMsgFailed(("Invalid uMethod=%d\n", uMethod));
552 return VERR_INVALID_PARAMETER;
553 }
554
555 /* check that within off_t range. */
556 if ( sizeof(off_t) < sizeof(offSeek)
557 && ( (offSeek > 0 && (unsigned)(offSeek >> 32) != 0)
558 || (offSeek < 0 && (unsigned)(-offSeek >> 32) != 0)))
559 {
560 AssertMsgFailed(("64-bit search not supported\n"));
561 return VERR_NOT_SUPPORTED;
562 }
563
564 off_t offCurrent = lseek(RTFileToNative(hFile), (off_t)offSeek, aSeekRecode[uMethod]);
565 if (offCurrent != ~0)
566 {
567 if (poffActual)
568 *poffActual = (uint64_t)offCurrent;
569 return VINF_SUCCESS;
570 }
571 return RTErrConvertFromErrno(errno);
572}
573
574
575RTR3DECL(int) RTFileRead(RTFILE hFile, void *pvBuf, size_t cbToRead, size_t *pcbRead)
576{
577 if (cbToRead <= 0)
578 {
579 if (pcbRead)
580 *pcbRead = 0;
581 return VINF_SUCCESS;
582 }
583
584 /*
585 * Attempt read.
586 */
587 ssize_t cbRead = read(RTFileToNative(hFile), pvBuf, cbToRead);
588 if (cbRead >= 0)
589 {
590 if (pcbRead)
591 /* caller can handle partial read. */
592 *pcbRead = cbRead;
593 else
594 {
595 /* Caller expects all to be read. */
596 while ((ssize_t)cbToRead > cbRead)
597 {
598 ssize_t cbReadPart = read(RTFileToNative(hFile), (char*)pvBuf + cbRead, cbToRead - cbRead);
599 if (cbReadPart <= 0)
600 {
601 if (cbReadPart == 0)
602 return VERR_EOF;
603 return RTErrConvertFromErrno(errno);
604 }
605 cbRead += cbReadPart;
606 }
607 }
608 return VINF_SUCCESS;
609 }
610
611 return RTErrConvertFromErrno(errno);
612}
613
614
615RTR3DECL(int) RTFileWrite(RTFILE hFile, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
616{
617 if (cbToWrite <= 0)
618 return VINF_SUCCESS;
619
620 /*
621 * Attempt write.
622 */
623 ssize_t cbWritten = write(RTFileToNative(hFile), pvBuf, cbToWrite);
624 if (cbWritten >= 0)
625 {
626 if (pcbWritten)
627 /* caller can handle partial write. */
628 *pcbWritten = cbWritten;
629 else
630 {
631 /* Caller expects all to be write. */
632 while ((ssize_t)cbToWrite > cbWritten)
633 {
634 ssize_t cbWrittenPart = write(RTFileToNative(hFile), (const char *)pvBuf + cbWritten, cbToWrite - cbWritten);
635 if (cbWrittenPart <= 0)
636 return cbWrittenPart < 0 ? RTErrConvertFromErrno(errno) : VERR_TRY_AGAIN;
637 cbWritten += cbWrittenPart;
638 }
639 }
640 return VINF_SUCCESS;
641 }
642 return RTErrConvertFromErrno(errno);
643}
644
645
646RTR3DECL(int) RTFileSetSize(RTFILE hFile, uint64_t cbSize)
647{
648 /*
649 * Validate offset.
650 */
651 if ( sizeof(off_t) < sizeof(cbSize)
652 && (cbSize >> 32) != 0)
653 {
654 AssertMsgFailed(("64-bit filesize not supported! cbSize=%lld\n", cbSize));
655 return VERR_NOT_SUPPORTED;
656 }
657
658#if defined(_MSC_VER) || (defined(RT_OS_OS2) && (!defined(__INNOTEK_LIBC__) || __INNOTEK_LIBC__ < 0x006))
659 if (chsize(RTFileToNative(hFile), (off_t)cbSize) == 0)
660#else
661 /* This relies on a non-standard feature of FreeBSD, Linux, and OS/2
662 * LIBC v0.6 and higher. (SuS doesn't define ftruncate() and size bigger
663 * than the file.)
664 */
665 if (ftruncate(RTFileToNative(hFile), (off_t)cbSize) == 0)
666#endif
667 return VINF_SUCCESS;
668 return RTErrConvertFromErrno(errno);
669}
670
671
672RTR3DECL(int) RTFileGetSize(RTFILE hFile, uint64_t *pcbSize)
673{
674 /*
675 * Ask fstat() first.
676 */
677 struct stat st;
678 if (!fstat(RTFileToNative(hFile), &st))
679 {
680 *pcbSize = st.st_size;
681 if ( st.st_size != 0
682#if defined(RT_OS_SOLARIS)
683 || (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
684#elif defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD)
685 || !S_ISCHR(st.st_mode)
686#else
687 || !S_ISBLK(st.st_mode)
688#endif
689 )
690 return VINF_SUCCESS;
691
692 /*
693 * It could be a block device. Try determin the size by I/O control
694 * query or seek.
695 */
696#ifdef RT_OS_DARWIN
697 uint64_t cBlocks;
698 if (!ioctl(RTFileToNative(hFile), DKIOCGETBLOCKCOUNT, &cBlocks))
699 {
700 uint32_t cbBlock;
701 if (!ioctl(RTFileToNative(hFile), DKIOCGETBLOCKSIZE, &cbBlock))
702 {
703 *pcbSize = cBlocks * cbBlock;
704 return VINF_SUCCESS;
705 }
706 }
707 /* must be a block device, fail on failure. */
708
709#elif defined(RT_OS_SOLARIS)
710 struct dk_minfo MediaInfo;
711 if (!ioctl(RTFileToNative(hFile), DKIOCGMEDIAINFO, &MediaInfo))
712 {
713 *pcbSize = MediaInfo.dki_capacity * MediaInfo.dki_lbsize;
714 return VINF_SUCCESS;
715 }
716 /* might not be a block device. */
717 if (errno == EINVAL || errno == ENOTTY)
718 return VINF_SUCCESS;
719
720#elif defined(RT_OS_FREEBSD)
721 off_t cbMedia = 0;
722 if (!ioctl(RTFileToNative(hFile), DIOCGMEDIASIZE, &cbMedia))
723 {
724 *pcbSize = cbMedia;
725 return VINF_SUCCESS;
726 }
727 /* might not be a block device. */
728 if (errno == EINVAL || errno == ENOTTY)
729 return VINF_SUCCESS;
730
731#else
732 /* PORTME! Avoid this path when possible. */
733 uint64_t offSaved;
734 int rc = RTFileSeek(hFile, 0, RTFILE_SEEK_CURRENT, &offSaved);
735 if (RT_SUCCESS(rc))
736 {
737 rc = RTFileSeek(hFile, 0, RTFILE_SEEK_END, pcbSize);
738 int rc2 = RTFileSeek(hFile, offSaved, RTFILE_SEEK_BEGIN, NULL);
739 if (RT_SUCCESS(rc))
740 return rc2;
741 }
742#endif
743 }
744 return RTErrConvertFromErrno(errno);
745}
746
747
748RTR3DECL(int) RTFileGetMaxSizeEx(RTFILE hFile, PRTFOFF pcbMax)
749{
750 /*
751 * Save the current location
752 */
753 uint64_t offOld;
754 int rc = RTFileSeek(hFile, 0, RTFILE_SEEK_CURRENT, &offOld);
755 if (RT_FAILURE(rc))
756 return rc;
757
758 /*
759 * Perform a binary search for the max file size.
760 */
761 uint64_t offLow = 0;
762 uint64_t offHigh = 8 * _1T; /* we don't need bigger files */
763 /** @todo Unfortunately this does not work for certain file system types,
764 * for instance cifs mounts. Even worse, statvfs.f_fsid returns 0 for such
765 * file systems. */
766 //uint64_t offHigh = INT64_MAX;
767 for (;;)
768 {
769 uint64_t cbInterval = (offHigh - offLow) >> 1;
770 if (cbInterval == 0)
771 {
772 if (pcbMax)
773 *pcbMax = offLow;
774 return RTFileSeek(hFile, offOld, RTFILE_SEEK_BEGIN, NULL);
775 }
776
777 rc = RTFileSeek(hFile, offLow + cbInterval, RTFILE_SEEK_BEGIN, NULL);
778 if (RT_FAILURE(rc))
779 offHigh = offLow + cbInterval;
780 else
781 offLow = offLow + cbInterval;
782 }
783}
784
785
786RTR3DECL(bool) RTFileIsValid(RTFILE hFile)
787{
788 if (hFile != NIL_RTFILE)
789 {
790 int fFlags = fcntl(RTFileToNative(hFile), F_GETFD);
791 if (fFlags >= 0)
792 return true;
793 }
794 return false;
795}
796
797
798RTR3DECL(int) RTFileFlush(RTFILE hFile)
799{
800 if (fsync(RTFileToNative(hFile)))
801 return RTErrConvertFromErrno(errno);
802 return VINF_SUCCESS;
803}
804
805
806RTR3DECL(int) RTFileIoCtl(RTFILE hFile, unsigned long ulRequest, void *pvData, unsigned cbData, int *piRet)
807{
808 NOREF(cbData);
809 int rc = ioctl(RTFileToNative(hFile), ulRequest, pvData);
810 if (piRet)
811 *piRet = rc;
812 return rc >= 0 ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
813}
814
815
816RTR3DECL(int) RTFileSetMode(RTFILE hFile, RTFMODE fMode)
817{
818 /*
819 * Normalize the mode and call the API.
820 */
821 fMode = rtFsModeNormalize(fMode, NULL, 0);
822 if (!rtFsModeIsValid(fMode))
823 return VERR_INVALID_PARAMETER;
824
825 if (fchmod(RTFileToNative(hFile), fMode & RTFS_UNIX_MASK))
826 {
827 int rc = RTErrConvertFromErrno(errno);
828 Log(("RTFileSetMode(%RTfile,%RTfmode): returns %Rrc\n", hFile, fMode, rc));
829 return rc;
830 }
831 return VINF_SUCCESS;
832}
833
834
835RTDECL(int) RTFileSetOwner(RTFILE hFile, uint32_t uid, uint32_t gid)
836{
837 uid_t uidNative = uid != NIL_RTUID ? (uid_t)uid : (uid_t)-1;
838 AssertReturn(uid == uidNative, VERR_INVALID_PARAMETER);
839 gid_t gidNative = gid != NIL_RTGID ? (gid_t)gid : (gid_t)-1;
840 AssertReturn(gid == gidNative, VERR_INVALID_PARAMETER);
841
842 if (fchown(RTFileToNative(hFile), uidNative, gidNative))
843 return RTErrConvertFromErrno(errno);
844 return VINF_SUCCESS;
845}
846
847
848RTR3DECL(int) RTFileRename(const char *pszSrc, const char *pszDst, unsigned fRename)
849{
850 /*
851 * Validate input.
852 */
853 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
854 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
855 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
856 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
857 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
858
859 /*
860 * Take common cause with RTPathRename.
861 */
862 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, RTFS_TYPE_FILE);
863
864 LogFlow(("RTDirRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n",
865 pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
866 return rc;
867}
868
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