xref: /reactos/sdk/lib/ucrt/lowio/chsize.cpp (revision 04e0dc4a)
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