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 Service a2f230
/* Returns true if *ENTRY matches last_entry, based on
Packit Service a2f230
   data->ut_type.  */
Packit Service a2f230
static bool
Packit Service a2f230
matches_last_entry (const struct utmp *data)
Packit Service a2f230
{
Packit Service a2f230
  if (file_offset <= 0)
Packit Service a2f230
    /* Nothing has been read.  last_entry is stale and cannot match.  */
Packit Service a2f230
    return false;
Packit Service a2f230
Packit Service a2f230
  if (data->ut_type == RUN_LVL
Packit Service a2f230
      || data->ut_type == BOOT_TIME
Packit Service a2f230
      || data->ut_type == OLD_TIME
Packit Service a2f230
      || data->ut_type == NEW_TIME)
Packit Service a2f230
    /* For some entry types, only a type match is required.  */
Packit Service a2f230
    return data->ut_type == last_entry.ut_type;
Packit Service a2f230
  else
Packit Service a2f230
    /* For the process-related entries, a full match is needed.  */
Packit Service a2f230
    return __utmp_equal (&last_entry, data);
Packit Service a2f230
}
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 Service 0efded
Packit Service 0efded
/* try_file_lock (LOCKING, FD, TYPE) returns true if the locking
Packit Service 0efded
   operation failed and recovery needs to be performed.
Packit Service 0efded
Packit Service 0efded
   file_unlock (FD) removes the lock (which must have been
Packit Service f08ff1
   successfully acquired). */
Packit Service 0efded
Packit Service 0efded
static bool
Packit Service f08ff1
try_file_lock (int fd, int type)
Packit Service 0efded
{
Packit Service 0efded
  /* Cancel any existing alarm.  */
Packit Service f08ff1
  int old_timeout = alarm (0);
Packit Service 0efded
Packit Service 0efded
  /* Establish signal handler.  */
Packit Service f08ff1
  struct sigaction old_action;
Packit Service 0efded
  struct sigaction action;
Packit Service 0efded
  action.sa_handler = timeout_handler;
Packit Service 0efded
  __sigemptyset (&action.sa_mask);
Packit Service 0efded
  action.sa_flags = 0;
Packit Service f08ff1
  __sigaction (SIGALRM, &action, &old_action);
Packit Service 0efded
Packit Service 0efded
  alarm (TIMEOUT);
Packit Service 0efded
Packit Service 0efded
  /* Try to get the lock.  */
Packit Service b1916a
 struct flock64 fl =
Packit Service 0efded
   {
Packit Service 0efded
    .l_type = type,
Packit Service 4aec34
    .l_whence = SEEK_SET,
Packit Service 0efded
   };
Packit Service f08ff1
Packit Service f08ff1
 bool status = __fcntl64_nocancel (fd, F_SETLKW, &fl) < 0;
Packit Service f08ff1
 int saved_errno = errno;
Packit Service f08ff1
Packit Service f08ff1
 /* Reset the signal handler and alarm.  We must reset the alarm
Packit Service f08ff1
    before resetting the handler so our alarm does not generate a
Packit Service f08ff1
    spurious SIGALRM seen by the user.  However, we cannot just set
Packit Service f08ff1
    the user's old alarm before restoring the handler, because then
Packit Service f08ff1
    it's possible our handler could catch the user alarm's SIGARLM and
Packit Service f08ff1
    then the user would never see the signal he expected.  */
Packit Service f08ff1
  alarm (0);
Packit Service f08ff1
  __sigaction (SIGALRM, &old_action, NULL);
Packit Service f08ff1
  if (old_timeout != 0)
Packit Service f08ff1
    alarm (old_timeout);
Packit Service f08ff1
Packit Service f08ff1
  __set_errno (saved_errno);
Packit Service f08ff1
  return status;
Packit Service 0efded
}
Packit Service 0efded
Packit Service 0efded
static void
Packit Service 0efded
file_unlock (int fd)
Packit Service 0efded
{
Packit Service b1916a
  struct flock64 fl =
Packit Service 0efded
    {
Packit Service 0efded
      .l_type = F_UNLCK,
Packit Service 0efded
    };
Packit Service 0efded
  __fcntl64_nocancel (fd, F_SETLKW, &fl);
Packit Service 0efded
}
Packit Service 0efded
Packit 6c4009
#ifndef TRANSFORM_UTMP_FILE_NAME
Packit 6c4009
# define TRANSFORM_UTMP_FILE_NAME(file_name) (file_name)
Packit 6c4009
#endif
Packit 6c4009
Packit Service a4dd66
int
Packit Service a4dd66
__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 Service a4dd66
/* Preform initialization if necessary.  */
Packit Service a4dd66
static bool
Packit Service a4dd66
maybe_setutent (void)
Packit Service a4dd66
{
Packit Service a4dd66
  return file_fd >= 0 || __libc_setutent ();
Packit Service a4dd66
}
Packit 6c4009
Packit Service 13a835
/* Reads the entry at file_offset, storing it in last_entry and
Packit Service 13a835
   updating file_offset on success.  Returns -1 for a read error, 0
Packit Service 13a835
   for EOF, and 1 for a successful read.  last_entry and file_offset
Packit Service 13a835
   are only updated on a successful and complete read.  */
