File: lockfile.C

package info (click to toggle)
sfs 1%3A0.5k-8
  • links: PTS
  • area: main
  • in suites: woody
  • size: 5,388 kB
  • ctags: 8,556
  • sloc: cpp: 43,410; ansic: 17,574; sh: 8,412; makefile: 771; yacc: 277; lex: 96; sed: 47
file content (119 lines) | stat: -rw-r--r-- 3,252 bytes parent folder | download
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
/* $Id: lockfile.C,v 1.4 2001/08/20 19:51:40 dm Exp $ */

/*
 *
 * Copyright (C) 1999 David Mazieres (dm@uun.org)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 *
 */

#include "amisc.h"

bool
stat_unchanged (const struct stat *sb1, const struct stat *sb2)
{
  return sb1->st_dev == sb2->st_dev && sb1->st_ino == sb2->st_ino
    && sb1->st_mtime == sb2->st_mtime
#ifdef SFS_HAVE_STAT_ST_MTIMESPEC
    && sb1->st_mtimespec.tv_nsec == sb2->st_mtimespec.tv_nsec
#endif /* SFS_HAVE_STAT_ST_MTIMESPEC */
    && sb1->st_size == sb2->st_size;
}

static bool
checkstat (const str &path, const struct stat &sb)
{
  if (!S_ISREG (sb.st_mode))
    warn << path << ": not a regular file -- please delete\n";
  else if (sb.st_nlink > 1)
    warn << path << ": too many links -- please delete\n";
  else if (sb.st_mode & 07177)
    warn ("%s: mode 0%o should be 0600 -- please delete\n",
	  path.cstr (), sb.st_mode & 07777);
  else if (sb.st_size)
    warn << path << ": file should be empty -- please delete\n";
  else
    return true;
  return false;
}

lockfile::~lockfile ()
{
  if (!ok ())
    warn << path << ": stolen lockfile\n";
  else
    unlink (path);
  flock (fd, LOCK_UN);
  close (fd);
}

bool
lockfile::ok () const
{
  struct stat sb, fsb;
  if (stat (path, &sb) < 0 || fstat (fd, &fsb) < 0
      || !stat_unchanged (&sb, &fsb))
    return false;
  return true;
}

ptr<lockfile>
lockfile::alloc (const str &path, bool wait)
{
  struct stat sb, fsb;

  errno = 0;
  if (lstat (path, &sb) >= 0 && !checkstat (path, sb))
      return NULL;
  else if (errno && errno != ENOENT) {
    warn << path << ": " << strerror (errno) << "\n";
    return NULL;
  }

  int fd;
 openit:
  /* N.B., 0600 (not 0644) to stop others from wedging us with
   * LOCK_SH.  Anyone with read permission to a file can flock it
   * with LOCK_SH, thereby blocking those with write permission who
   * want to lock it with LOCK_EX. */
  fd = open (path, O_RDWR|O_CREAT, 0600);
  if (fd < 0) {
    warn << path << ": " << strerror (errno) << "\n";
    return NULL;
  }
  if (flock (fd, LOCK_EX | (wait ? 0 : LOCK_NB)) < 0) {
    close (fd);
    if (wait && errno == EINTR)
      goto openit;
    return NULL;
  }
  errno = 0;
  if (fstat (fd, &fsb) < 0 || !checkstat (path, fsb)) {
    if (errno)
      warn << "fstat (" << path << "): " << strerror (errno) << "\n";
    flock (fd, LOCK_UN);
    close (fd);
    return NULL;
  }
  if (lstat (path, &sb) < 0 || !stat_unchanged (&fsb, &sb)) {
    flock (fd, LOCK_UN);
    close (fd);
    goto openit;
  }
  close_on_exec (fd);
  return New refcounted<lockfile> (path, fd);
}