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