Blame login/utmp_file.c

Packit 6c4009
/* Copyright (C) 1996-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@cygnus.com>
Packit 6c4009
   and Paul Janzen <pcj@primenet.com>, 1996.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <signal.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <utmp.h>
Packit 6c4009
#include <not-cancel.h>
Packit 6c4009
#include <kernel-features.h>
Packit 6c4009
#include <sigsetops.h>
Packit 6c4009
#include <not-cancel.h>
Packit 6c4009
Packit 6c4009
#include "utmp-private.h"
Packit 6c4009
#include "utmp-equal.h"
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Descriptor for the file and position.  */
Packit 6c4009
static int file_fd = -1;
Packit 6c4009
static bool file_writable;
Packit 6c4009
static off64_t file_offset;
Packit 6c4009
Packit 6c4009
/* Cache for the last read entry.  */
Packit 6c4009
static struct utmp last_entry;
Packit 6c4009
Packit Bot f5dbca
/* Returns true if *ENTRY matches last_entry, based on
Packit Bot f5dbca
   data->ut_type.  */
Packit Bot f5dbca
static bool
Packit Bot f5dbca
matches_last_entry (const struct utmp *data)
Packit Bot f5dbca
{
Packit Bot f5dbca
  if (file_offset <= 0)
Packit Bot f5dbca
    /* Nothing has been read.  last_entry is stale and cannot match.  */
Packit Bot f5dbca
    return false;
Packit Bot f5dbca
Packit Bot f5dbca
  if (data->ut_type == RUN_LVL
Packit Bot f5dbca
      || data->ut_type == BOOT_TIME
Packit Bot f5dbca
      || data->ut_type == OLD_TIME
Packit Bot f5dbca
      || data->ut_type == NEW_TIME)
Packit Bot f5dbca
    /* For some entry types, only a type match is required.  */
Packit Bot f5dbca
    return data->ut_type == last_entry.ut_type;
Packit Bot f5dbca
  else
Packit Bot f5dbca
    /* For the process-related entries, a full match is needed.  */
Packit Bot f5dbca
    return __utmp_equal (&last_entry, data);
Packit Bot f5dbca
}
Packit 6c4009
Packit 6c4009
/* Locking timeout.  */
Packit 6c4009
#ifndef TIMEOUT
Packit 6c4009
# define TIMEOUT 10
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Do-nothing handler for locking timeout.  */
Packit 6c4009
static void timeout_handler (int signum) {};
Packit 6c4009
Packit Bot 565700
Packit Bot 565700
/* try_file_lock (LOCKING, FD, TYPE) returns true if the locking
Packit Bot 565700
   operation failed and recovery needs to be performed.
Packit Bot 565700
Packit Bot 565700
   file_unlock (FD) removes the lock (which must have been
Packit Bot 490693
   successfully acquired). */
