1 //
2 // chsize.cpp
3 //
4 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //
6 // Defines _chsize() and _chsize_s(), which change the size of a file.
7 //
8 #include <corecrt_internal_lowio.h>
9 #include <corecrt_internal_ptd_propagation.h>
10
11
12 // Changes the size of the given file, either extending or truncating it. The
13 // file must have been opened with write permissions or this will fail. Returns
14 // 0 on success; returns an errno error code on failure,
_chsize_s_internal(int const fh,__int64 const size,__crt_cached_ptd_host & ptd)15 static errno_t __cdecl _chsize_s_internal(int const fh, __int64 const size, __crt_cached_ptd_host& ptd)
16 {
17 _UCRT_CHECK_FH_CLEAR_OSSERR_RETURN_ERRCODE(ptd, fh, EBADF);
18 _UCRT_VALIDATE_CLEAR_OSSERR_RETURN_ERRCODE(ptd, (fh >= 0 && (unsigned)fh < (unsigned)_nhandle), EBADF);
19 _UCRT_VALIDATE_CLEAR_OSSERR_RETURN_ERRCODE(ptd, (_osfile(fh) & FOPEN), EBADF);
20 _UCRT_VALIDATE_CLEAR_OSSERR_RETURN_ERRCODE(ptd, (size >= 0), EINVAL);
21
22 return __acrt_lowio_lock_fh_and_call(fh, [&]()
23 {
24 if (_osfile(fh) & FOPEN)
25 {
26 return _chsize_nolock_internal(fh, size, ptd);
27 }
28 else
29 {
30 _ASSERTE(("Invalid file descriptor. File possibly closed by a different thread", 0));
31 return ptd.get_errno().set(EBADF);
32 }
33 });
34 }
35
_chsize_s(int const fh,__int64 const size)36 extern "C" errno_t __cdecl _chsize_s(int const fh, __int64 const size)
37 {
38 __crt_cached_ptd_host ptd;
39 return _chsize_s_internal(fh, size, ptd);
40 }
41
42 struct __crt_seek_guard
43 {
44
__crt_seek_guard__crt_seek_guard45 __crt_seek_guard(int const fh, __int64 const size)
46 : place(_lseeki64_nolock(fh, 0, SEEK_CUR)),
47 end(_lseeki64_nolock(fh, 0, SEEK_END)),
48 extend(size - end),
49 fhh(fh)
50 {
51 }
52
~__crt_seek_guard__crt_seek_guard53 ~__crt_seek_guard()
54 {
55 _lseeki64_nolock(fhh, place, SEEK_SET);
56 }
57
58 __crt_seek_guard(__crt_seek_guard const &) = delete;
59 __crt_seek_guard& operator=(__crt_seek_guard const &) = delete;
60
61 __int64 place;
62 __int64 end;
63 __int64 extend;
64 int fhh;
65 };
66
_chsize_nolock_internal(int const fh,__int64 const size,__crt_cached_ptd_host & ptd)67 extern "C" errno_t __cdecl _chsize_nolock_internal(int const fh, __int64 const size, __crt_cached_ptd_host& ptd)
68 {
69 // Get current file position and seek to end
70 __crt_seek_guard seek_guard(fh, size);
71
72 if (seek_guard.place == -1 || seek_guard.end == -1)
73 {
74 // EBADF if earlier lseek (in __crt_seek_guard) failed
75 // EINVAL otherwise (ex: too large of a offset)
76 return ptd.get_errno().value_or(EINVAL);
77 }
78
79 // Grow or shrink the file as necessary:
80 if (seek_guard.extend > 0)
81 {
82 // Extend the file by filling the new area with zeroes:
83 __crt_unique_heap_ptr<char> const zero_buffer(_calloc_crt_t(char, _INTERNAL_BUFSIZ));
84 if (!zero_buffer)
85 {
86 return ptd.get_errno().set(ENOMEM);
87 }
88
89 int const old_mode = _setmode_nolock(fh, _O_BINARY);
90
91 do
92 {
93 int const bytes_to_write = seek_guard.extend >= static_cast<__int64>(_INTERNAL_BUFSIZ)
94 ? _INTERNAL_BUFSIZ
95 : static_cast<int>(seek_guard.extend);
96
97 int const bytes_written = _write_nolock(fh, zero_buffer.get(), bytes_to_write, ptd);
98 if (bytes_written == -1)
99 {
100 // Error on write:
101 if (ptd.get_doserrno().check(ERROR_ACCESS_DENIED))
102 {
103 ptd.get_errno().set(EACCES);
104 }
105
106 return ptd.get_errno().value_or(0);
107 }
108
109 seek_guard.extend -= bytes_written;
110 } while (seek_guard.extend > 0);
111
112 #pragma warning(suppress:6031) // return value ignored
113 _setmode_nolock(fh, old_mode);
114 }
115 else if (seek_guard.extend < 0)
116 {
117 // Shorten the file by truncating it:
118 __int64 const new_end = _lseeki64_nolock(fh, size, SEEK_SET);
119 if (new_end == -1)
120 {
121 return ptd.get_errno().value_or(0);
122 }
123
124 if (!SetEndOfFile(reinterpret_cast<HANDLE>(_get_osfhandle(fh))))
125 {
126 ptd.get_doserrno().set(GetLastError());
127 return ptd.get_errno().set(EACCES);
128 }
129 }
130
131 return 0;
132 }
133
_chsize_nolock(int const fh,__int64 const size)134 extern "C" errno_t __cdecl _chsize_nolock(int const fh, __int64 const size)
135 { // TODO: _chsize_nolock is already internal-only.
136 // Once PTD is propagated everywhere, rename _chsize_nolock_internal to _chsize_nolock.
137 __crt_cached_ptd_host ptd;
138 return _chsize_nolock_internal(fh, size, ptd);
139 }
140
141
142 // Changes the size of the given file, either extending or truncating it. The
143 // file must have been opened with write permissions or this will fail. Returns
144 // 0 on success; returns -1 and sets errno on failure.
_chsize(int const fh,long const size)145 extern "C" int __cdecl _chsize(int const fh, long const size)
146 {
147 return _chsize_s(fh, size) == 0 ? 0 : -1;
148 }
149