VirtualBox

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

Last change on this file since 96076 was 96076, checked in by vboxsync, 22 months ago

IPRT/RTFileOpen: Added a RTFILE_O_TEMP_AUTO_DELETE flag for implementing tmpfile/tmpfile_s and similar. bugref:10261

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 28.2 KB
Line 
1/* $Id: fileio-posix.cpp 96076 2022-08-06 01:57:05Z vboxsync $ */
2/** @file
3 * IPRT - File I/O, POSIX, Part 1.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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#if defined(RT_OS_OS2) /* Cannot delete open files on OS/2. */
163 AssertReturn(!(fOpen & RTFILE_O_TEMP_AUTO_DELETE), VERR_NOT_SUPPORTED);
164#endif
165
166 /*
167 * Calculate open mode flags.
168 */
169 int fOpenMode = 0;
170#ifdef O_BINARY
171 fOpenMode |= O_BINARY; /* (pc) */
172#endif
173#ifdef O_LARGEFILE
174 fOpenMode |= O_LARGEFILE; /* (linux, solaris) */
175#endif
176#ifdef O_NOINHERIT
177 if (!(fOpen & RTFILE_O_INHERIT))
178 fOpenMode |= O_NOINHERIT;
179#endif
180#ifdef O_CLOEXEC
181 int fHave_O_CLOEXEC = g_fHave_O_CLOEXEC;
182 if ( !(fOpen & RTFILE_O_INHERIT)
183 && ( fHave_O_CLOEXEC > 0
184 || ( fHave_O_CLOEXEC == 0
185 && (fHave_O_CLOEXEC = rtFileOpenExDetectCloExecSupport()) > 0)))
186 fOpenMode |= O_CLOEXEC;
187#endif
188#ifdef O_NONBLOCK
189 if (fOpen & RTFILE_O_NON_BLOCK)
190 fOpenMode |= O_NONBLOCK;
191#endif
192#ifdef O_SYNC
193 if (fOpen & RTFILE_O_WRITE_THROUGH)
194 fOpenMode |= O_SYNC;
195#endif
196#if defined(O_DIRECT) && defined(RT_OS_LINUX)
197 /* O_DIRECT is mandatory to get async I/O working on Linux. */
198 if (fOpen & RTFILE_O_ASYNC_IO)
199 fOpenMode |= O_DIRECT;
200#endif
201#if defined(O_DIRECT) && (defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD))
202 /* Disable the kernel cache. */
203 if (fOpen & RTFILE_O_NO_CACHE)
204 fOpenMode |= O_DIRECT;
205#endif
206
207 /* create/truncate file */
208 switch (fOpen & RTFILE_O_ACTION_MASK)
209 {
210 case RTFILE_O_OPEN: break;
211 case RTFILE_O_OPEN_CREATE: fOpenMode |= O_CREAT; break;
212 case RTFILE_O_CREATE: fOpenMode |= O_CREAT | O_EXCL; break;
213 case RTFILE_O_CREATE_REPLACE: fOpenMode |= O_CREAT | O_TRUNC; break; /** @todo replacing needs fixing, this is *not* a 1:1 mapping! */
214 default:
215 AssertMsgFailed(("fOpen=%#llx\n", fOpen));
216 fOpen = (fOpen & ~RTFILE_O_ACTION_MASK) | RTFILE_O_OPEN;
217 break;
218
219 }
220 if ( (fOpen & RTFILE_O_TRUNCATE)
221 && (fOpen & RTFILE_O_ACTION_MASK) != RTFILE_O_CREATE)
222 fOpenMode |= O_TRUNC;
223
224 switch (fOpen & RTFILE_O_ACCESS_MASK)
225 {
226 case RTFILE_O_READ:
227 fOpenMode |= O_RDONLY; /* RTFILE_O_APPEND is ignored. */
228 break;
229 case RTFILE_O_WRITE:
230 fOpenMode |= fOpen & RTFILE_O_APPEND ? O_APPEND | O_WRONLY : O_WRONLY;
231 break;
232 case RTFILE_O_READWRITE:
233 fOpenMode |= fOpen & RTFILE_O_APPEND ? O_APPEND | O_RDWR : O_RDWR;
234 break;
235 default:
236 AssertMsgFailedReturn(("RTFileOpen received an invalid RW value, fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS);
237 }
238
239 /* File mode. */
240 int fMode = (fOpen & RTFILE_O_CREATE_MODE_MASK)
241 ? (fOpen & RTFILE_O_CREATE_MODE_MASK) >> RTFILE_O_CREATE_MODE_SHIFT
242 : RT_FILE_PERMISSION;
243
244 /** @todo sharing? */
245
246 /*
247 * Open/create the file.
248 */
249 char const *pszNativeFilename;
250 rc = rtPathToNative(&pszNativeFilename, pszFilename, NULL);
251 if (RT_FAILURE(rc))
252 return (rc);
253
254 int fh;
255 int iErr;
256 if (!penmActionTaken)
257 {
258 fh = open(pszNativeFilename, fOpenMode, fMode);
259 iErr = errno;
260 }
261 else
262 {
263 /* We need to know exactly which action was taken by open, Windows &
264 OS/2 style. Can be tedious and subject to races: */
265 switch (fOpen & RTFILE_O_ACTION_MASK)
266 {
267 case RTFILE_O_OPEN:
268 Assert(!(fOpenMode & O_CREAT));
269 Assert(!(fOpenMode & O_EXCL));
270 fh = open(pszNativeFilename, fOpenMode, fMode);
271 iErr = errno;
272 if (fh >= 0)
273 *penmActionTaken = fOpenMode & O_TRUNC ? RTFILEACTION_TRUNCATED : RTFILEACTION_OPENED;
274 break;
275
276 case RTFILE_O_CREATE:
277 Assert(fOpenMode & O_CREAT);
278 Assert(fOpenMode & O_EXCL);
279 fh = open(pszNativeFilename, fOpenMode, fMode);
280 iErr = errno;
281 if (fh >= 0)
282 *penmActionTaken = RTFILEACTION_CREATED;
283 else if (iErr == EEXIST)
284 *penmActionTaken = RTFILEACTION_ALREADY_EXISTS;
285 break;
286
287 case RTFILE_O_OPEN_CREATE:
288 case RTFILE_O_CREATE_REPLACE:
289 {
290 Assert(fOpenMode & O_CREAT);
291 Assert(!(fOpenMode & O_EXCL));
292 int iTries = 64;
293 while (iTries-- > 0)
294 {
295 /* Yield the CPU if we've raced too long. */
296 if (iTries < 4)
297 RTThreadSleep(2 - (iTries & 1));
298
299 /* Try exclusive creation first: */
300 fh = open(pszNativeFilename, fOpenMode | O_EXCL, fMode);
301 iErr = errno;
302 if (fh >= 0)
303 {
304 *penmActionTaken = RTFILEACTION_CREATED;
305 break;
306 }
307 if (iErr != EEXIST)
308 break;
309
310 /* If the file exists, try open it: */
311 fh = open(pszNativeFilename, fOpenMode & ~O_CREAT, fMode);
312 iErr = errno;
313 if (fh >= 0)
314 {
315 if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
316 *penmActionTaken = fOpenMode & O_TRUNC ? RTFILEACTION_TRUNCATED : RTFILEACTION_OPENED;
317 else
318 *penmActionTaken = RTFILEACTION_REPLACED;
319 break;
320 }
321 if (iErr != ENOENT)
322 break;
323 }
324 Assert(iTries >= 0);
325 if (iTries < 0)
326 {
327 /* Thanks for the race, but we need to get on with things. */
328 fh = open(pszNativeFilename, fOpenMode, fMode);
329 iErr = errno;
330 if (fh >= 0)
331 *penmActionTaken = RTFILEACTION_OPENED;
332 }
333 break;
334 }
335
336 default:
337 AssertMsgFailed(("fOpen=%#llx fOpenMode=%#x\n", fOpen, fOpenMode));
338 iErr = EINVAL;
339 fh = -1;
340 break;
341 }
342 }
343
344 rtPathFreeNative(pszNativeFilename, pszFilename);
345 if (fh >= 0)
346 {
347 iErr = 0;
348
349 /*
350 * If temporary file, delete it.
351 */
352 if (fOpen & RTFILE_O_TEMP_AUTO_DELETE)
353 {
354 /** @todo Use funlinkat/funlink or similar here when available! Or better,
355 * use O_TMPFILE, only that may require fallback as not supported by
356 * all file system on linux. */
357 iErr = unlink(pszNativeFilename);
358 Assert(iErr == 0);
359 }
360
361 /*
362 * Mark the file handle close on exec, unless inherit is specified.
363 */
364 if ( !(fOpen & RTFILE_O_INHERIT)
365#ifdef O_NOINHERIT
366 && !(fOpenMode & O_NOINHERIT) /* Take care since it might be a zero value dummy. */
367#endif
368#ifdef O_CLOEXEC
369 && fHave_O_CLOEXEC <= 0
370#endif
371 )
372 iErr = fcntl(fh, F_SETFD, FD_CLOEXEC) >= 0 ? 0 : errno;
373
374 /*
375 * Switch direct I/O on now if requested and required.
376 */
377#if defined(RT_OS_DARWIN) \
378 || (defined(RT_OS_SOLARIS) && !defined(IN_GUEST))
379 if (iErr == 0 && (fOpen & RTFILE_O_NO_CACHE))
380 {
381# if defined(RT_OS_DARWIN)
382 iErr = fcntl(fh, F_NOCACHE, 1) >= 0 ? 0 : errno;
383# else
384 iErr = directio(fh, DIRECTIO_ON) >= 0 ? 0 : errno;
385# endif
386 }
387#endif
388
389 /*
390 * Implement / emulate file sharing.
391 *
392 * We need another mode which allows skipping this stuff completely
393 * and do things the UNIX way. So for the present this is just a debug
394 * aid that can be enabled by developers too lazy to test on Windows.
395 */
396#if 0 && defined(RT_OS_LINUX)
397 if (iErr == 0)
398 {
399 /* This approach doesn't work because only knfsd checks for these
400 buggers. :-( */
401 int iLockOp;
402 switch (fOpen & RTFILE_O_DENY_MASK)
403 {
404 default:
405 AssertFailed();
406 case RTFILE_O_DENY_NONE:
407 case RTFILE_O_DENY_NOT_DELETE:
408 iLockOp = LOCK_MAND | LOCK_READ | LOCK_WRITE;
409 break;
410 case RTFILE_O_DENY_READ:
411 case RTFILE_O_DENY_READ | RTFILE_O_DENY_NOT_DELETE:
412 iLockOp = LOCK_MAND | LOCK_WRITE;
413 break;
414 case RTFILE_O_DENY_WRITE:
415 case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_NOT_DELETE:
416 iLockOp = LOCK_MAND | LOCK_READ;
417 break;
418 case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ:
419 case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ | RTFILE_O_DENY_NOT_DELETE:
420 iLockOp = LOCK_MAND;
421 break;
422 }
423 iErr = flock(fh, iLockOp | LOCK_NB);
424 if (iErr != 0)
425 iErr = errno == EAGAIN ? ETXTBSY : 0;
426 }
427#endif /* 0 && RT_OS_LINUX */
428#if defined(DEBUG_bird) && !defined(RT_OS_SOLARIS)
429 if (iErr == 0)
430 {
431 /* This emulation is incomplete but useful. */
432 switch (fOpen & RTFILE_O_DENY_MASK)
433 {
434 default:
435 AssertFailed();
436 case RTFILE_O_DENY_NONE:
437 case RTFILE_O_DENY_NOT_DELETE:
438 case RTFILE_O_DENY_READ:
439 case RTFILE_O_DENY_READ | RTFILE_O_DENY_NOT_DELETE:
440 break;
441 case RTFILE_O_DENY_WRITE:
442 case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_NOT_DELETE:
443 case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ:
444 case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ | RTFILE_O_DENY_NOT_DELETE:
445 if (fOpen & RTFILE_O_WRITE)
446 {
447 iErr = flock(fh, LOCK_EX | LOCK_NB);
448 if (iErr != 0)
449 iErr = errno == EAGAIN ? ETXTBSY : 0;
450 }
451 break;
452 }
453 }
454#endif
455#ifdef RT_OS_SOLARIS
456 /** @todo Use fshare_t and associates, it's a perfect match. see sys/fcntl.h */
457#endif
458
459 /*
460 * We're done.
461 */
462 if (iErr == 0)
463 {
464 *phFile = (RTFILE)(uintptr_t)fh;
465 Assert((intptr_t)*phFile == fh);
466 LogFlow(("RTFileOpen(%p:{%RTfile}, %p:{%s}, %#llx): returns %Rrc\n",
467 phFile, *phFile, pszFilename, pszFilename, fOpen, rc));
468 return VINF_SUCCESS;
469 }
470
471 close(fh);
472 }
473 return RTErrConvertFromErrno(iErr);
474}
475
476
477RTR3DECL(int) RTFileOpenBitBucket(PRTFILE phFile, uint64_t fAccess)
478{
479 AssertReturn( fAccess == RTFILE_O_READ
480 || fAccess == RTFILE_O_WRITE
481 || fAccess == RTFILE_O_READWRITE,
482 VERR_INVALID_PARAMETER);
483 return RTFileOpen(phFile, "/dev/null", fAccess | RTFILE_O_DENY_NONE | RTFILE_O_OPEN);
484}
485
486
487RTR3DECL(int) RTFileClose(RTFILE hFile)
488{
489 if (hFile == NIL_RTFILE)
490 return VINF_SUCCESS;
491 if (close(RTFileToNative(hFile)) == 0)
492 return VINF_SUCCESS;
493 return RTErrConvertFromErrno(errno);
494}
495
496
497RTR3DECL(int) RTFileFromNative(PRTFILE pFile, RTHCINTPTR uNative)
498{
499 AssertCompile(sizeof(uNative) == sizeof(*pFile));
500 if (uNative < 0)
501 {
502 AssertMsgFailed(("%p\n", uNative));
503 *pFile = NIL_RTFILE;
504 return VERR_INVALID_HANDLE;
505 }
506 *pFile = (RTFILE)uNative;
507 return VINF_SUCCESS;
508}
509
510
511RTR3DECL(RTHCINTPTR) RTFileToNative(RTFILE hFile)
512{
513 AssertReturn(hFile != NIL_RTFILE, -1);
514 return (intptr_t)hFile;
515}
516
517
518RTFILE rtFileGetStandard(RTHANDLESTD enmStdHandle)
519{
520 int fd;
521 switch (enmStdHandle)
522 {
523 case RTHANDLESTD_INPUT: fd = 0; break;
524 case RTHANDLESTD_OUTPUT: fd = 1; break;
525 case RTHANDLESTD_ERROR: fd = 2; break;
526 default:
527 AssertFailedReturn(NIL_RTFILE);
528 }
529
530 struct stat st;
531 int rc = fstat(fd, &st);
532 if (rc == -1)
533 return NIL_RTFILE;
534 return (RTFILE)(intptr_t)fd;
535}
536
537
538RTR3DECL(int) RTFileDelete(const char *pszFilename)
539{
540 char const *pszNativeFilename;
541 int rc = rtPathToNative(&pszNativeFilename, pszFilename, NULL);
542 if (RT_SUCCESS(rc))
543 {
544 if (unlink(pszNativeFilename) != 0)
545 rc = RTErrConvertFromErrno(errno);
546 rtPathFreeNative(pszNativeFilename, pszFilename);
547 }
548 return rc;
549}
550
551
552RTR3DECL(int) RTFileSeek(RTFILE hFile, int64_t offSeek, unsigned uMethod, uint64_t *poffActual)
553{
554 static const unsigned aSeekRecode[] =
555 {
556 SEEK_SET,
557 SEEK_CUR,
558 SEEK_END,
559 };
560
561 /*
562 * Validate input.
563 */
564 if (uMethod > RTFILE_SEEK_END)
565 {
566 AssertMsgFailed(("Invalid uMethod=%d\n", uMethod));
567 return VERR_INVALID_PARAMETER;
568 }
569
570 /* check that within off_t range. */
571 if ( sizeof(off_t) < sizeof(offSeek)
572 && ( (offSeek > 0 && (unsigned)(offSeek >> 32) != 0)
573 || (offSeek < 0 && (unsigned)(-offSeek >> 32) != 0)))
574 {
575 AssertMsgFailed(("64-bit search not supported\n"));
576 return VERR_NOT_SUPPORTED;
577 }
578
579 off_t offCurrent = lseek(RTFileToNative(hFile), (off_t)offSeek, aSeekRecode[uMethod]);
580 if (offCurrent != ~0)
581 {
582 if (poffActual)
583 *poffActual = (uint64_t)offCurrent;
584 return VINF_SUCCESS;
585 }
586 return RTErrConvertFromErrno(errno);
587}
588
589
590RTR3DECL(int) RTFileRead(RTFILE hFile, void *pvBuf, size_t cbToRead, size_t *pcbRead)
591{
592 if (cbToRead <= 0)
593 {
594 if (pcbRead)
595 *pcbRead = 0;
596 return VINF_SUCCESS;
597 }
598
599 /*
600 * Attempt read.
601 */
602 ssize_t cbRead = read(RTFileToNative(hFile), pvBuf, cbToRead);
603 if (cbRead >= 0)
604 {
605 if (pcbRead)
606 /* caller can handle partial read. */
607 *pcbRead = cbRead;
608 else
609 {
610 /* Caller expects all to be read. */
611 while ((ssize_t)cbToRead > cbRead)
612 {
613 ssize_t cbReadPart = read(RTFileToNative(hFile), (char*)pvBuf + cbRead, cbToRead - cbRead);
614 if (cbReadPart <= 0)
615 {
616 if (cbReadPart == 0)
617 return VERR_EOF;
618 return RTErrConvertFromErrno(errno);
619 }
620 cbRead += cbReadPart;
621 }
622 }
623 return VINF_SUCCESS;
624 }
625
626 return RTErrConvertFromErrno(errno);
627}
628
629
630RTR3DECL(int) RTFileWrite(RTFILE hFile, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
631{
632 if (cbToWrite <= 0)
633 return VINF_SUCCESS;
634
635 /*
636 * Attempt write.
637 */
638 ssize_t cbWritten = write(RTFileToNative(hFile), pvBuf, cbToWrite);
639 if (cbWritten >= 0)
640 {
641 if (pcbWritten)
642 /* caller can handle partial write. */
643 *pcbWritten = cbWritten;
644 else
645 {
646 /* Caller expects all to be write. */
647 while ((ssize_t)cbToWrite > cbWritten)
648 {
649 ssize_t cbWrittenPart = write(RTFileToNative(hFile), (const char *)pvBuf + cbWritten, cbToWrite - cbWritten);
650 if (cbWrittenPart <= 0)
651 return cbWrittenPart < 0 ? RTErrConvertFromErrno(errno) : VERR_TRY_AGAIN;
652 cbWritten += cbWrittenPart;
653 }
654 }
655 return VINF_SUCCESS;
656 }
657 return RTErrConvertFromErrno(errno);
658}
659
660
661RTR3DECL(int) RTFileSetSize(RTFILE hFile, uint64_t cbSize)
662{
663 /*
664 * Validate offset.
665 */
666 if ( sizeof(off_t) < sizeof(cbSize)
667 && (cbSize >> 32) != 0)
668 {
669 AssertMsgFailed(("64-bit filesize not supported! cbSize=%lld\n", cbSize));
670 return VERR_NOT_SUPPORTED;
671 }
672
673#if defined(_MSC_VER) || (defined(RT_OS_OS2) && (!defined(__INNOTEK_LIBC__) || __INNOTEK_LIBC__ < 0x006))
674 if (chsize(RTFileToNative(hFile), (off_t)cbSize) == 0)
675#else
676 /* This relies on a non-standard feature of FreeBSD, Linux, and OS/2
677 * LIBC v0.6 and higher. (SuS doesn't define ftruncate() and size bigger
678 * than the file.)
679 */
680 if (ftruncate(RTFileToNative(hFile), (off_t)cbSize) == 0)
681#endif
682 return VINF_SUCCESS;
683 return RTErrConvertFromErrno(errno);
684}
685
686
687RTR3DECL(int) RTFileQuerySize(RTFILE hFile, uint64_t *pcbSize)
688{
689 /*
690 * Ask fstat() first.
691 */
692 struct stat st;
693 if (!fstat(RTFileToNative(hFile), &st))
694 {
695 *pcbSize = st.st_size;
696 if ( st.st_size != 0
697#if defined(RT_OS_SOLARIS) || defined(RT_OS_DARWIN)
698 || (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
699#elif defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD) || defined(RT_OS_DARWIN)
700 || !S_ISCHR(st.st_mode)
701#else
702 || !S_ISBLK(st.st_mode)
703#endif
704 )
705 return VINF_SUCCESS;
706
707 /*
708 * It could be a block device. Try determin the size by I/O control
709 * query or seek.
710 */
711#ifdef RT_OS_DARWIN
712 uint64_t cBlocks;
713 if (!ioctl(RTFileToNative(hFile), DKIOCGETBLOCKCOUNT, &cBlocks))
714 {
715 uint32_t cbBlock;
716 if (!ioctl(RTFileToNative(hFile), DKIOCGETBLOCKSIZE, &cbBlock))
717 {
718 *pcbSize = cBlocks * cbBlock;
719 return VINF_SUCCESS;
720 }
721 }
722
723 /* Always fail block devices. Character devices doesn't all need to be
724 /dev/rdisk* nodes, they should return ENOTTY but /dev/null returns ENODEV
725 and we include EINVAL just in case. */
726 if (!S_ISBLK(st.st_mode) && (errno == ENOTTY || errno == ENODEV || errno == EINVAL))
727 return VINF_SUCCESS;
728
729#elif defined(RT_OS_SOLARIS)
730 struct dk_minfo MediaInfo;
731 if (!ioctl(RTFileToNative(hFile), DKIOCGMEDIAINFO, &MediaInfo))
732 {
733 *pcbSize = MediaInfo.dki_capacity * MediaInfo.dki_lbsize;
734 return VINF_SUCCESS;
735 }
736 /* might not be a block device. */
737 if (errno == EINVAL || errno == ENOTTY)
738 return VINF_SUCCESS;
739
740#elif defined(RT_OS_FREEBSD)
741 off_t cbMedia = 0;
742 if (!ioctl(RTFileToNative(hFile), DIOCGMEDIASIZE, &cbMedia))
743 {
744 *pcbSize = cbMedia;
745 return VINF_SUCCESS;
746 }
747 /* might not be a block device. */
748 if (errno == EINVAL || errno == ENOTTY)
749 return VINF_SUCCESS;
750
751#else
752 /* PORTME! Avoid this path when possible. */
753 uint64_t offSaved = UINT64_MAX;
754 int rc = RTFileSeek(hFile, 0, RTFILE_SEEK_CURRENT, &offSaved);
755 if (RT_SUCCESS(rc))
756 {
757 rc = RTFileSeek(hFile, 0, RTFILE_SEEK_END, pcbSize);
758 int rc2 = RTFileSeek(hFile, offSaved, RTFILE_SEEK_BEGIN, NULL);
759 if (RT_SUCCESS(rc))
760 return rc2;
761 }
762#endif
763 }
764 return RTErrConvertFromErrno(errno);
765}
766
767
768RTR3DECL(int) RTFileQueryMaxSizeEx(RTFILE hFile, PRTFOFF pcbMax)
769{
770 /*
771 * Save the current location
772 */
773 uint64_t offOld = UINT64_MAX;
774 int rc = RTFileSeek(hFile, 0, RTFILE_SEEK_CURRENT, &offOld);
775 if (RT_FAILURE(rc))
776 return rc;
777
778 uint64_t offLow = 0;
779 uint64_t offHigh = INT64_MAX; /* we don't need bigger files */
780 /** @todo Unfortunately this does not work for certain file system types,
781 * for instance cifs mounts. Even worse, statvfs.f_fsid returns 0 for such
782 * file systems. */
783
784 /*
785 * Quickly guess the order of magnitude for offHigh and offLow.
786 */
787 {
788 uint64_t offHighPrev = offHigh;
789 while (offHigh >= INT32_MAX)
790 {
791 rc = RTFileSeek(hFile, offHigh, RTFILE_SEEK_BEGIN, NULL);
792 if (RT_SUCCESS(rc))
793 {
794 offLow = offHigh;
795 offHigh = offHighPrev;
796 break;
797 }
798 else
799 {
800 offHighPrev = offHigh;
801 offHigh >>= 8;
802 }
803 }
804 }
805
806 /*
807 * Sanity: if the seek to the initial offHigh (INT64_MAX) works, then
808 * this algorithm cannot possibly work. Declare defeat.
809 */
810 if (offLow == offHigh)
811 {
812 rc = RTFileSeek(hFile, offOld, RTFILE_SEEK_BEGIN, NULL);
813 if (RT_SUCCESS(rc))
814 rc = VERR_NOT_IMPLEMENTED;
815
816 return rc;
817 }
818
819 /*
820 * Perform a binary search for the max file size.
821 */
822 while (offLow <= offHigh)
823 {
824 uint64_t offMid = offLow + (offHigh - offLow) / 2;
825 rc = RTFileSeek(hFile, offMid, RTFILE_SEEK_BEGIN, NULL);
826 if (RT_FAILURE(rc))
827 offHigh = offMid - 1;
828 else
829 offLow = offMid + 1;
830 }
831
832 if (pcbMax)
833 *pcbMax = RT_MIN(offLow, offHigh);
834 return RTFileSeek(hFile, offOld, RTFILE_SEEK_BEGIN, NULL);
835}
836
837
838RTR3DECL(bool) RTFileIsValid(RTFILE hFile)
839{
840 if (hFile != NIL_RTFILE)
841 {
842 int fFlags = fcntl(RTFileToNative(hFile), F_GETFD);
843 if (fFlags >= 0)
844 return true;
845 }
846 return false;
847}
848
849
850RTR3DECL(int) RTFileFlush(RTFILE hFile)
851{
852 if (!fsync(RTFileToNative(hFile)))
853 return VINF_SUCCESS;
854 /* Ignore EINVAL here as that's what returned for pseudo ttys
855 and other odd handles. */
856 if (errno == EINVAL)
857 return VINF_NOT_SUPPORTED;
858 return RTErrConvertFromErrno(errno);
859}
860
861
862RTR3DECL(int) RTFileIoCtl(RTFILE hFile, unsigned long ulRequest, void *pvData, unsigned cbData, int *piRet)
863{
864 NOREF(cbData);
865 int rc = ioctl(RTFileToNative(hFile), ulRequest, pvData);
866 if (piRet)
867 *piRet = rc;
868 return rc >= 0 ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
869}
870
871
872RTR3DECL(int) RTFileSetMode(RTFILE hFile, RTFMODE fMode)
873{
874 /*
875 * Normalize the mode and call the API.
876 */
877 fMode = rtFsModeNormalize(fMode, NULL, 0, RTFS_TYPE_FILE);
878 if (!rtFsModeIsValid(fMode))
879 return VERR_INVALID_PARAMETER;
880
881 if (fchmod(RTFileToNative(hFile), fMode & RTFS_UNIX_MASK))
882 {
883 int rc = RTErrConvertFromErrno(errno);
884 Log(("RTFileSetMode(%RTfile,%RTfmode): returns %Rrc\n", hFile, fMode, rc));
885 return rc;
886 }
887 return VINF_SUCCESS;
888}
889
890
891RTDECL(int) RTFileSetOwner(RTFILE hFile, uint32_t uid, uint32_t gid)
892{
893 uid_t uidNative = uid != NIL_RTUID ? (uid_t)uid : (uid_t)-1;
894 AssertReturn(uid == uidNative, VERR_INVALID_PARAMETER);
895 gid_t gidNative = gid != NIL_RTGID ? (gid_t)gid : (gid_t)-1;
896 AssertReturn(gid == gidNative, VERR_INVALID_PARAMETER);
897
898 if (fchown(RTFileToNative(hFile), uidNative, gidNative))
899 return RTErrConvertFromErrno(errno);
900 return VINF_SUCCESS;
901}
902
903
904RTR3DECL(int) RTFileRename(const char *pszSrc, const char *pszDst, unsigned fRename)
905{
906 /*
907 * Validate input.
908 */
909 AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
910 AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
911 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
912 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
913 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
914
915 /*
916 * Take common cause with RTPathRename.
917 */
918 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, RTFS_TYPE_FILE);
919
920 LogFlow(("RTDirRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n",
921 pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
922 return rc;
923}
924
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use