1 /*-
2 * Copyright (c) 2000, 2001, 2002 Lev Walkin <vlm@lionet.info>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $Id: filepos.c,v 1.4 2006/12/11 07:11:52 vlm Exp $
27 */
28
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <errno.h>
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43
44 #ifdef HAVE_TIME_H
45 #include <time.h>
46 #endif
47
48 #ifdef HAVE_SYS_TIME_H
49 #include <sys/time.h>
50 #endif
51
52 #ifdef HAVE_FCNTL_H
53 #include <fcntl.h>
54 #endif
55
56 #ifdef HAVE_SYS_LOCKING_H
57 #include <sys/locking.h>
58 #endif
59
60 #ifdef HAVE_ALLOCA_H
61 #include <alloca.h>
62 #endif
63
64 #if !defined(HAVE_FCNTL) && !defined(HAVE_LOCKF) && defined(HAVE__LOCKING)
65 # define lockf _locking
66 # define HAVE_LOCKF 1
67 #endif
68
69 #ifndef F_TLOCK
70 # ifdef _LK_NBLCK
71 # define F_TLOCK _LK_NBLCK
72 # endif
73 #endif
74
75 #include <sf_fpos.h>
76 #include <sf_mem.h>
77
78 int
initfdpos(const char * filename,int stream,const char * ext)79 initfdpos(const char *filename, int stream, const char *ext) {
80 char *pos_fname;
81 int posfd;
82 off_t ofs = 0;
83 ino_t ino = 0;
84 time_t tloc;
85 struct stat sb;
86 int flen, elen;
87 #ifdef HAVE_FCNTL
88 struct flock flk;
89 #endif /* HAVE_FCNTL */
90
91
92 if(filename == NULL || *filename == '\0'
93 || ext == NULL || *ext == '\0') {
94 errno = EINVAL;
95 return -1;
96 }
97
98 flen = strlen(filename);
99 elen = strlen(ext);
100
101 pos_fname = (char *)alloca(flen + elen + 2);
102
103 /* filename.ext */
104 memcpy(pos_fname, filename, flen);
105 pos_fname[flen] = '.';
106 memcpy(pos_fname + flen + 1, ext, elen + 1);
107 /* it will be naturally NUL-terminated because of elen + 1 */
108
109 posfd = open(pos_fname, O_RDWR | O_CREAT, 0644);
110 if(posfd == -1)
111 return -1;
112
113 #ifdef HAVE_FCNTL
114 memset(&flk, 0, sizeof(flk));
115 flk.l_type = F_WRLCK;
116 flk.l_whence = SEEK_SET;
117
118 if(fcntl(posfd, F_SETLK, &flk) == -1) {
119 close(posfd);
120 return -1;
121 }
122 #elif defined(HAVE_LOCKF)
123 if(lockf(posfd, F_TLOCK, sizeof(ino_t) + sizeof(off_t)) != 0) {
124 close(posfd);
125 return -1;
126 }
127 #else
128 #warning "File locking unavailable. filepos(3) may malfunction"
129 #endif /* HAVE_FCNTL */
130
131 if(fstat(posfd, &sb)) {
132 close(posfd);
133 return -1;
134 }
135
136 if((sb.st_mode & S_IFMT) != S_IFREG) {
137 close(posfd);
138 return -1;
139 }
140
141 time(&tloc);
142
143 lseek(posfd, 0, SEEK_SET);
144
145 if( (sb.st_size == 0)
146 || ( (sb.st_size != sizeof(ino_t) + sizeof(off_t))
147 && (sb.st_size % (sizeof(ino_t) + sizeof(off_t) + sizeof(time_t))) )
148 ) {
149
150 fstat(stream, &sb);
151 ino = sb.st_ino;
152 ofs = 0;
153
154 write(posfd, &ino, sizeof(ino_t));
155 write(posfd, &ofs, sizeof(off_t));
156 write(posfd, &tloc, sizeof(time_t));
157
158 } else {
159 /* If could not read basic info */
160 if(read(posfd, &ino, sizeof(ino_t)) != sizeof(ino_t)
161 || read(posfd, &ofs, sizeof(off_t)) != sizeof(off_t))
162 return -1;
163
164 fstat(stream, &sb);
165
166 if((ino != sb.st_ino) || (ofs > sb.st_size)) {
167 ino = sb.st_ino;
168 ofs = 0;
169 lseek(posfd, 0, SEEK_SET);
170 write(posfd, &ino, sizeof(ino_t));
171 write(posfd, &ofs, sizeof(off_t));
172 write(posfd, &tloc, sizeof(time_t));
173 }
174 }
175
176 lseek(stream, ofs, SEEK_SET);
177
178 return posfd;
179 }
180
181 static int
_sf_writeposfd(off_t offset,int posfd,int doSync)182 _sf_writeposfd(off_t offset, int posfd, int doSync) {
183
184 if(offset < 0 || posfd == -1) {
185 errno = EINVAL;
186 return -1;
187 }
188
189 if(lseek(posfd, sizeof(ino_t), SEEK_SET) == -1)
190 return -1;
191
192 if(write(posfd, &offset, sizeof(off_t)) != sizeof(off_t))
193 return -1;
194
195 if(doSync) {
196 time_t tloc;
197 time(&tloc);
198
199 write(posfd, &tloc, sizeof(time_t));
200
201 #ifdef HAVE_FSYNC
202 fsync(posfd);
203 #endif
204 }
205
206 return 0;
207 }
208
209 int
adjfdpos(int stream,int posfd,int doSync)210 adjfdpos(int stream, int posfd, int doSync) {
211
212 /* Determine current offset and write it to the position file */
213
214 return _sf_writeposfd(
215 lseek(stream, 0, SEEK_CUR),
216 posfd, doSync);
217
218 return 0;
219 }
220
221 int
initFILEpos(const char * filename,FILE * stream,const char * ext)222 initFILEpos(const char *filename, FILE *stream, const char *ext) {
223 int posfd;
224 off_t ofs;
225 off_t ols;
226
227 if(stream == NULL) {
228 errno = EINVAL;
229 return -1;
230 }
231
232 ols = lseek(fileno(stream), 0, SEEK_CUR);
233
234 posfd=initfdpos(filename, fileno(stream), ext);
235 if(posfd == -1)
236 return -1;
237
238 ofs = lseek(fileno(stream), 0, SEEK_CUR);
239 if(ofs == -1)
240 return -1;
241
242 /* Rewind it where it should be. For transparency. */
243 lseek(fileno(stream), ols, SEEK_SET);
244
245 /* Advance a FILE position */
246 if(fseek(stream, ofs, SEEK_SET) == -1)
247 return -1;
248
249 return posfd;
250 }
251
252 int
adjFILEpos(FILE * stream,int posfd,int doSync)253 adjFILEpos(FILE *stream, int posfd, int doSync) {
254 off_t ofs;
255 off_t newofs;
256 long fofs;
257
258 if(stream == NULL) {
259 errno = EINVAL;
260 return -1;
261 }
262
263 ofs = lseek(fileno(stream), 0, SEEK_CUR);
264 fofs = ftell(stream);
265
266 lseek(fileno(stream), fofs, SEEK_SET);
267
268 if(adjfdpos(fileno(stream), posfd, doSync) == -1)
269 return -1;
270
271 newofs = lseek(fileno(stream), 0, SEEK_CUR);
272
273 /* Rewind it where it should be. For transparency. */
274 lseek(fileno(stream), ofs, SEEK_SET);
275
276 if(fseek(stream, newofs, SEEK_SET) == -1)
277 return -1;
278
279 return 0;
280 }
281
282
283
284