1 /* @(#)filesubs.c	1.65 19/01/07 Copyright 1984-2019 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)filesubs.c	1.65 19/01/07 Copyright 1984-2019 J. Schilling";
6 #endif
7 /*
8  *	Commands that deal with non i/o file subroutines
9  *
10  *	Copyright (c) 1984-2019 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 #include <schily/unistd.h>
27 #include <schily/fcntl.h>
28 #include <schily/errno.h>
29 #include "ved.h"
30 
31 EXPORT	void	lockfile	__PR((ewin_t *wp, char *filename));
32 EXPORT	void	writelockmsg	__PR((ewin_t *wp));
33 LOCAL	int	check_lock	__PR((void));
34 EXPORT	int	lockfd		__PR((int f));
35 LOCAL	int	lock_fcntl	__PR((int f));
36 EXPORT	int	getfmodes	__PR((Uchar* name));
37 EXPORT	long	gftime		__PR((char *file));
38 EXPORT	BOOL	readable	__PR((Uchar* name));
39 EXPORT	BOOL	writable	__PR((Uchar* name));
40 EXPORT	BOOL	wrtcheck	__PR((ewin_t *wp, BOOL  err));
41 EXPORT	BOOL	modcheck	__PR((ewin_t *wp));
42 
43 /*
44  * Used when no locking interface is available.
45  */
46 #ifdef	ENOSYS
47 #	define	NO_LOCK	ENOSYS
48 #else
49 #	define	NO_LOCK	EINVAL
50 #endif
51 #define	no_lock()	(seterrno(NO_LOCK))
52 
53 /*
54  * Lock the current default file.
55  */
56 EXPORT void
lockfile(wp,filename)57 lockfile(wp, filename)
58 	ewin_t	*wp;
59 	char	*filename;
60 {
61 	int	f = open(filename, O_RDWR);
62 	int	ret;
63 
64 	if (f < 0) {	/* New file ??? */
65 		if (geterrno() != ENOENT)
66 			wp->eflags |= FNOLOCK;
67 		return;
68 	}
69 
70 	ret = lockfd(f);
71 
72 	switch (ret) {
73 
74 	case LOCK_LOCAL:
75 		wp->eflags |= FLOCKLOCAL;
76 		/* FALLTHRU */
77 	case LOCK_OK:
78 		wp->curfd = f;
79 		break;
80 	case LOCK_ALREADY:
81 		close(f);
82 		wp->eflags |= FREADONLY;
83 		wp->eflags |= FNOLOCK;
84 		break;
85 	case LOCK_CANNOT:
86 		close(f);
87 		wp->eflags |= FNOLOCK;
88 		break;
89 	}
90 }
91 
92 /*
93  * Write an error message according to the current lock status.
94  * We cannot do this in lockfile() because after the file is read,
95  * the system line is cleared.
96  */
97 EXPORT void
writelockmsg(wp)98 writelockmsg(wp)
99 	ewin_t	*wp;
100 {
101 	if ((wp->eflags & (FREADONLY|FNOLOCK)) == (FREADONLY|FNOLOCK))
102 		writeerr(wp, "FILE ALREADY LOCKED");
103 	else if ((wp->eflags & FNOLOCK) != 0)
104 		writeerr(wp, "CANNOT LOCK FILE");
105 	else if ((wp->eflags & FLOCKLOCAL) != 0)
106 		writeserr(wp, "LOCK IS LOCAL ONLY");
107 }
108 
109 /*
110  * Check whether the lock was successful.
111  */
112 LOCAL int
check_lock()113 check_lock()
114 {
115 	/*
116 	 * We need to distinguish a lock not being available for the file
117 	 * from the file system not supporting locking.
118 	 * Fcntl is documented to return EACCESS and EAGAIN if file is already
119 	 * locked. We need to add EWOULDBLOCK as *BSD returns this undocumented
120 	 * value.
121 	 */
122 	return (geterrno() == EACCES || geterrno() == EAGAIN
123 #ifdef EWOULDBLOCK
124 						|| geterrno() == EWOULDBLOCK
125 #endif
126 	?  LOCK_ALREADY : LOCK_CANNOT);
127 }
128 
129 /*
130  * Do the actual file locking.
131  */
132 EXPORT int
lockfd(f)133 lockfd(f)
134 	int	f;
135 {
136 	int	ret	= LOCK_CANNOT;
137 	BOOL	didlock	= FALSE;
138 
139 	if (nolock)
140 		return (LOCK_OK);
141 
142 #ifdef	HAVE_LOCKF
143 #undef	FOUND_LOCK
144 #define	FOUND_LOCK
145 	/*
146 	 * First try the most recent standard interface.
147 	 * It is most likely working correctly on all systems.
148 	 * It also supports locking for all machines that
149 	 * use the file via NFS.
150 	 */
151 	seterrno(0);
152 	if (lockf(f, F_TLOCK, 0) < 0) {
153 		ret = check_lock();
154 	} else {
155 		ret = LOCK_OK;
156 		didlock = TRUE;
157 	}
158 #endif
159 
160 #ifdef	HAVE_FCNTL_LOCKF
161 #undef	FOUND_LOCK
162 #define	FOUND_LOCK
163 	/*
164 	 * Now try the SVSvr4 interface.
165 	 * It also supports locking for all machines that
166 	 * use the file via NFS.
167 	 */
168 	if (ret == LOCK_CANNOT) {
169 		seterrno(0);
170 		if (lock_fcntl(f) < 0) {
171 			ret = check_lock();
172 		} else {
173 			ret = LOCK_OK;
174 			didlock = TRUE;
175 		}
176 	}
177 #endif
178 
179 #ifdef	HAVE_FLOCK
180 #undef	FOUND_LOCK
181 #define	FOUND_LOCK
182 	/*
183 	 * If no other lock method is supported or if the lock
184 	 * was signalled successful we call this outdated BSD
185 	 * interface.
186 	 * This is needed because on some old systems the
187 	 * standard compliant locking may not work correctly.
188 	 * There are implementations that implent the old BSD
189 	 * method completely disjunct from the standard method
190 	 * and thus force us to apply for both locks.
191 	 *
192 	 * As this old BSD method does most likely not work over NFS
193 	 * it would be a bad idea to only use this locking call as
194 	 * 'nvi' does. Co-operative work in a non-networked environment
195 	 * looks like a method from the past.
196 	 */
197 	if (ret != LOCK_ALREADY) {
198 		seterrno(0);
199 		if (flock(f, LOCK_EX | LOCK_NB) < 0) {
200 			if (!didlock)
201 				ret = check_lock();
202 		} else {
203 			if (!didlock)
204 				ret = LOCK_LOCAL;
205 			didlock = TRUE;
206 		}
207 	}
208 #endif
209 #ifndef	FOUND_LOCK
210 	no_lock();
211 	return (check_lock());
212 #else
213 	return (ret);
214 #endif
215 }
216 
217 LOCAL int
lock_fcntl(f)218 lock_fcntl(f)
219 	int	f;
220 {
221 #ifdef	HAVE_FCNTL_LOCKF
222 	struct flock fl;
223 	int	ret;
224 
225 	fl.l_whence = 0;
226 	fl.l_start = 0;
227 	fl.l_len = 0;
228 	fl.l_type = 0;
229 
230 	fl.l_type |= F_WRLCK;
231 
232 	ret = fcntl(f, F_SETLK, &fl);
233 	return (ret);
234 #else
235 	no_lock();
236 	return (-1);
237 #endif
238 }
239 
240 #ifdef	JOS
241 #	include <sys/stypes.h>
242 #	include <sys/access.h>
243 #	include <sys/filedesc.h>
244 #	include <error.h>
245 
246 #	define	stat		status
247 #	define	st_mtime	f_tmodified
248 #	define	st_ino		f_fileno
249 #	define	st_dev		f_fromfs
250 #	define	STATBUF		FILEDESC
251 
252 /*
253  * Get the file permissions
254  */
255 EXPORT int
getfmodes(name)256 getfmodes(name)
257 	Uchar	*name;
258 {
259 	FILEDESC	fd;
260 
261 	if (status(C name, &fd) < 0)
262 		return (-1);
263 	return (fd.f_modes);
264 }
265 
266 /*
267  * Set the file permissions in UNIX flavor
268  */
269 int
chmod(name,modes)270 chmod(name, modes)
271 	char	*name;
272 	int	modes;
273 {
274 	FILE	*f;
275 
276 	if (f = fileopen(name, "")) {
277 		chaccess(fdown(f), modes);
278 		fclose(f);
279 	}
280 }
281 
282 #else
283 #	include <schily/stat.h>
284 #	define	STATBUF		struct stat
285 
286 /*
287  * Get the file permissions
288  */
289 EXPORT int
getfmodes(name)290 getfmodes(name)
291 	Uchar	*name;
292 {
293 	struct stat	fd;
294 
295 	if (stat(C name, &fd) < 0)
296 		return (-1);
297 
298 	/*
299 	 * We are only interested in the file permissions which will fit into
300 	 * an 'int' as long as it hold >= 12 bits.
301 	 */
302 	return ((int)fd.st_mode);
303 }
304 
305 #endif
306 
307 /*
308  * Get the time of last modification for a file.
309  */
310 EXPORT long
gftime(file)311 gftime(file)
312 	char	*file;
313 {
314 	STATBUF	stbuf;
315 
316 	stbuf.st_mtime = 0;
317 	if (stat(file, &stbuf) < 0) {
318 		/*
319 		 * GNU libc.6 destroys st_mtime
320 		 */
321 		stbuf.st_mtime = 0;
322 	}
323 	return ((long)stbuf.st_mtime);
324 }
325 
326 /*
327  * Return if the file is readable
328  */
329 EXPORT BOOL
readable(name)330 readable(name)
331 	Uchar	*name;
332 {
333 	int	ret;
334 
335 	ret = access((char *)name, R_OK); /* XXX nur OK wenn uid == euid !!! */
336 
337 	if (ret < 0)
338 		return (FALSE);
339 	return (TRUE);
340 }
341 
342 /*
343  * Return if the file is writable
344  */
345 EXPORT BOOL
writable(name)346 writable(name)
347 	Uchar	*name;
348 {
349 	int	ret;
350 
351 	ret = access((char *)name, W_OK); /* XXX nur OK wenn uid == euid !!! */
352 
353 	if (ret < 0)
354 		return (geterrno() == ENOENT);
355 
356 #ifdef	JOS
357 	if (getuid() == 0 && (getfmodes(name) & 0x222) == 0)
358 		return (FALSE);
359 #else
360 	if (getuid() == 0 && (getfmodes(name) & 0222) == 0)
361 		return (FALSE);
362 #endif
363 	return (TRUE);
364 }
365 
366 /*
367  * Check if we are in read only mode or if the file is read only.
368  * write the appropriate message and return if we would be allowed to write.
369  */
370 EXPORT BOOL
wrtcheck(wp,err)371 wrtcheck(wp, err)
372 	ewin_t	*wp;
373 	BOOL	err;
374 {
375 	int	ronly = ReadOnly;
376 
377 	if (ronly == 0 && (wp->eflags & FREADONLY) != 0)
378 		ronly = 1;
379 
380 	if (ronly > 0) {
381 		(err ? writeerr : writemsg)(wp, "READ ONLY MODE");
382 		return (FALSE);
383 	}
384 	if (!writable(wp->curfile)) {
385 		(err ? writeerr : writemsg)(wp, "READ ONLY FILE");
386 		return (FALSE);
387 	}
388 	return (TRUE);
389 }
390 
391 /*
392  * Check if the file has been modified more recent than our copy.
393  */
394 EXPORT BOOL
modcheck(wp)395 modcheck(wp)
396 	ewin_t	*wp;
397 {
398 	long	ftime;
399 
400 	ftime = gftime(C wp->curfile);
401 	if (ftime != 0 && wp->curftime != ftime) {
402 		writemsg(wp, "FILE MODIFIED BY ANOTHER USER");
403 		return (FALSE);
404 	}
405 	return (TRUE);
406 }
407