Packit Bot 565700
Packit Bot 565700
static bool
Packit Bot 490693
try_file_lock (int fd, int type)
Packit Bot 565700
{
Packit Bot 565700
  /* Cancel any existing alarm.  */
Packit Bot 490693
  int old_timeout = alarm (0);
Packit Bot 565700
Packit Bot 565700
  /* Establish signal handler.  */
Packit Bot 490693
  struct sigaction old_action;
Packit Bot 565700
  struct sigaction action;
Packit Bot 565700
  action.sa_handler = timeout_handler;
Packit Bot 565700
  __sigemptyset (&action.sa_mask);
Packit Bot 565700
  action.sa_flags = 0;
Packit Bot 490693
  __sigaction (SIGALRM, &action, &old_action);
Packit Bot 565700
Packit Bot 565700
  alarm (TIMEOUT);
Packit Bot 565700
Packit Bot 565700
  /* Try to get the lock.  */
Packit Bot 0891d7
 struct flock64 fl =
Packit Bot 565700
   {
Packit Bot 565700
    .l_type = type,
Packit Bot 6982a9
    .l_whence = SEEK_SET,
Packit Bot 565700
   };
Packit Bot 490693
Packit Bot 490693
 bool status = __fcntl64_nocancel (fd, F_SETLKW, &fl) < 0;
Packit Bot 490693
 int saved_errno = errno;
Packit Bot 490693
Packit Bot 490693
 /* Reset the signal handler and alarm.  We must reset the alarm
Packit Bot 490693
    before resetting the handler so our alarm does not generate a
Packit Bot 490693
    spurious SIGALRM seen by the user.  However, we cannot just set
Packit Bot 490693
    the user's old alarm before restoring the handler, because then
Packit Bot 490693
    it's possible our handler could catch the user alarm's SIGARLM and
Packit Bot 490693
    then the user would never see the signal he expected.  */
Packit Bot 490693
  alarm (0);
Packit Bot 490693
  __sigaction (SIGALRM, &old_action, NULL);
Packit Bot 490693
  if (old_timeout != 0)
Packit Bot 490693
    alarm (old_timeout);
Packit Bot 490693
Packit Bot 490693
  __set_errno (saved_errno);
Packit Bot 490693
  return status;
Packit Bot 565700
}
Packit Bot 565700
Packit Bot 565700
static void
Packit Bot 565700
file_unlock (int fd)
Packit Bot 565700
{
Packit Bot 0891d7
  struct flock64 fl =
Packit Bot 565700
    {
Packit Bot 565700
      .l_type = F_UNLCK,
Packit Bot 565700
    };
Packit Bot 565700
  __fcntl64_nocancel (fd, F_SETLKW, &fl);
Packit Bot 565700
}
Packit Bot 565700
Packit 6c4009
#ifndef TRANSFORM_UTMP_FILE_NAME
Packit 6c4009
# define TRANSFORM_UTMP_FILE_NAME(file_name) (file_name)
Packit 6c4009
#endif
Packit 6c4009
Packit Bot 20692a
int
Packit Bot 20692a
__libc_setutent (void)
Packit 6c4009
{
Packit 6c4009
  if (file_fd < 0)
Packit 6c4009
    {
Packit 6c4009
      const char *file_name;
Packit 6c4009
Packit 6c4009
      file_name = TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name);
Packit 6c4009
Packit 6c4009
      file_writable = false;
Packit 6c4009
      file_fd = __open_nocancel
Packit 6c4009
	(file_name, O_RDONLY | O_LARGEFILE | O_CLOEXEC);
Packit 6c4009
      if (file_fd == -1)
Packit 6c4009
	return 0;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  __lseek64 (file_fd, 0, SEEK_SET);
Packit 6c4009
  file_offset = 0;
Packit 6c4009
Packit 6c4009
  return 1;
Packit 6c4009
}
Packit 6c4009
Packit Bot 20692a
/* Preform initialization if necessary.  */
Packit Bot 20692a
static bool
Packit Bot 20692a
maybe_setutent (void)
Packit Bot 20692a
{
Packit Bot 20692a
  return file_fd >= 0 || __libc_setutent ();
Packit Bot 20692a
}
Packit 6c4009
Packit Bot 80015a
/* Reads the entry at file_offset, storing it in last_entry and
Packit Bot 80015a
   updating file_offset on success.  Returns -1 for a read error, 0
Packit Bot 80015a
   for EOF, and 1 for a successful read.  last_entry and file_offset
Packit Bot 80015a
   are only updated on a successful and complete read.  */