Packit Service 13a835
static ssize_t
Packit Service 13a835
read_last_entry (void)
Packit Service 13a835
{
Packit Service 13a835
  struct utmp buffer;
Packit Service 13a835
  ssize_t nbytes = __pread64_nocancel (file_fd, &buffer, sizeof (buffer),
Packit Service 13a835
				       file_offset);
Packit Service 13a835
  if (nbytes < 0)
Packit Service 13a835
    return -1;
Packit Service 13a835
  else if (nbytes != sizeof (buffer))
Packit Service 13a835
    /* Assume EOF.  */
Packit Service 13a835
    return 0;
Packit Service 13a835
  else
Packit Service 13a835
    {
Packit Service 13a835
      last_entry = buffer;
Packit Service 13a835
      file_offset += sizeof (buffer);
Packit Service 13a835
      return 1;
Packit Service 13a835
    }
Packit Service 13a835
}
Packit Service 13a835
Packit Service a4dd66
int
Packit Service a4dd66
__libc_getutent_r (struct utmp *buffer, struct utmp **result)
Packit Service 5abeed
{
Packit Service 13a835
  int saved_errno = errno;
Packit Service 5abeed
Packit Service 13a835
  if (!maybe_setutent ())
Packit 6c4009
    {
Packit 6c4009
      /* Not available.  */
Packit 6c4009
      *result = NULL;
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit Service f08ff1
  if (try_file_lock (file_fd, F_RDLCK))
Packit Service 13a835
    return -1;
Packit Service 541783
Packit Service 13a835
  ssize_t nbytes = read_last_entry ();
Packit Service 13a835
  file_unlock (file_fd);
Packit Service 13a835
Packit Service 13a835
  if (nbytes <= 0)		/* Read error or EOF.  */
Packit 6c4009
    {
Packit Service 13a835
      if (nbytes == 0)
Packit Service 13a835
	/* errno should be unchanged to indicate success.  A premature
Packit Service 13a835
	   EOF is treated like an EOF (missing complete record at the
Packit Service 13a835
	   end).  */
Packit Service 13a835
	__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 Service abb4b6
/* Search for *ID, updating last_entry and file_offset.  Return 0 on
Packit Service f9b05c
   success and -1 on failure.  Does not perform locking; for that see
Packit Service f9b05c
   internal_getut_r below.  */
Packit 6c4009
static int
Packit Service f9b05c
internal_getut_nolock (const struct utmp *id)
Packit 6c4009
{
Packit Service a2f230
  while (1)
Packit 6c4009
    {
Packit Service 13a835
      ssize_t nbytes = read_last_entry ();
Packit Service 13a835
      if (nbytes < 0)
Packit Service 13a835
	return -1;
Packit Service 13a835
      if (nbytes == 0)
Packit 6c4009
	{
Packit Service 13a835
	  /* End of file reached.  */
Packit Service a2f230
	  __set_errno (ESRCH);
Packit Service a2f230
	  return -1;
Packit 6c4009
	}
Packit Service 541783
Packit Service a2f230
      if (matches_last_entry (id))
Packit Service a2f230
	break;
Packit 6c4009
    }
Packit 6c4009
Packit Service f9b05c
  return 0;
Packit Service f9b05c
}
Packit 6c4009
Packit Service f9b05c
/* Search for *ID, updating last_entry and file_offset.  Return 0 on
Packit Service f9b05c
   success and -1 on failure.  If the locking operation failed, write
Packit Service f9b05c
   true to *LOCK_FAILED.  */
Packit Service f9b05c
static int
Packit Service f9b05c
internal_getut_r (const struct utmp *id, bool *lock_failed)
Packit Service f9b05c
{
Packit Service f9b05c
  if (try_file_lock (file_fd, F_RDLCK))
Packit Service f9b05c
    {
Packit Service f9b05c
      *lock_failed = true;
Packit Service f9b05c
      return -1;
Packit Service f9b05c
    }
Packit 6c4009
Packit Service f9b05c
  int result = internal_getut_nolock (id);
Packit Service f9b05c
  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 Service a4dd66
int
Packit Service a4dd66
__libc_getutid_r (const struct utmp *id, struct utmp *buffer,
Packit Service a4dd66
		  struct utmp **result)
Packit 6c4009
{
Packit Service 13a835
  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 Service abb4b6
  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 Service a4dd66
int
Packit Service a4dd66
__libc_getutline_r (const struct utmp *line, struct utmp *buffer,
Packit Service a4dd66
		    struct utmp **result)
Packit 6c4009
{
Packit Service 13a835
  if (!maybe_setutent ())
Packit 6c4009
    {
Packit 6c4009
      *result = NULL;
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit Service f08ff1
  if (try_file_lock (file_fd, F_RDLCK))
Packit 6c4009
    {
Packit 6c4009
      *result = NULL;
Packit Service 0efded
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  while (1)
Packit 6c4009
    {
Packit Service 13a835
      ssize_t nbytes = read_last_entry ();
Packit Service 13a835
      if (nbytes < 0)
Packit Service 13a835
	{
Packit Service 13a835
	  file_unlock (file_fd);
Packit Service 13a835
	  *result = NULL;
Packit Service 13a835
	  return -1;
Packit Service 13a835
	}
Packit Service 13a835
      if (nbytes == 0)
Packit 6c4009
	{
Packit Service 13a835
	  /* End of file reached.  */
Packit Service 13a835
	  file_unlock (file_fd);
Packit 6c4009
	  __set_errno (ESRCH);
Packit 6c4009
	  *result = NULL;
Packit Service 13a835
	  return -1;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Stop if we found a user or login entry.  */
Packit Service 66ff64
      if ((last_entry.ut_type == USER_PROCESS
Packit 6c4009
	   || last_entry.ut_type == LOGIN_PROCESS)
Packit Service 66ff64
	  && (strncmp (line->ut_line, last_entry.ut_line, sizeof line->ut_line)
Packit Service 66ff64
	      == 0))
Packit 6c4009
	break;
Packit 6c4009
    }
Packit 6c4009
Packit Service 13a835
  file_unlock (file_fd);
Packit 6c4009
  memcpy (buffer, &last_entry, sizeof (struct utmp));
Packit 6c4009
  *result = buffer;
Packit 6c4009
Packit Service 13a835
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit Service a4dd66
struct utmp *
Packit Service a4dd66
__libc_pututline (const struct utmp *data)
Packit 6c4009
{
Packit Service 13a835
  if (!maybe_setutent ())
Packit Service a4dd66
    return NULL;
Packit Service a4dd66
Packit 6c4009
  struct utmp *pbuf;
Packit Service 541783
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 Service 13a835
      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 Service f9b05c
  /* Exclude other writers before validating the cache.  */
Packit Service f9b05c
  if (try_file_lock (file_fd, F_WRLCK))
Packit Service f9b05c
    return NULL;
Packit Service f9b05c
Packit 6c4009
  /* Find the correct place to insert the data.  */
Packit Service f9b05c
  bool found = false;
Packit Service a2f230
  if (matches_last_entry (data))
Packit 6c4009
    {
Packit Service 13a835
      /* Read back the entry under the write lock.  */
Packit Service 13a835
      file_offset -= sizeof (last_entry);
Packit Service 13a835
      ssize_t nbytes = read_last_entry ();
Packit Service 13a835
      if (nbytes < 0)
Packit 6c4009
	{
Packit Service f9b05c
	  file_unlock (file_fd);
Packit 6c4009
	  return NULL;
Packit 6c4009
	}
Packit Service 13a835
Packit Service 13a835
      if (nbytes == 0)
Packit Service 13a835
	/* End of file reached.  */
Packit Service 13a835
	found = false;
Packit Service f9b05c
      else
Packit Service a2f230
	found = matches_last_entry (data);
Packit 6c4009
    }
Packit 6c4009
Packit Service f9b05c
  if (!found)
Packit Service 13a835
    /* Search forward for the entry.  */
Packit Service f9b05c
    found = internal_getut_nolock (data) >= 0;
Packit 6c4009
Packit Service 13a835
  off64_t write_offset;
Packit Service f9b05c
  if (!found)
Packit 6c4009
    {
Packit 6c4009
      /* We append the next entry.  */
Packit Service 13a835
      write_offset = __lseek64 (file_fd, 0, SEEK_END);
Packit Service 13a835
Packit Service 13a835
      /* Round down to the next multiple of the entry size.  This
Packit Service 13a835
	 ensures any partially-written record is overwritten by the
Packit Service 13a835
	 new record.  */
Packit Service 13a835
      write_offset = (write_offset / sizeof (struct utmp)
Packit Service 13a835
		      * sizeof (struct utmp));
Packit 6c4009
    }
Packit 6c4009
  else
Packit Service 13a835
    /* Overwrite last_entry.  */
Packit Service 13a835
    write_offset = file_offset - sizeof (struct utmp);
Packit Service 13a835
Packit Service 13a835
  /* Write the new data.  */
Packit Service 13a835
  ssize_t nbytes;
Packit Service 13a835
  if (__lseek64 (file_fd, write_offset, SEEK_SET) < 0
Packit Service 13a835
      || (nbytes = __write_nocancel (file_fd, data, sizeof (struct utmp))) < 0)
Packit 6c4009
    {
Packit Service 13a835
      /* There is no need to recover the file position because all
Packit Service 13a835
	 reads use pread64, and any future write is preceded by
Packit Service 13a835
	 another seek.  */
Packit Service 13a835
      file_unlock (file_fd);
Packit Service 13a835
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit Service 13a835
  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 Service f9b05c
      if (!found)
Packit Service 13a835
	(void) __ftruncate64 (file_fd, write_offset);
Packit Service 13a835
      file_unlock (file_fd);
Packit Service 13a835
      /* Assume that the write failure was due to missing disk
Packit Service 13a835
	 space.  */
Packit Service 13a835
      __set_errno (ENOSPC);
Packit Service 13a835
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit Service 0efded
  file_unlock (file_fd);
Packit Service 13a835
  file_offset = write_offset + sizeof (struct utmp);
Packit Service 13a835
  pbuf = (struct utmp *) data;
Packit 6c4009
Packit 6c4009
  return pbuf;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit Service a4dd66
void
Packit Service a4dd66
__libc_endutent (void)
Packit 6c4009
{
Packit Service a4dd66
  if (file_fd >= 0)
Packit Service a4dd66
    {
Packit Service a4dd66
      __close_nocancel_nostatus (file_fd);
Packit Service a4dd66
      file_fd = -1;
Packit Service a4dd66
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit Service a4dd66
int
Packit Service a4dd66
__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 Service f08ff1
  if (try_file_lock (fd, F_WRLCK))
Packit Service 0efded
    {
Packit Service 0efded
      __close_nocancel_nostatus (fd);
Packit Service 0efded
      return -1;
Packit Service 0efded
    }
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 Service 2010ed
  file_unlock (fd);
Packit 6c4009
Packit 6c4009
  /* Close WTMP file.  */
Packit 6c4009
  __close_nocancel_nostatus (fd);
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}