File: commonio.c

package info (click to toggle)
pwdb 0.54preD-3
  • links: PTS
  • area: main
  • in suites: hamm, slink
  • size: 644 kB
  • ctags: 797
  • sloc: ansic: 8,459; makefile: 346; sh: 204
file content (157 lines) | stat: -rw-r--r-- 3,296 bytes parent folder | download | duplicates (2)
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

#include "../_pwdb_internal.h"

static int check_link_count(const char *file)
{
	struct stat sb;

	if (stat(file, &sb) != 0)
		return 0;

	if (sb.st_nlink != 2)
		return 0;

	unlink(file);
	return 1;
}

/*
 * do_lock_file - lock a password file
 *
 *	do_lock_file() encapsulates the lock operation.  it returns
 *	TRUE or FALSE depending on the password file being
 *	properly locked.  the lock is set by creating a semaphore
 *	file, LOCK.  FILE is a temporary file name.
 */
int do_lock_file(const char *file, const char *lock)
{
	int	fd;
	int	pid;
	int	len;
	char	buf[32];

	/*
	 * Create a lock file which can be switched into place
	 */

	if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
		return 0;

	pid = getpid();
	sprintf (buf, "%d", pid);
	len = strlen(buf) + 1;
	if (write (fd, buf, len) != len) {
		(void) close (fd);
		(void) unlink (file);
		return 0;
	}
	close (fd);

	/*
	 * Simple case first -
	 *	Link fails (in a sane environment ...) if the target
	 *	exists already.  So we try to switch in a new lock
	 *	file.  If that succeeds, we assume we have the only
	 *	valid lock.  Needs work for NFS where this assumption
	 *	may not hold.  The simple hack is to check the link
	 *	count on the source file, which should be 2 iff the
	 *	link =really= worked.
	 */

	if (link (file, lock) == 0)
		return check_link_count(file);

	/*
	 * Invalid lock test -
	 *	Open the lock file and see if the lock is valid.
	 *	The PID of the lock file is checked, and if the PID
	 *	is not valid, the lock file is removed.  If the unlink
	 *	of the lock file fails, it should mean that someone
	 *	else is executing this code.  They will get success,
	 *	and we will fail.
	 */

	if ((fd = open (lock, O_RDWR)) == -1 ||
			(len = read (fd, buf, sizeof buf)) <= 0) {
		errno = EINVAL;
		return 0;
	}
	buf[len] = '\0';
	if ((pid = strtol (buf, (char **) 0, 10)) == 0) {
		errno = EINVAL;
		return 0;
	}
	if (kill (pid, 0) == 0)  {
		errno = EEXIST;
		return 0;
	}
	if (unlink (lock)) {
		(void) close (fd);
		(void) unlink (file);

		return 0;
	}

	/*
	 * Re-try lock -
	 *	The invalid lock has now been removed and I should
	 *	be able to acquire a lock for myself just fine.  If
	 *	this fails there will be no retry.  The link count
	 *	test here makes certain someone executing the previous
	 *	block of code didn't just remove the lock we just
	 *	linked to.
	 */

	if (link (file, lock) == 0)
		return check_link_count(file);

	(void) unlink (file);
	return 0;
}

FILE * fopen_with_umask(const char *name, const char *mode, int mask)
{
	FILE *f;

	mask = umask(mask);
	f = fopen(name, mode);
	umask(mask);
	return f;
}

/*
 * Copy fp to backup, set permissions and times from st.
 */
int create_backup_file(FILE *fp, const char *backup, const struct stat *st)
{
	FILE *bkfp;
	int c;

	unlink(backup);
	bkfp = fopen_with_umask(backup, "w", 0777);
	if (bkfp == NULL)
		return -1;
	rewind(fp);
	while ((c = getc(fp)) != EOF) {
		if (putc(c, bkfp) == EOF) {
			fclose(bkfp);
			return -1;
		}
	}
	if (fflush(bkfp)) {
		fclose(bkfp);
		return -1;
	}
	if (fclose(bkfp))
		return -1;
	if (st) {
		struct utimbuf ut;

		chown(backup, st->st_uid, st->st_gid);
		chmod(backup, st->st_mode);
		ut.actime = st->st_atime;
		ut.modtime = st->st_mtime;
		utime(backup, &ut);
	}
	return 0;
}