4 * Implement a 64-bit file size capable ftruncate() function; GCC-9.x
5 * gratuitously assumes that this is available, via the ftruncate64()
10 * Written by Keith Marshall <keith@users.osdn.me>
11 * Copyright (C) 2020, MinGW.org Project
14 * Permission is hereby granted, free of charge, to any person obtaining a
15 * copy of this software and associated documentation files (the "Software"),
16 * to deal in the Software without restriction, including without limitation
17 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 * and/or sell copies of the Software, and to permit persons to whom the
19 * Software is furnished to do so, subject to the following conditions:
21 * The above copyright notice and this permission notice (including the next
22 * paragraph) shall be included in all copies or substantial portions of the
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31 * DEALINGS IN THE SOFTWARE.
42 /* The following in-line function is provided to facilitate abnormal return
43 * from ftruncate64(), (in which case the return value is always -1), while
44 * setting the global errno indicator to an appropriate error code.
46 static __inline__ __attribute__((__always_inline__))
47 int errout( int error_code ){ errno = error_code; return -1L; }
49 /* When running on Vista, or later, or for applications which have been
50 * linked against non-free MSVCR80.DLL, or later, we may be able to simply
51 * substitute a call to Microsoft's _chsize_s() function, (which behaves
52 * as a 64-bit variant of the universally available _chsize() function).
53 * On legacy Windows versions, which are unlikely to provide _chsize_s(),
54 * we need to provide our own fallback 64-bit chsize() implementation;
55 * this may be static, but cannot be inlined, because we need a physical
56 * entry point address to which execution may be redirected.
58 static int mingw_chsize64_fallback( int fd, __off64_t offset )
60 /* POSIX.1 requires the file pointer to be unchanged, as a consequence
61 * of calling ftruncate(), (and Microsoft's _chsize() functions do seem
62 * to satisfy this requirement); however, to mark a new end of file, we
63 * need move the file pointer to the new end of file offset, so we need
64 * to save the original pointer now, to restore later.
66 __off64_t cur_offset = _lseeki64( fd, 0LL, SEEK_CUR );
68 /* In the event that the new end of file offset requires the file to be
69 * extended beyond its current end of file offset, POSIX.1 also requires
70 * NUL byte padding to be written to the extended file space, (and again,
71 * Microsoft's _chsize() functions seem to do this); we may reposition
72 * the file pointer to its current end of file offset, in preparation
73 * for the possibility that we need to fulfil this requirement.
75 __off64_t end_offset = _lseeki64( fd, 0LL, SEEK_END );
77 /* We will eventually need to restore the original file pointer, AFTER
78 * we have evaluated the return status code, so we will need to save
83 /* There are two possible options for repositioning the end of file
86 if( offset > end_offset )
88 /* In this case, the file is to be extended beyond its current
89 * end of file offset; initialize a NUL filled buffer, which we
90 * may then copy to the extended region of the file, to satisfy
91 * the POSIX.1 requirement that this region shall be NUL filled.
94 memset( padding, 0, sizeof( padding ) );
96 /* Recompute the desired offset, relative to the current end of
97 * file, then repeatedly write copies of the NUL filled buffer,
98 * until the file space represented by this relative offset has
99 * been completely filled; (this results in advancement of the
100 * file pointer to the desired new end of file offset).
102 offset -= end_offset;
103 while( offset > (__off64_t)(sizeof( padding )) )
104 offset -= write( fd, padding, sizeof( padding ) );
105 write( fd, padding, offset );
108 /* In the alternative case, the new end of file pointer will lie
109 * within the space already occupied by the file; we may simply
110 * seek directly to the desired offset.
112 _lseeki64( fd, offset, SEEK_SET );
114 /* We have now adjusted the file pointer to be coincident with the
115 * desired new end of file offset; this is exactly what is required
116 * by the Windows API function, to mark the new end of file.
118 retval = SetEndOfFile( (void *)(_get_osfhandle( fd )) )
119 ? 0 : errout( EBADF );
121 /* Finally, we must restore the originally saved file pointer, before
122 * we return the status code from the ftruncate() operation.
124 _lseeki64( fd, cur_offset, SEEK_SET );
128 /* Regardless of the platform version, Microsoft do not provide an
129 * implementation of ftruncate64(); all link-time references to this
130 * function will be resolved by this libmingwex.a implementation.
132 int ftruncate64( int fd, __off64_t offset )
134 /* The offset parameter MUST be positive valued; bail out if not.
136 if( 0LL > offset ) return errout( EINVAL );
138 /* For offsets which may be represented by a 32-bit integer, we
139 * may ALWAYS delegate this call to Microsoft's _chsize().
141 if( INT32_MAX >= offset ) return _chsize( fd, (off_t)(offset) );
143 { /* For offsets which cannot be represented within 32-bits, we
144 * MAY be able to delegate this call, (and also any subsequent
145 * calls), to Microsoft's _chsize_s(); set up a redirector to
146 * handle such delegation...
148 static int (*redirector_hook)( int, __off64_t ) = NULL;
150 /* ...initially checking for _chsize_s() availability...
152 if( (redirector_hook == NULL)
153 && ((redirector_hook = dlsym( RTLD_DEFAULT, "_chsize_s" )) == NULL) )
155 /* ...and setting up a suitable fallback if not...
157 redirector_hook = mingw_chsize64_fallback;
159 /* ...and ultimately, on initial selection, (and directly on
160 * all subsequent calls), hand off execution to the selected
163 return redirector_hook( fd, offset );
167 /* $RCSfile$: end of file */