/* $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::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 (path, fd); }