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