]> git.ipfire.org Git - thirdparty/shadow.git/commitdiff
commonio: Use do_lock_file again
authorSamanta Navarro <ferivoz@riseup.net>
Wed, 10 May 2023 11:57:20 +0000 (11:57 +0000)
committerSerge Hallyn <serge@hallyn.com>
Thu, 11 May 2023 15:59:21 +0000 (10:59 -0500)
This avoids regressions introduced with do_fcntl_lock.

Signed-off-by: Samanta Navarro <ferivoz@riseup.net>
lib/commonio.c

index 6857f53134c346813c345a38eb784b3451b95747..1f9291e5aaf4672e2c392b19c2f9847d9a056fab 100644 (file)
@@ -34,6 +34,8 @@
 
 /* local function prototypes */
 static int lrename (const char *, const char *);
+static int check_link_count (const char *file, bool log);
+static int do_lock_file (const char *file, const char *lock, bool log);
 static /*@null@*/ /*@dependent@*/FILE *fopen_set_perms (
        const char *name,
        const char *mode,
@@ -91,6 +93,150 @@ int lrename (const char *old, const char *new)
        return res;
 }
 
+static int check_link_count (const char *file, bool log)
+{
+       struct stat sb;
+
+       if (stat (file, &sb) != 0) {
+               if (log) {
+                       (void) fprintf (shadow_logfd,
+                                       "%s: %s file stat error: %s\n",
+                                       shadow_progname, file, strerror (errno));
+               }
+               return 0;
+       }
+
+       if (sb.st_nlink != 2) {
+               if (log) {
+                       (void) fprintf (shadow_logfd,
+                                       "%s: %s: lock file already used (nlink: %u)\n",
+                                       shadow_progname, file, sb.st_nlink);
+               }
+               return 0;
+       }
+
+       return 1;
+}
+
+
+static int do_lock_file (const char *file, const char *lock, bool log)
+{
+       int fd;
+       pid_t pid;
+       ssize_t len;
+       int retval;
+       char buf[32];
+
+       fd = open (file, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+       if (-1 == fd) {
+               if (log) {
+                       (void) fprintf (shadow_logfd,
+                                       "%s: %s: %s\n",
+                                       shadow_progname, file, strerror (errno));
+               }
+               return 0;
+       }
+
+       pid = getpid ();
+       snprintf (buf, sizeof buf, "%lu", (unsigned long) pid);
+       len = (ssize_t) strlen (buf) + 1;
+       if (write (fd, buf, (size_t) len) != len) {
+               if (log) {
+                       (void) fprintf (shadow_logfd,
+                                       "%s: %s file write error: %s\n",
+                                       shadow_progname, file, strerror (errno));
+               }
+               (void) close (fd);
+               unlink (file);
+               return 0;
+       }
+       if (fdatasync (fd) == -1) {
+               if (log) {
+                       (void) fprintf (shadow_logfd,
+                                       "%s: %s file sync error: %s\n",
+                                       shadow_progname, file, strerror (errno));
+               }
+               (void) close (fd);
+               unlink (file);
+               return 0;
+       }
+       close (fd);
+
+       if (link (file, lock) == 0) {
+               retval = check_link_count (file, log);
+               unlink (file);
+               return retval;
+       }
+
+       fd = open (lock, O_RDWR);
+       if (-1 == fd) {
+               if (log) {
+                       (void) fprintf (shadow_logfd,
+                                       "%s: %s: %s\n",
+                                       shadow_progname, lock, strerror (errno));
+               }
+               unlink (file);
+               errno = EINVAL;
+               return 0;
+       }
+       len = read (fd, buf, sizeof (buf) - 1);
+       close (fd);
+       if (len <= 0) {
+               if (log) {
+                       (void) fprintf (shadow_logfd,
+                                       "%s: existing lock file %s without a PID\n",
+                                       shadow_progname, lock);
+               }
+               unlink (file);
+               errno = EINVAL;
+               return 0;
+       }
+       buf[len] = '\0';
+       if (get_pid (buf, &pid) == 0) {
+               if (log) {
+                       (void) fprintf (shadow_logfd,
+                                       "%s: existing lock file %s with an invalid PID '%s'\n",
+                                       shadow_progname, lock, buf);
+               }
+               unlink (file);
+               errno = EINVAL;
+               return 0;
+       }
+       if (kill (pid, 0) == 0) {
+               if (log) {
+                       (void) fprintf (shadow_logfd,
+                                       "%s: lock %s already used by PID %lu\n",
+                                       shadow_progname, lock, (unsigned long) pid);
+               }
+               unlink (file);
+               errno = EEXIST;
+               return 0;
+       }
+       if (unlink (lock) != 0) {
+               if (log) {
+                       (void) fprintf (shadow_logfd,
+                                       "%s: cannot get lock %s: %s\n",
+                                       shadow_progname, lock, strerror (errno));
+               }
+               unlink (file);
+               return 0;
+       }
+
+       retval = 0;
+       if (link (file, lock) == 0) {
+               retval = check_link_count (file, log);
+       } else {
+               if (log) {
+                       (void) fprintf (shadow_logfd,
+                                       "%s: cannot get lock %s: %s\n",
+                                       shadow_progname, lock, strerror (errno));
+               }
+       }
+
+       unlink (file);
+       return retval;
+}
+
 
 static /*@null@*/ /*@dependent@*/FILE *fopen_set_perms (
        const char *name,
@@ -205,43 +351,38 @@ bool commonio_present (const struct commonio_db *db)
 }
 
 
-int do_fcntl_lock (const char *file, bool log, short type)
-{
-       int fd;
-       struct flock lck = {
-               .l_type = type,
-               .l_whence = SEEK_SET,
-               .l_start = 0,
-               .l_len = 0,
-       };
-
-       fd = open (file, O_WRONLY | O_CREAT, 0600);
-       if (-1 == fd) {
-               if (log) {
-                       (void) fprintf (shadow_logfd, "%s: %s: %s\n",
-                                       shadow_progname, file, strerror (errno));
-               }
-               return 0;
-       }
-
-       fcntl (fd, F_SETLKW, &lck);
-       return(1);
-}
-
 int commonio_lock_nowait (struct commonio_db *db, bool log)
 {
+       char* file = NULL;
+       char* lock = NULL;
+       size_t lock_file_len;
+       size_t file_len;
        int err = 0;
 
        if (db->locked) {
                return 1;
        }
-
-       if (do_fcntl_lock (db->filename, log, F_WRLCK | F_RDLCK) != 0) {
+       file_len = strlen(db->filename) + 11;/* %lu max size */
+       lock_file_len = strlen(db->filename) + 6; /* sizeof ".lock" */
+       file = MALLOCARRAY(file_len, char);
+       if (file == NULL) {
+               goto cleanup_ENOMEM;
+       }
+       lock = MALLOCARRAY(lock_file_len, char);
+       if (lock == NULL) {
+               goto cleanup_ENOMEM;
+       }
+       snprintf (file, file_len, "%s.%lu",
+                 db->filename, (unsigned long) getpid ());
+       snprintf (lock, lock_file_len, "%s.lock", db->filename);
+       if (do_lock_file (file, lock, log) != 0) {
                db->locked = true;
                lock_count++;
                err = 1;
        }
-
+cleanup_ENOMEM:
+       free(file);
+       free(lock);
        return err;
 }
 
@@ -332,6 +473,8 @@ static void dec_lock_count (void)
 
 int commonio_unlock (struct commonio_db *db)
 {
+       char lock[1024];
+
        if (db->isopen) {
                db->readonly = true;
                if (commonio_close (db) == 0) {
@@ -342,9 +485,13 @@ int commonio_unlock (struct commonio_db *db)
                }
        }
        if (db->locked) {
+               /*
+                * Unlock in reverse order: remove the lock file,
+                * then call ulckpwdf() (if used) on last unlock.
+                */
                db->locked = false;
-
-               do_fcntl_lock (db->filename, false, F_UNLCK);
+               snprintf (lock, sizeof lock, "%s.lock", db->filename);
+               unlink (lock);
                dec_lock_count ();
                return 1;
        }