Packit Bot 80015a
static ssize_t
Packit Bot 80015a
read_last_entry (void)
Packit Bot 80015a
{
Packit Bot 80015a
  struct utmp buffer;
Packit Bot 80015a
  ssize_t nbytes = __pread64_nocancel (file_fd, &buffer, sizeof (buffer),
Packit Bot 80015a
				       file_offset);
Packit Bot 80015a
  if (nbytes < 0)
Packit Bot 80015a
    return -1;
Packit Bot 80015a
  else if (nbytes != sizeof (buffer))
Packit Bot 80015a
    /* Assume EOF.  */
Packit Bot 80015a
    return 0;
Packit Bot 80015a
  else
Packit Bot 80015a
    {
Packit Bot 80015a
      last_entry = buffer;
Packit Bot 80015a
      file_offset += sizeof (buffer);
Packit Bot 80015a
      return 1;
Packit Bot 80015a
    }
Packit Bot 80015a
}
Packit Bot 80015a
Packit Bot 20692a
int
Packit Bot 20692a
__libc_getutent_r (struct utmp *buffer, struct utmp **result)
Packit Service 4d2050
{
Packit Bot 80015a
  int saved_errno = errno;
Packit Service 4d2050
Packit Bot 80015a
  if (!maybe_setutent ())
Packit 6c4009
    {
Packit 6c4009
      /* Not available.  */
Packit 6c4009
      *result = NULL;
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit Bot 490693
  if (try_file_lock (file_fd, F_RDLCK))
Packit Bot 80015a
    return -1;
Packit Bot 0c2104
Packit Bot 80015a
  ssize_t nbytes = read_last_entry ();
Packit Bot 80015a
  file_unlock (file_fd);
Packit Bot 80015a
Packit Bot 80015a
  if (nbytes <= 0)		/* Read error or EOF.  */
Packit 6c4009
    {
Packit Bot 80015a
      if (nbytes == 0)
Packit Bot 80015a
	/* errno should be unchanged to indicate success.  A premature
Packit Bot 80015a
	   EOF is treated like an EOF (missing complete record at the
Packit Bot 80015a
	   end).  */
Packit Bot 80015a
	__set_errno (saved_errno);
Packit 6c4009
      *result = NULL;
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  memcpy (buffer, &last_entry, sizeof (struct utmp));
Packit 6c4009
  *result = buffer;
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit Bot 80fdcf
/* Search for *ID, updating last_entry and file_offset.  Return 0 on
Packit Bot f3904a
   success and -1 on failure.  Does not perform locking; for that see
Packit Bot f3904a
   internal_getut_r below.  */
Packit 6c4009
static int
Packit Bot f3904a
internal_getut_nolock (const struct utmp *id)
Packit 6c4009
{
Packit Bot f5dbca
  while (1)
Packit 6c4009
    {
Packit Bot 80015a
      ssize_t nbytes = read_last_entry ();
Packit Bot 80015a
      if (nbytes < 0)
Packit Bot 80015a
	return -1;
Packit Bot 80015a
      if (nbytes == 0)
Packit 6c4009
	{
Packit Bot 80015a
	  /* End of file reached.  */
Packit Bot f5dbca
	  __set_errno (ESRCH);
Packit Bot f5dbca
	  return -1;
Packit 6c4009
	}
Packit Bot 0c2104
Packit Bot f5dbca
      if (matches_last_entry (id))
Packit Bot f5dbca
	break;
Packit 6c4009
    }
Packit 6c4009
Packit Bot f3904a
  return 0;
Packit Bot f3904a
}
Packit 6c4009
Packit Bot f3904a
/* Search for *ID, updating last_entry and file_offset.  Return 0 on
Packit Bot f3904a
   success and -1 on failure.  If the locking operation failed, write
Packit Bot f3904a
   true to *LOCK_FAILED.  */
Packit Bot f3904a
static int
Packit Bot f3904a
internal_getut_r (const struct utmp *id, bool *lock_failed)
Packit Bot f3904a
{
Packit Bot f3904a
  if (try_file_lock (file_fd, F_RDLCK))
Packit Bot f3904a
    {
Packit Bot f3904a
      *lock_failed = true;
Packit Bot f3904a
      return -1;
Packit Bot f3904a
    }
Packit 6c4009
Packit Bot f3904a
  int result = internal_getut_nolock (id);
Packit Bot f3904a
  file_unlock (file_fd);
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* For implementing this function we don't use the getutent_r function
Packit 6c4009
   because we can avoid the reposition on every new entry this way.  */
Packit Bot 20692a
int
Packit Bot 20692a
__libc_getutid_r (const struct utmp *id, struct utmp *buffer,
Packit Bot 20692a
		  struct utmp **result)
Packit 6c4009
{
Packit Bot 80015a
  if (!maybe_setutent ())
Packit 6c4009
    {
Packit 6c4009
      *result = NULL;
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* We don't have to distinguish whether we can lock the file or
Packit 6c4009
     whether there is no entry.  */
Packit 6c4009
  bool lock_failed = false;
Packit Bot 80fdcf
  if (internal_getut_r (id, &lock_failed) < 0)
Packit 6c4009
    {
Packit 6c4009
      *result = NULL;
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  memcpy (buffer, &last_entry, sizeof (struct utmp));
Packit 6c4009
  *result = buffer;
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* For implementing this function we don't use the getutent_r function
Packit 6c4009
   because we can avoid the reposition on every new entry this way.  */
Packit Bot 20692a
int
Packit Bot 20692a
__libc_getutline_r (const struct utmp *line, struct utmp *buffer,
Packit Bot 20692a
		    struct utmp **result)
Packit 6c4009
{
Packit Bot 80015a
  if (!maybe_setutent ())
Packit 6c4009
    {
Packit 6c4009
      *result = NULL;
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit Bot 490693
  if (try_file_lock (file_fd, F_RDLCK))
Packit 6c4009
    {
Packit 6c4009
      *result = NULL;
Packit Bot 565700
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  while (1)
Packit 6c4009
    {
Packit Bot 80015a
      ssize_t nbytes = read_last_entry ();
Packit Bot 80015a
      if (nbytes < 0)
Packit Bot 80015a
	{
Packit Bot 80015a
	  file_unlock (file_fd);
Packit Bot 80015a
	  *result = NULL;
Packit Bot 80015a
	  return -1;
Packit Bot 80015a
	}
Packit Bot 80015a
      if (nbytes == 0)
Packit 6c4009
	{
Packit Bot 80015a
	  /* End of file reached.  */
Packit Bot 80015a
	  file_unlock (file_fd);
Packit 6c4009
	  __set_errno (ESRCH);
Packit 6c4009
	  *result = NULL;
Packit Bot 80015a
	  return -1;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Stop if we found a user or login entry.  */
Packit Bot 95f862
      if ((last_entry.ut_type == USER_PROCESS
Packit 6c4009
	   || last_entry.ut_type == LOGIN_PROCESS)
Packit Bot 95f862
	  && (strncmp (line->ut_line, last_entry.ut_line, sizeof line->ut_line)
Packit Bot 95f862
	      == 0))
Packit 6c4009
	break;
Packit 6c4009
    }
Packit 6c4009
Packit Bot 80015a
  file_unlock (file_fd);
Packit 6c4009
  memcpy (buffer, &last_entry, sizeof (struct utmp));
Packit 6c4009
  *result = buffer;
Packit 6c4009
Packit Bot 80015a
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit Bot 20692a
struct utmp *
Packit Bot 20692a
__libc_pututline (const struct utmp *data)
Packit 6c4009
{
Packit Bot 80015a
  if (!maybe_setutent ())
Packit Bot 20692a
    return NULL;
Packit Bot 20692a
Packit 6c4009
  struct utmp *pbuf;
Packit Bot 0c2104
Packit 6c4009
  if (! file_writable)
Packit 6c4009
    {
Packit 6c4009
      /* We must make the file descriptor writable before going on.  */
Packit 6c4009
      const char *file_name = TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name);
Packit 6c4009
Packit 6c4009
      int new_fd = __open_nocancel
Packit 6c4009
	(file_name, O_RDWR | O_LARGEFILE | O_CLOEXEC);
Packit 6c4009
      if (new_fd == -1)
Packit 6c4009
	return NULL;
Packit 6c4009
Packit Bot 80015a
      if (__dup2 (new_fd, file_fd) < 0)
Packit 6c4009
	{
Packit 6c4009
	  __close_nocancel_nostatus (new_fd);
Packit 6c4009
	  return NULL;
Packit 6c4009
	}
Packit 6c4009
      __close_nocancel_nostatus (new_fd);
Packit 6c4009
      file_writable = true;
Packit 6c4009
    }
Packit 6c4009
Packit Bot f3904a
  /* Exclude other writers before validating the cache.  */
Packit Bot f3904a
  if (try_file_lock (file_fd, F_WRLCK))
Packit Bot f3904a
    return NULL;
Packit Bot f3904a
Packit 6c4009
  /* Find the correct place to insert the data.  */
Packit Bot f3904a
  bool found = false;
Packit Bot f5dbca
  if (matches_last_entry (data))
Packit 6c4009
    {
Packit Bot 80015a
      /* Read back the entry under the write lock.  */
Packit Bot 80015a
      file_offset -= sizeof (last_entry);
Packit Bot 80015a
      ssize_t nbytes = read_last_entry ();
Packit Bot 80015a
      if (nbytes < 0)
Packit 6c4009
	{
Packit Bot f3904a
	  file_unlock (file_fd);
Packit 6c4009
	  return NULL;
Packit 6c4009
	}
Packit Bot 80015a
Packit Bot 80015a
      if (nbytes == 0)
Packit Bot 80015a
	/* End of file reached.  */
Packit Bot 80015a
	found = false;
Packit Bot f3904a
      else
Packit Bot f5dbca
	found = matches_last_entry (data);
Packit 6c4009
    }
Packit 6c4009
Packit Bot f3904a
  if (!found)
Packit Bot 80015a
    /* Search forward for the entry.  */
Packit Bot f3904a
    found = internal_getut_nolock (data) >= 0;
Packit 6c4009
Packit Bot 80015a
  off64_t write_offset;
Packit Bot f3904a
  if (!found)
Packit 6c4009
    {
Packit 6c4009
      /* We append the next entry.  */
Packit Bot 80015a
      write_offset = __lseek64 (file_fd, 0, SEEK_END);
Packit Bot 80015a
Packit Bot 80015a
      /* Round down to the next multiple of the entry size.  This
Packit Bot 80015a
	 ensures any partially-written record is overwritten by the
Packit Bot 80015a
	 new record.  */
Packit Bot 80015a
      write_offset = (write_offset / sizeof (struct utmp)
Packit Bot 80015a
		      * sizeof (struct utmp));
Packit 6c4009
    }
Packit 6c4009
  else
Packit Bot 80015a
    /* Overwrite last_entry.  */
Packit Bot 80015a
    write_offset = file_offset - sizeof (struct utmp);
Packit Bot 80015a
Packit Bot 80015a
  /* Write the new data.  */
Packit Bot 80015a
  ssize_t nbytes;
Packit Bot 80015a
  if (__lseek64 (file_fd, write_offset, SEEK_SET) < 0
Packit Bot 80015a
      || (nbytes = __write_nocancel (file_fd, data, sizeof (struct utmp))) < 0)
Packit 6c4009
    {
Packit Bot 80015a
      /* There is no need to recover the file position because all
Packit Bot 80015a
	 reads use pread64, and any future write is preceded by
Packit Bot 80015a
	 another seek.  */
Packit Bot 80015a
      file_unlock (file_fd);
Packit Bot 80015a
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit Bot 80015a
  if (nbytes != sizeof (struct utmp))
Packit 6c4009
    {
Packit 6c4009
      /* If we appended a new record this is only partially written.
Packit 6c4009
	 Remove it.  */
Packit Bot f3904a
      if (!found)
Packit Bot 80015a
	(void) __ftruncate64 (file_fd, write_offset);
Packit Bot 80015a
      file_unlock (file_fd);
Packit Bot 80015a
      /* Assume that the write failure was due to missing disk
Packit Bot 80015a
	 space.  */
Packit Bot 80015a
      __set_errno (ENOSPC);
Packit Bot 80015a
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit Bot 565700
  file_unlock (file_fd);
Packit Bot 80015a
  file_offset = write_offset + sizeof (struct utmp);
Packit Bot 80015a
  pbuf = (struct utmp *) data;
Packit 6c4009
Packit 6c4009
  return pbuf;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit Bot 20692a
void
Packit Bot 20692a
__libc_endutent (void)
Packit 6c4009
{
Packit Bot 20692a
  if (file_fd >= 0)
Packit Bot 20692a
    {
Packit Bot 20692a
      __close_nocancel_nostatus (file_fd);
Packit Bot 20692a
      file_fd = -1;
Packit Bot 20692a
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit Bot 20692a
int
Packit Bot 20692a
__libc_updwtmp (const char *file, const struct utmp *utmp)
Packit 6c4009
{
Packit 6c4009
  int result = -1;
Packit 6c4009
  off64_t offset;
Packit 6c4009
  int fd;
Packit 6c4009
Packit 6c4009
  /* Open WTMP file.  */
Packit 6c4009
  fd = __open_nocancel (file, O_WRONLY | O_LARGEFILE);
Packit 6c4009
  if (fd < 0)
Packit 6c4009
    return -1;
Packit 6c4009
Packit Bot 490693
  if (try_file_lock (fd, F_WRLCK))
Packit Bot 565700
    {
Packit Bot 565700
      __close_nocancel_nostatus (fd);
Packit Bot 565700
      return -1;
Packit Bot 565700
    }
Packit 6c4009
Packit 6c4009
  /* Remember original size of log file.  */
Packit 6c4009
  offset = __lseek64 (fd, 0, SEEK_END);
Packit 6c4009
  if (offset % sizeof (struct utmp) != 0)
Packit 6c4009
    {
Packit 6c4009
      offset -= offset % sizeof (struct utmp);
Packit 6c4009
      __ftruncate64 (fd, offset);
Packit 6c4009
Packit 6c4009
      if (__lseek64 (fd, 0, SEEK_END) < 0)
Packit 6c4009
	goto unlock_return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Write the entry.  If we can't write all the bytes, reset the file
Packit 6c4009
     size back to the original size.  That way, no partial entries
Packit 6c4009
     will remain.  */
Packit 6c4009
  if (__write_nocancel (fd, utmp, sizeof (struct utmp))
Packit 6c4009
      != sizeof (struct utmp))
Packit 6c4009
    {
Packit 6c4009
      __ftruncate64 (fd, offset);
Packit 6c4009
      goto unlock_return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  result = 0;
Packit 6c4009
Packit 6c4009
unlock_return:
Packit Bot 2c8614
  file_unlock (fd);
Packit 6c4009
Packit 6c4009
  /* Close WTMP file.  */
Packit 6c4009
  __close_nocancel_nostatus (fd);
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}