File: flock.c

package info (click to toggle)
magic 7.5.220-1
  • links: PTS
  • area: main
  • in suites: wheezy
  • size: 17,860 kB
file content (170 lines) | stat: -rw-r--r-- 4,993 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/*
 *-------------------------------------------------------------------------
 * flock.c --
 *
 * File opening with file locks (used by utils/path.c and database/DBio.c)
 *
 * Original implementation by Michael Godfrey, Stanford University.
 * Implementation using "lockf" by Stefan Jones, MultiGiG, Inc.,
 * September 2005 (magic 7.3.102_lockf, finally corrected and implemented
 * as magic 7.3.119)
 *-------------------------------------------------------------------------
 */

#ifdef FILE_LOCKS

#include <unistd.h>
#include <fcntl.h>

#include "utils/magic.h"
#include "utils/hash.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "database/database.h"
#include "windows/windows.h"
#include "utils/malloc.h"

/*
 *-------------------------------------------------------------------------
 * Below are the service routines for file locking.
 *
 * The original file locking mechanism (Michael Godfrey) used common files.
 * However, this has the drawback of requiring extraneous directories, and
 * orphaned lock files will remain after a program crash.
 *
 * The lockf solution (Stefan Jones) has the drawback that in order to
 * hold a lock, the file descriptor must be left open.  This leaves the
 * possibility that we may exceed the maximum number of file descriptors
 * per process.  Because this solution is the simplest, it is implemented
 * here.
 *
 * For future reference, the best file locking system (?) would use
 * signaling via fcntl(F_NOTIFY).  The locking mechanism goes like this:
 *
 * 1. process X requests an attributes notification on each directory that
 *    it reads a file from.
 *
 * 2. process X reads file A for editing.
 *
 * 3. process Y requests an attributes notification on each directory that
 *    it reads a file from.
 *
 * 4. process Y reads file A, thus changing its access time attribute.
 *    process Y sets the cell as read/write.
 *
 * 5. process X receives a signal.  It checks the cell that is being
 *    accessed.  If the cell has been modified, it sets a lock on
 *    file A.  Opening file A to set the lock modifies its access time.
 *    If the cell has not been modified, it sets the cell to read-only.
 *
 * 6. process Y receives a signal.  It checks the cell that is being
 *    accessed.  If the cell has not yet been modified, it sets the
 *    cell to read-only.  If the cell has been modified (less likely,
 *    but possible if process X was doing something time-consuming and
 *    uninterruptable and didn't process the signal for a while), then
 *    process Y attempts to set a lock.  If the lock fails, then a
 *    warning is issued.
 *
 * It is possible for either X or Y to win the race if both processes
 * modified the file right after opening.  However, this rare condition
 * is unlikely to be a serious problem, and prevents a process from
 * having to hold open many file descriptors.
 * 
 *-------------------------------------------------------------------------
 */

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

/*
 *-------------------------------------------------------------------------
 * flock_open --
 *
 *	Open a file with "lockf" file locking method.
 *
 * Results --
 *	Pointer to FILE that was opened, or NULL if an error occurred.
 *
 * Side effects --
 *	System I/O
 *-------------------------------------------------------------------------
 */

FILE *flock_open(filename, mode, is_locked)
    char *filename;
    char *mode;
    bool *is_locked;
{
    FILE *f = NULL, *tmp;
    struct flock fl;

    if (is_locked) *is_locked = FALSE;

    /* If is_locked is NULL, then a lock is not requested, so just do	*/
    /* a normal fopen() and return.					*/

    if (is_locked == NULL) return fopen(filename, mode);
	
    /* Diagnostic */
    /* TxPrintf("Opening file <%s>\n", filename); */

    tmp = fopen(filename, "r+");
    if (tmp == NULL)
    {
	if (is_locked) *is_locked = TRUE;
	f = fopen(filename, "r");
	goto done;	
    }

    fl.l_len = 0;
    fl.l_start = 0;
    fl.l_whence = SEEK_SET;
    fl.l_type = F_WRLCK;
    fl.l_pid = getpid();

    if (fcntl(fileno(tmp), F_GETLK, &fl))
    {
	perror(filename);
	f = fopen(filename, mode);
	goto done;
    }
    fclose(tmp);
	
    if (fl.l_type == F_UNLCK)
    {
	fl.l_len = 0;
	fl.l_start = 0;
	fl.l_whence = SEEK_SET;
	fl.l_type = F_WRLCK;
	fl.l_pid = getpid();

	f = fopen(filename, "r+");
	if (fcntl(fileno(f), F_SETLK, &fl))
	{
 	    perror(filename);
	}
	else
	{
	    /* Diagnostic */
	    /* TxPrintf("Obtained lock on file <%s> (fd=%d)\n", filename, fileno(f)); */
	}
    }
    else
    {
	/* Don't know why PID is not set by F_GETLK as advertised? */
	if (fl.l_pid == 0)
	    TxPrintf("File <%s> is already locked by another process."
			"  Opening read-only.\n", filename);
	else
	    TxPrintf("File <%s> is already locked by pid %d.  Opening read-only.\n",
				filename, (int)fl.l_pid);
	if (is_locked) *is_locked = TRUE;
	f = fopen(filename, "r");
    }

done:
    return f;
}

#endif  /* FILE_LOCKS */