1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "apr_arch_file_io.h"
18 #include "apr_file_io.h"
19 #include <errno.h>
20 #include <string.h>
21 
setptr(apr_file_t * thefile,apr_off_t pos)22 static apr_status_t setptr(apr_file_t *thefile, apr_off_t pos )
23 {
24     apr_off_t newbufpos;
25     apr_status_t rv;
26     DWORD rc;
27 
28     if (thefile->direction == 1) {
29         /* XXX: flush here is not mutex protected */
30         rv = apr_file_flush(thefile);
31         if (rv != APR_SUCCESS)
32             return rv;
33         thefile->bufpos = thefile->dataRead = 0;
34         thefile->direction = 0;
35     }
36 
37     /* We may be truncating to size here.
38      * XXX: testing an 'unsigned' as >= 0 below indicates a bug
39      */
40     newbufpos = pos - (thefile->filePtr - thefile->dataRead);
41 
42     if (newbufpos >= 0 && newbufpos <= (apr_off_t)thefile->dataRead) {
43         thefile->bufpos = (apr_size_t)newbufpos;
44         rv = APR_SUCCESS;
45     } else {
46         DWORD offlo = (DWORD)pos;
47         LONG  offhi = (LONG)(pos >> 32);
48         rc = SetFilePointer(thefile->filehand, offlo, &offhi, FILE_BEGIN);
49 
50         if (rc == (DWORD)-1)
51             /* A legal value, perhaps?  MSDN implies prior SetLastError isn't
52              * needed, googling for SetLastError SetFilePointer seems
53              * to confirm this.  INVALID_SET_FILE_POINTER is too recently
54              * added for us to rely on it as a constant.
55              */
56             rv = apr_get_os_error();
57         else
58             rv = APR_SUCCESS;
59 
60         if (rv == APR_SUCCESS) {
61             rv = APR_SUCCESS;
62             thefile->eof_hit = 0;
63             thefile->bufpos = thefile->dataRead = 0;
64             thefile->filePtr = pos;
65         }
66     }
67 
68     return rv;
69 }
70 
71 
apr_file_seek(apr_file_t * thefile,apr_seek_where_t where,apr_off_t * offset)72 APR_DECLARE(apr_status_t) apr_file_seek(apr_file_t *thefile, apr_seek_where_t where, apr_off_t *offset)
73 {
74     apr_finfo_t finfo;
75     apr_status_t rc = APR_SUCCESS;
76 
77     thefile->eof_hit = 0;
78 
79     if (thefile->buffered) {
80         switch (where) {
81             case APR_SET:
82                 rc = setptr(thefile, *offset);
83                 break;
84 
85             case APR_CUR:
86                 rc = setptr(thefile, thefile->filePtr - thefile->dataRead
87                                       + thefile->bufpos + *offset);
88                 break;
89 
90             case APR_END:
91                 rc = apr_file_info_get(&finfo, APR_FINFO_SIZE, thefile);
92                 if (rc == APR_SUCCESS)
93                     rc = setptr(thefile, finfo.size + *offset);
94                 break;
95 
96             default:
97                 return APR_EINVAL;
98         }
99 
100         *offset = thefile->filePtr - thefile->dataRead + thefile->bufpos;
101         return rc;
102     }
103     /* A file opened with APR_FOPEN_XTHREAD has been opened for overlapped i/o.
104      * APR must explicitly track the file pointer in this case.
105      */
106     else if (thefile->pOverlapped || thefile->flags & APR_FOPEN_XTHREAD) {
107         switch(where) {
108             case APR_SET:
109                 thefile->filePtr = *offset;
110                 break;
111 
112             case APR_CUR:
113                 thefile->filePtr += *offset;
114                 break;
115 
116             case APR_END:
117                 rc = apr_file_info_get(&finfo, APR_FINFO_SIZE, thefile);
118                 if (rc == APR_SUCCESS && finfo.size + *offset >= 0)
119                     thefile->filePtr = finfo.size + *offset;
120                 break;
121 
122             default:
123                 return APR_EINVAL;
124         }
125         *offset = thefile->filePtr;
126         return rc;
127     }
128     else {
129         DWORD howmove;
130         DWORD offlo = (DWORD)*offset;
131         DWORD offhi = (DWORD)(*offset >> 32);
132 
133         switch(where) {
134             case APR_SET:
135                 howmove = FILE_BEGIN;   break;
136             case APR_CUR:
137                 howmove = FILE_CURRENT; break;
138             case APR_END:
139                 howmove = FILE_END;     break;
140             default:
141                 return APR_EINVAL;
142         }
143         offlo = SetFilePointer(thefile->filehand, (LONG)offlo,
144                                (LONG*)&offhi, howmove);
145         if (offlo == 0xFFFFFFFF)
146             rc = apr_get_os_error();
147         else
148             rc = APR_SUCCESS;
149         /* Since we can land at 0xffffffff we will measure our APR_SUCCESS */
150         if (rc == APR_SUCCESS)
151             *offset = ((apr_off_t)offhi << 32) | offlo;
152         return rc;
153     }
154 }
155 
156 
apr_file_trunc(apr_file_t * thefile,apr_off_t offset)157 APR_DECLARE(apr_status_t) apr_file_trunc(apr_file_t *thefile, apr_off_t offset)
158 {
159     apr_status_t rv;
160     DWORD offlo = (DWORD)offset;
161     LONG  offhi = (LONG)(offset >> 32);
162     DWORD rc;
163 
164     if (thefile->buffered) {
165         if (thefile->direction == 1) {
166             /* Figure out what needs to be flushed.  Don't flush the part
167              * of the write buffer that will get truncated anyway.
168              */
169             if (offset < thefile->filePtr) {
170                 thefile->bufpos = 0;
171             }
172             else if (offset < thefile->filePtr + (apr_off_t)thefile->bufpos) {
173                 thefile->bufpos = offset - thefile->filePtr;
174             }
175 
176             if (thefile->bufpos != 0) {
177                 rv = apr_file_flush(thefile);
178                 if (rv != APR_SUCCESS)
179                     return rv;
180             }
181         }
182         else if (thefile->direction == 0) {
183             /* Discard the read buffer, as we are about to reposition
184              * ourselves to the end of file.
185              */
186             thefile->bufpos = 0;
187             thefile->dataRead = 0;
188         }
189     }
190 
191     rc = SetFilePointer(thefile->filehand, offlo, &offhi, FILE_BEGIN);
192     if (rc == 0xFFFFFFFF)
193         if ((rv = apr_get_os_error()) != APR_SUCCESS)
194             return rv;
195     thefile->filePtr = offset;
196     /* Don't report EOF until the next read. */
197     thefile->eof_hit = 0;
198 
199     if (!SetEndOfFile(thefile->filehand))
200         return apr_get_os_error();
201 
202     return APR_SUCCESS;
203 }
204