xref: /freebsd/crypto/krb5/src/lib/krb5/os/lock_file.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/os/lock_file.c */
3 /*
4  * Copyright 1990, 1998 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 
27 #include "k5-int.h"
28 #include <stdio.h>
29 
30 #if !defined(_WIN32)
31 
32 /* Unix version...  */
33 
34 #if HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37 
38 #include <errno.h>
39 
40 #ifdef HAVE_FCNTL_H
41 #include <fcntl.h>
42 #endif
43 
44 #if defined(HAVE_FCNTL_H) && defined(F_SETLKW) && defined(F_RDLCK)
45 #define POSIX_FILE_LOCKS
46 
47 /*
48  * Gnu libc bug 20251, fixed in 2.28, breaks OFD lock support on
49  * 32-bit platforms.  Work around this bug by explicitly using the
50  * fcntl64 system call and struct flock64.
51  */
52 #if defined(__linux__) && __WORDSIZE == 32
53 #include <sys/syscall.h>
54 #ifdef SYS_fcntl64
55 #define USE_FCNTL64
56 #endif
57 #endif
58 #ifdef USE_FCNTL64
59 /* Use the fcntl64 system call and struct flock64.  (Gnu libc does not
60  * define a fcntl64() function, so we must use syscall().) */
61 #define fcntl(fd, cmd, arg) syscall(SYS_fcntl64, fd, cmd, arg)
62 typedef struct flock64 fcntl_lock_st;
63 #else
64 /* Use regular fcntl() and struct flock. */
65 typedef struct flock fcntl_lock_st;
66 #endif
67 
68 #endif /* defined(HAVE_FCNTL_H) && defined(F_SETLKW) && defined(F_RDLCK) */
69 
70 #ifdef HAVE_FLOCK
71 #ifndef sysvimp
72 #include <sys/file.h>
73 #endif
74 #else
75 #ifndef LOCK_SH
76 #define LOCK_SH 0
77 #define LOCK_EX 0
78 #define LOCK_UN 0
79 #endif
80 #endif
81 
82 #ifdef POSIX_FILE_LOCKS
83 /*
84  * Try to use OFD locks where available (e.g. Linux 3.15 and later).  OFD locks
85  * contend with regular POSIX file locks, but are owned by the open file
86  * description instead of the process, which is much better for thread safety.
87  * Fall back to regular POSIX locks on EINVAL in case we are running with an
88  * older kernel than we were built with.
89  */
90 static int
ofdlock(int fd,int cmd,fcntl_lock_st * lock_arg)91 ofdlock(int fd, int cmd, fcntl_lock_st *lock_arg)
92 {
93 #ifdef F_OFD_SETLKW
94     int st, ofdcmd;
95 
96     assert(cmd == F_SETLKW || cmd == F_SETLK);
97     ofdcmd = (cmd == F_SETLKW) ? F_OFD_SETLKW : F_OFD_SETLK;
98     st = fcntl(fd, ofdcmd, lock_arg);
99     if (st == 0 || errno != EINVAL)
100         return st;
101 #endif
102     return fcntl(fd, cmd, lock_arg);
103 }
104 #endif /* POSIX_FILE_LOCKS */
105 
106 /*ARGSUSED*/
107 krb5_error_code
krb5_lock_file(krb5_context context,int fd,int mode)108 krb5_lock_file(krb5_context context, int fd, int mode)
109 {
110     int                 lock_flag = -1;
111     krb5_error_code     retval = 0;
112 #ifdef POSIX_FILE_LOCKS
113     int lock_cmd = F_SETLKW;
114     fcntl_lock_st lock_arg = { 0 };
115 #endif
116 
117     switch (mode & ~KRB5_LOCKMODE_DONTBLOCK) {
118     case KRB5_LOCKMODE_SHARED:
119 #ifdef POSIX_FILE_LOCKS
120         lock_arg.l_type = F_RDLCK;
121 #endif
122         lock_flag = LOCK_SH;
123         break;
124     case KRB5_LOCKMODE_EXCLUSIVE:
125 #ifdef POSIX_FILE_LOCKS
126         lock_arg.l_type = F_WRLCK;
127 #endif
128         lock_flag = LOCK_EX;
129         break;
130     case KRB5_LOCKMODE_UNLOCK:
131 #ifdef POSIX_FILE_LOCKS
132         lock_arg.l_type = F_UNLCK;
133 #endif
134         lock_flag = LOCK_UN;
135         break;
136     }
137 
138     if (lock_flag == -1)
139         return(KRB5_LIBOS_BADLOCKFLAG);
140 
141     if (mode & KRB5_LOCKMODE_DONTBLOCK) {
142 #ifdef POSIX_FILE_LOCKS
143         lock_cmd = F_SETLK;
144 #endif
145 #ifdef HAVE_FLOCK
146         lock_flag |= LOCK_NB;
147 #endif
148     }
149 
150 #ifdef POSIX_FILE_LOCKS
151     lock_arg.l_whence = 0;
152     lock_arg.l_start = 0;
153     lock_arg.l_len = 0;
154     if (ofdlock(fd, lock_cmd, &lock_arg) == -1) {
155         if (errno == EACCES || errno == EAGAIN) /* see POSIX/IEEE 1003.1-1988,
156                                                    6.5.2.4 */
157             return(EAGAIN);
158         if (errno != EINVAL)    /* Fall back to flock if we get EINVAL */
159             return(errno);
160         retval = errno;
161     } else
162         return 0;           /* We succeeded.  Yay. */
163 #endif
164 
165 #ifdef HAVE_FLOCK
166     if (flock(fd, lock_flag) == -1)
167         retval = errno;
168 #endif
169 
170     return retval;
171 }
172 #else   /* Windows or Macintosh */
173 
174 krb5_error_code
krb5_lock_file(context,fd,mode)175 krb5_lock_file(context, fd, mode)
176     krb5_context context;
177     int fd;
178     int mode;
179 {
180     return 0;
181 }
182 #endif
183