1 /* $Id$ */
2 /*
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <pj/file_io.h>
21 #include <pj/unicode.h>
22 #include <pj/errno.h>
23 #include <pj/assert.h>
24 #include <pj/string.h>
25 
26 #include <windows.h>
27 
28 #ifndef INVALID_SET_FILE_POINTER
29 #   define INVALID_SET_FILE_POINTER     ((DWORD)-1)
30 #endif
31 
set_file_pointer(pj_oshandle_t fd,pj_off_t offset,pj_off_t * newPos,DWORD dwMoveMethod)32 static pj_status_t set_file_pointer(pj_oshandle_t fd,
33     pj_off_t offset,
34     pj_off_t* newPos,
35     DWORD dwMoveMethod)
36 {
37 #if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8
38     LARGE_INTEGER liDistance, liNewPos;
39 
40     liDistance.QuadPart = offset;
41     if (!SetFilePointerEx(fd, liDistance, &liNewPos, dwMoveMethod)) {
42 	return PJ_RETURN_OS_ERROR(GetLastError());
43     }
44     *newPos = liNewPos.QuadPart;
45 #else
46     DWORD dwNewPos;
47     LONG  hi32;
48 
49     hi32 = (LONG)(offset >> 32);
50 
51     dwNewPos = SetFilePointer(fd, (long)offset, &hi32, dwMoveMethod);
52     if (dwNewPos == (DWORD)INVALID_SET_FILE_POINTER) {
53 	DWORD dwStatus = GetLastError();
54 	if (dwStatus != 0)
55 	    return PJ_RETURN_OS_ERROR(dwStatus);
56 	/* dwNewPos actually is not an error. */
57     }
58     *newPos = hi32;
59     *newPos = (*newPos << 32) + dwNewPos;
60 #endif
61 
62     return PJ_SUCCESS;
63 }
64 
65 /**
66  * Check for end-of-file condition on the specified descriptor.
67  *
68  * @param fd            The file descriptor.
69  * @param access        The desired access.
70  *
71  * @return              Non-zero if file is EOF.
72  */
73 PJ_DECL(pj_bool_t) pj_file_eof(pj_oshandle_t fd,
74                                enum pj_file_access access);
75 
76 
pj_file_open(pj_pool_t * pool,const char * pathname,unsigned flags,pj_oshandle_t * fd)77 PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool,
78                                   const char *pathname,
79                                   unsigned flags,
80                                   pj_oshandle_t *fd)
81 {
82     PJ_DECL_UNICODE_TEMP_BUF(wpathname, 256)
83     HANDLE hFile;
84     DWORD dwDesiredAccess = 0;
85     DWORD dwShareMode = 0;
86     DWORD dwCreationDisposition = 0;
87     DWORD dwFlagsAndAttributes = 0;
88 
89     PJ_UNUSED_ARG(pool);
90 
91     PJ_ASSERT_RETURN(pathname!=NULL, PJ_EINVAL);
92 
93     if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) {
94         dwDesiredAccess |= GENERIC_WRITE;
95         if ((flags & PJ_O_APPEND) == PJ_O_APPEND) {
96 #if !defined(PJ_WIN32_WINCE) || !PJ_WIN32_WINCE
97 	    /* FILE_APPEND_DATA is invalid on WM2003 and WM5, but it seems
98 	     * to be working on WM6. All are tested on emulator though.
99 	     * Removing this also seem to work (i.e. data is appended), so
100 	     * I guess this flag is "optional".
101 	     * See http://trac.pjsip.org/repos/ticket/825
102 	     */
103             dwDesiredAccess |= FILE_APPEND_DATA;
104 #endif
105 	    dwCreationDisposition |= OPEN_ALWAYS;
106         } else {
107             dwDesiredAccess &= ~(FILE_APPEND_DATA);
108             dwCreationDisposition |= CREATE_ALWAYS;
109         }
110     }
111     if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) {
112         dwDesiredAccess |= GENERIC_READ;
113         if (flags == PJ_O_RDONLY)
114             dwCreationDisposition |= OPEN_EXISTING;
115     }
116 
117     if (dwDesiredAccess == 0) {
118         pj_assert(!"Invalid file open flags");
119         return PJ_EINVAL;
120     }
121 
122     dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
123 
124     dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
125 
126 #if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8
127     hFile = CreateFile2(PJ_STRING_TO_NATIVE(pathname,
128 			wpathname, sizeof(wpathname)),
129 			dwDesiredAccess, dwShareMode, dwCreationDisposition,
130 			NULL);
131 #else
132     hFile = CreateFile(PJ_STRING_TO_NATIVE(pathname,
133 		       wpathname, sizeof(wpathname)),
134 		       dwDesiredAccess, dwShareMode, NULL,
135 		       dwCreationDisposition, dwFlagsAndAttributes, NULL);
136 #endif
137 
138     if (hFile == INVALID_HANDLE_VALUE) {
139 	DWORD lastErr = GetLastError();
140         *fd = 0;
141         return PJ_RETURN_OS_ERROR(lastErr);
142     }
143 
144     if ((flags & PJ_O_APPEND) == PJ_O_APPEND) {
145 	pj_status_t status;
146 
147 	status = pj_file_setpos(hFile, 0, PJ_SEEK_END);
148 	if (status != PJ_SUCCESS) {
149 	    pj_file_close(hFile);
150 	    return status;
151 	}
152     }
153 
154     *fd = hFile;
155     return PJ_SUCCESS;
156 }
157 
pj_file_close(pj_oshandle_t fd)158 PJ_DEF(pj_status_t) pj_file_close(pj_oshandle_t fd)
159 {
160     if (CloseHandle(fd)==0)
161         return PJ_RETURN_OS_ERROR(GetLastError());
162     return PJ_SUCCESS;
163 }
164 
pj_file_write(pj_oshandle_t fd,const void * data,pj_ssize_t * size)165 PJ_DEF(pj_status_t) pj_file_write( pj_oshandle_t fd,
166                                    const void *data,
167                                    pj_ssize_t *size)
168 {
169     BOOL rc;
170     DWORD bytesWritten;
171 
172     rc = WriteFile(fd, data, (DWORD)*size, &bytesWritten, NULL);
173     if (!rc) {
174         *size = -1;
175         return PJ_RETURN_OS_ERROR(GetLastError());
176     }
177 
178     *size = bytesWritten;
179     return PJ_SUCCESS;
180 }
181 
pj_file_read(pj_oshandle_t fd,void * data,pj_ssize_t * size)182 PJ_DEF(pj_status_t) pj_file_read( pj_oshandle_t fd,
183                                   void *data,
184                                   pj_ssize_t *size)
185 {
186     BOOL rc;
187     DWORD bytesRead;
188 
189     rc = ReadFile(fd, data, (DWORD)*size, &bytesRead, NULL);
190     if (!rc) {
191         *size = -1;
192         return PJ_RETURN_OS_ERROR(GetLastError());
193     }
194 
195     *size = bytesRead;
196     return PJ_SUCCESS;
197 }
198 
199 /*
200 PJ_DEF(pj_bool_t) pj_file_eof(pj_oshandle_t fd, enum pj_file_access access)
201 {
202     BOOL rc;
203     DWORD dummy = 0, bytes;
204     DWORD dwStatus;
205 
206     if ((access & PJ_O_RDONLY) == PJ_O_RDONLY) {
207         rc = ReadFile(fd, &dummy, 0, &bytes, NULL);
208     } else if ((access & PJ_O_WRONLY) == PJ_O_WRONLY) {
209         rc = WriteFile(fd, &dummy, 0, &bytes, NULL);
210     } else {
211         pj_assert(!"Invalid access");
212         return PJ_TRUE;
213     }
214 
215     dwStatus = GetLastError();
216     if (dwStatus==ERROR_HANDLE_EOF)
217         return PJ_TRUE;
218 
219     return 0;
220 }
221 */
222 
pj_file_setpos(pj_oshandle_t fd,pj_off_t offset,enum pj_file_seek_type whence)223 PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd,
224                                     pj_off_t offset,
225                                     enum pj_file_seek_type whence)
226 {
227     DWORD dwMoveMethod;
228     pj_off_t newPos;
229 
230     if (whence == PJ_SEEK_SET)
231         dwMoveMethod = FILE_BEGIN;
232     else if (whence == PJ_SEEK_CUR)
233         dwMoveMethod = FILE_CURRENT;
234     else if (whence == PJ_SEEK_END)
235         dwMoveMethod = FILE_END;
236     else {
237         pj_assert(!"Invalid whence in file_setpos");
238         return PJ_EINVAL;
239     }
240 
241     if (set_file_pointer(fd, offset, &newPos, dwMoveMethod) != PJ_SUCCESS) {
242 	return PJ_RETURN_OS_ERROR(GetLastError());
243     }
244 
245     return PJ_SUCCESS;
246 }
247 
pj_file_getpos(pj_oshandle_t fd,pj_off_t * pos)248 PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd,
249                                     pj_off_t *pos)
250 {
251     if (set_file_pointer(fd, 0, pos, FILE_CURRENT) != PJ_SUCCESS) {
252 	return PJ_RETURN_OS_ERROR(GetLastError());
253     }
254 
255     return PJ_SUCCESS;
256 }
257 
pj_file_flush(pj_oshandle_t fd)258 PJ_DEF(pj_status_t) pj_file_flush(pj_oshandle_t fd)
259 {
260     BOOL rc;
261 
262     rc = FlushFileBuffers(fd);
263 
264     if (!rc) {
265 	DWORD dwStatus = GetLastError();
266         if (dwStatus != 0)
267             return PJ_RETURN_OS_ERROR(dwStatus);
268     }
269 
270     return PJ_SUCCESS;
271 }
272