|
|
d8307d |
commit d4625a19fe64f664119a541b317fb83de01bb273
|
|
|
d8307d |
Author: Florian Weimer <fweimer@redhat.com>
|
|
|
d8307d |
Date: Tue Nov 12 12:25:49 2019 +0100
|
|
|
d8307d |
|
|
|
d8307d |
login: Use pread64 in utmp implementation
|
|
|
d8307d |
|
|
|
d8307d |
This reduces the possible error scenarios considerably because
|
|
|
d8307d |
no longer can file seek fail, leaving the file descriptor in an
|
|
|
d8307d |
inconsistent state and out of sync with the cache.
|
|
|
d8307d |
|
|
|
d8307d |
As a result, it is possible to avoid setting file_offset to -1
|
|
|
d8307d |
to make an error persistent. Instead, subsequent calls will retry
|
|
|
d8307d |
the operation and report any errors returned by the kernel.
|
|
|
d8307d |
|
|
|
d8307d |
This change also avoids reading the file from the start if pututline
|
|
|
d8307d |
is called multiple times, to work around lock acquisition failures
|
|
|
d8307d |
due to timeouts.
|
|
|
d8307d |
|
|
|
d8307d |
Change-Id: If21ea0c162c38830a89331ea93cddec14c0974de
|
|
|
d8307d |
|
|
|
d8307d |
diff --git a/login/utmp_file.c b/login/utmp_file.c
|
|
|
d8307d |
index e653d14967c4fb7a..c828a28ac54c150e 100644
|
|
|
d8307d |
--- a/login/utmp_file.c
|
|
|
d8307d |
+++ b/login/utmp_file.c
|
|
|
d8307d |
@@ -162,12 +162,35 @@ maybe_setutent (void)
|
|
|
d8307d |
return file_fd >= 0 || __libc_setutent ();
|
|
|
d8307d |
}
|
|
|
d8307d |
|
|
|
d8307d |
+/* Reads the entry at file_offset, storing it in last_entry and
|
|
|
d8307d |
+ updating file_offset on success. Returns -1 for a read error, 0
|
|
|
d8307d |
+ for EOF, and 1 for a successful read. last_entry and file_offset
|
|
|
d8307d |
+ are only updated on a successful and complete read. */
|
|
|
d8307d |
+static ssize_t
|
|
|
d8307d |
+read_last_entry (void)
|
|
|
d8307d |
+{
|
|
|
d8307d |
+ struct utmp buffer;
|
|
|
d8307d |
+ ssize_t nbytes = __pread64_nocancel (file_fd, &buffer, sizeof (buffer),
|
|
|
d8307d |
+ file_offset);
|
|
|
d8307d |
+ if (nbytes < 0)
|
|
|
d8307d |
+ return -1;
|
|
|
d8307d |
+ else if (nbytes != sizeof (buffer))
|
|
|
d8307d |
+ /* Assume EOF. */
|
|
|
d8307d |
+ return 0;
|
|
|
d8307d |
+ else
|
|
|
d8307d |
+ {
|
|
|
d8307d |
+ last_entry = buffer;
|
|
|
d8307d |
+ file_offset += sizeof (buffer);
|
|
|
d8307d |
+ return 1;
|
|
|
d8307d |
+ }
|
|
|
d8307d |
+}
|
|
|
d8307d |
+
|
|
|
d8307d |
int
|
|
|
d8307d |
__libc_getutent_r (struct utmp *buffer, struct utmp **result)
|
|
|
d8307d |
{
|
|
|
d8307d |
- ssize_t nbytes;
|
|
|
d8307d |
+ int saved_errno = errno;
|
|
|
d8307d |
|
|
|
d8307d |
- if (!maybe_setutent () || file_offset == -1l)
|
|
|
d8307d |
+ if (!maybe_setutent ())
|
|
|
d8307d |
{
|
|
|
d8307d |
/* Not available. */
|
|
|
d8307d |
*result = NULL;
|
|
|
d8307d |
@@ -175,25 +198,22 @@ __libc_getutent_r (struct utmp *buffer, struct utmp **result)
|
|
|
d8307d |
}
|
|
|
d8307d |
|
|
|
d8307d |
if (try_file_lock (file_fd, F_RDLCK))
|
|
|
d8307d |
- nbytes = 0;
|
|
|
d8307d |
- else
|
|
|
d8307d |
- {
|
|
|
d8307d |
- /* Read the next entry. */
|
|
|
d8307d |
- nbytes = __read_nocancel (file_fd, &last_entry, sizeof (struct utmp));
|
|
|
d8307d |
- file_unlock (file_fd);
|
|
|
d8307d |
- }
|
|
|
d8307d |
+ return -1;
|
|
|
d8307d |
|
|
|
d8307d |
- if (nbytes != sizeof (struct utmp))
|
|
|
d8307d |
+ ssize_t nbytes = read_last_entry ();
|
|
|
d8307d |
+ file_unlock (file_fd);
|
|
|
d8307d |
+
|
|
|
d8307d |
+ if (nbytes <= 0) /* Read error or EOF. */
|
|
|
d8307d |
{
|
|
|
d8307d |
- if (nbytes != 0)
|
|
|
d8307d |
- file_offset = -1l;
|
|
|
d8307d |
+ if (nbytes == 0)
|
|
|
d8307d |
+ /* errno should be unchanged to indicate success. A premature
|
|
|
d8307d |
+ EOF is treated like an EOF (missing complete record at the
|
|
|
d8307d |
+ end). */
|
|
|
d8307d |
+ __set_errno (saved_errno);
|
|
|
d8307d |
*result = NULL;
|
|
|
d8307d |
return -1;
|
|
|
d8307d |
}
|
|
|
d8307d |
|
|
|
d8307d |
- /* Update position pointer. */
|
|
|
d8307d |
- file_offset += sizeof (struct utmp);
|
|
|
d8307d |
-
|
|
|
d8307d |
memcpy (buffer, &last_entry, sizeof (struct utmp));
|
|
|
d8307d |
*result = buffer;
|
|
|
d8307d |
|
|
|
d8307d |
@@ -209,15 +229,15 @@ internal_getut_nolock (const struct utmp *id)
|
|
|
d8307d |
{
|
|
|
d8307d |
while (1)
|
|
|
d8307d |
{
|
|
|
d8307d |
- /* Read the next entry. */
|
|
|
d8307d |
- if (__read_nocancel (file_fd, &last_entry, sizeof (struct utmp))
|
|
|
d8307d |
- != sizeof (struct utmp))
|
|
|
d8307d |
+ ssize_t nbytes = read_last_entry ();
|
|
|
d8307d |
+ if (nbytes < 0)
|
|
|
d8307d |
+ return -1;
|
|
|
d8307d |
+ if (nbytes == 0)
|
|
|
d8307d |
{
|
|
|
d8307d |
+ /* End of file reached. */
|
|
|
d8307d |
__set_errno (ESRCH);
|
|
|
d8307d |
- file_offset = -1l;
|
|
|
d8307d |
return -1;
|
|
|
d8307d |
}
|
|
|
d8307d |
- file_offset += sizeof (struct utmp);
|
|
|
d8307d |
|
|
|
d8307d |
if (matches_last_entry (id))
|
|
|
d8307d |
break;
|
|
|
d8307d |
@@ -249,7 +269,7 @@ int
|
|
|
d8307d |
__libc_getutid_r (const struct utmp *id, struct utmp *buffer,
|
|
|
d8307d |
struct utmp **result)
|
|
|
d8307d |
{
|
|
|
d8307d |
- if (!maybe_setutent () || file_offset == -1l)
|
|
|
d8307d |
+ if (!maybe_setutent ())
|
|
|
d8307d |
{
|
|
|
d8307d |
*result = NULL;
|
|
|
d8307d |
return -1;
|
|
|
d8307d |
@@ -276,7 +296,7 @@ int
|
|
|
d8307d |
__libc_getutline_r (const struct utmp *line, struct utmp *buffer,
|
|
|
d8307d |
struct utmp **result)
|
|
|
d8307d |
{
|
|
|
d8307d |
- if (!maybe_setutent () || file_offset == -1l)
|
|
|
d8307d |
+ if (!maybe_setutent ())
|
|
|
d8307d |
{
|
|
|
d8307d |
*result = NULL;
|
|
|
d8307d |
return -1;
|
|
|
d8307d |
@@ -290,16 +310,21 @@ __libc_getutline_r (const struct utmp *line, struct utmp *buffer,
|
|
|
d8307d |
|
|
|
d8307d |
while (1)
|
|
|
d8307d |
{
|
|
|
d8307d |
- /* Read the next entry. */
|
|
|
d8307d |
- if (__read_nocancel (file_fd, &last_entry, sizeof (struct utmp))
|
|
|
d8307d |
- != sizeof (struct utmp))
|
|
|
d8307d |
+ ssize_t nbytes = read_last_entry ();
|
|
|
d8307d |
+ if (nbytes < 0)
|
|
|
d8307d |
{
|
|
|
d8307d |
+ file_unlock (file_fd);
|
|
|
d8307d |
+ *result = NULL;
|
|
|
d8307d |
+ return -1;
|
|
|
d8307d |
+ }
|
|
|
d8307d |
+ if (nbytes == 0)
|
|
|
d8307d |
+ {
|
|
|
d8307d |
+ /* End of file reached. */
|
|
|
d8307d |
+ file_unlock (file_fd);
|
|
|
d8307d |
__set_errno (ESRCH);
|
|
|
d8307d |
- file_offset = -1l;
|
|
|
d8307d |
*result = NULL;
|
|
|
d8307d |
- goto unlock_return;
|
|
|
d8307d |
+ return -1;
|
|
|
d8307d |
}
|
|
|
d8307d |
- file_offset += sizeof (struct utmp);
|
|
|
d8307d |
|
|
|
d8307d |
/* Stop if we found a user or login entry. */
|
|
|
d8307d |
if ((last_entry.ut_type == USER_PROCESS
|
|
|
d8307d |
@@ -309,20 +334,18 @@ __libc_getutline_r (const struct utmp *line, struct utmp *buffer,
|
|
|
d8307d |
break;
|
|
|
d8307d |
}
|
|
|
d8307d |
|
|
|
d8307d |
+ file_unlock (file_fd);
|
|
|
d8307d |
memcpy (buffer, &last_entry, sizeof (struct utmp));
|
|
|
d8307d |
*result = buffer;
|
|
|
d8307d |
|
|
|
d8307d |
-unlock_return:
|
|
|
d8307d |
- file_unlock (file_fd);
|
|
|
d8307d |
-
|
|
|
d8307d |
- return ((*result == NULL) ? -1 : 0);
|
|
|
d8307d |
+ return 0;
|
|
|
d8307d |
}
|
|
|
d8307d |
|
|
|
d8307d |
|
|
|
d8307d |
struct utmp *
|
|
|
d8307d |
__libc_pututline (const struct utmp *data)
|
|
|
d8307d |
{
|
|
|
d8307d |
- if (!maybe_setutent () || file_offset == -1l)
|
|
|
d8307d |
+ if (!maybe_setutent ())
|
|
|
d8307d |
return NULL;
|
|
|
d8307d |
|
|
|
d8307d |
struct utmp *pbuf;
|
|
|
d8307d |
@@ -337,8 +360,7 @@ __libc_pututline (const struct utmp *data)
|
|
|
d8307d |
if (new_fd == -1)
|
|
|
d8307d |
return NULL;
|
|
|
d8307d |
|
|
|
d8307d |
- if (__lseek64 (new_fd, __lseek64 (file_fd, 0, SEEK_CUR), SEEK_SET) == -1
|
|
|
d8307d |
- || __dup2 (new_fd, file_fd) < 0)
|
|
|
d8307d |
+ if (__dup2 (new_fd, file_fd) < 0)
|
|
|
d8307d |
{
|
|
|
d8307d |
__close_nocancel_nostatus (new_fd);
|
|
|
d8307d |
return NULL;
|
|
|
d8307d |
@@ -355,69 +377,70 @@ __libc_pututline (const struct utmp *data)
|
|
|
d8307d |
bool found = false;
|
|
|
d8307d |
if (matches_last_entry (data))
|
|
|
d8307d |
{
|
|
|
d8307d |
- if (__lseek64 (file_fd, file_offset, SEEK_SET) < 0)
|
|
|
d8307d |
+ /* Read back the entry under the write lock. */
|
|
|
d8307d |
+ file_offset -= sizeof (last_entry);
|
|
|
d8307d |
+ ssize_t nbytes = read_last_entry ();
|
|
|
d8307d |
+ if (nbytes < 0)
|
|
|
d8307d |
{
|
|
|
d8307d |
file_unlock (file_fd);
|
|
|
d8307d |
return NULL;
|
|
|
d8307d |
}
|
|
|
d8307d |
- if (__read_nocancel (file_fd, &last_entry, sizeof (last_entry))
|
|
|
d8307d |
- != sizeof (last_entry))
|
|
|
d8307d |
- {
|
|
|
d8307d |
- if (__lseek64 (file_fd, file_offset, SEEK_SET) < 0)
|
|
|
d8307d |
- {
|
|
|
d8307d |
- file_unlock (file_fd);
|
|
|
d8307d |
- return NULL;
|
|
|
d8307d |
- }
|
|
|
d8307d |
- found = false;
|
|
|
d8307d |
- }
|
|
|
d8307d |
+
|
|
|
d8307d |
+ if (nbytes == 0)
|
|
|
d8307d |
+ /* End of file reached. */
|
|
|
d8307d |
+ found = false;
|
|
|
d8307d |
else
|
|
|
d8307d |
found = matches_last_entry (data);
|
|
|
d8307d |
}
|
|
|
d8307d |
|
|
|
d8307d |
if (!found)
|
|
|
d8307d |
+ /* Search forward for the entry. */
|
|
|
d8307d |
found = internal_getut_nolock (data) >= 0;
|
|
|
d8307d |
|
|
|
d8307d |
+ off64_t write_offset;
|
|
|
d8307d |
if (!found)
|
|
|
d8307d |
{
|
|
|
d8307d |
/* We append the next entry. */
|
|
|
d8307d |
- file_offset = __lseek64 (file_fd, 0, SEEK_END);
|
|
|
d8307d |
- if (file_offset % sizeof (struct utmp) != 0)
|
|
|
d8307d |
- {
|
|
|
d8307d |
- file_offset -= file_offset % sizeof (struct utmp);
|
|
|
d8307d |
- __ftruncate64 (file_fd, file_offset);
|
|
|
d8307d |
-
|
|
|
d8307d |
- if (__lseek64 (file_fd, 0, SEEK_END) < 0)
|
|
|
d8307d |
- {
|
|
|
d8307d |
- pbuf = NULL;
|
|
|
d8307d |
- goto unlock_return;
|
|
|
d8307d |
- }
|
|
|
d8307d |
- }
|
|
|
d8307d |
+ write_offset = __lseek64 (file_fd, 0, SEEK_END);
|
|
|
d8307d |
+
|
|
|
d8307d |
+ /* Round down to the next multiple of the entry size. This
|
|
|
d8307d |
+ ensures any partially-written record is overwritten by the
|
|
|
d8307d |
+ new record. */
|
|
|
d8307d |
+ write_offset = (write_offset / sizeof (struct utmp)
|
|
|
d8307d |
+ * sizeof (struct utmp));
|
|
|
d8307d |
}
|
|
|
d8307d |
else
|
|
|
d8307d |
+ /* Overwrite last_entry. */
|
|
|
d8307d |
+ write_offset = file_offset - sizeof (struct utmp);
|
|
|
d8307d |
+
|
|
|
d8307d |
+ /* Write the new data. */
|
|
|
d8307d |
+ ssize_t nbytes;
|
|
|
d8307d |
+ if (__lseek64 (file_fd, write_offset, SEEK_SET) < 0
|
|
|
d8307d |
+ || (nbytes = __write_nocancel (file_fd, data, sizeof (struct utmp))) < 0)
|
|
|
d8307d |
{
|
|
|
d8307d |
- /* We replace the just read entry. */
|
|
|
d8307d |
- file_offset -= sizeof (struct utmp);
|
|
|
d8307d |
- __lseek64 (file_fd, file_offset, SEEK_SET);
|
|
|
d8307d |
+ /* There is no need to recover the file position because all
|
|
|
d8307d |
+ reads use pread64, and any future write is preceded by
|
|
|
d8307d |
+ another seek. */
|
|
|
d8307d |
+ file_unlock (file_fd);
|
|
|
d8307d |
+ return NULL;
|
|
|
d8307d |
}
|
|
|
d8307d |
|
|
|
d8307d |
- /* Write the new data. */
|
|
|
d8307d |
- if (__write_nocancel (file_fd, data, sizeof (struct utmp))
|
|
|
d8307d |
- != sizeof (struct utmp))
|
|
|
d8307d |
+ if (nbytes != sizeof (struct utmp))
|
|
|
d8307d |
{
|
|
|
d8307d |
/* If we appended a new record this is only partially written.
|
|
|
d8307d |
Remove it. */
|
|
|
d8307d |
if (!found)
|
|
|
d8307d |
- (void) __ftruncate64 (file_fd, file_offset);
|
|
|
d8307d |
- pbuf = NULL;
|
|
|
d8307d |
- }
|
|
|
d8307d |
- else
|
|
|
d8307d |
- {
|
|
|
d8307d |
- file_offset += sizeof (struct utmp);
|
|
|
d8307d |
- pbuf = (struct utmp *) data;
|
|
|
d8307d |
+ (void) __ftruncate64 (file_fd, write_offset);
|
|
|
d8307d |
+ file_unlock (file_fd);
|
|
|
d8307d |
+ /* Assume that the write failure was due to missing disk
|
|
|
d8307d |
+ space. */
|
|
|
d8307d |
+ __set_errno (ENOSPC);
|
|
|
d8307d |
+ return NULL;
|
|
|
d8307d |
}
|
|
|
d8307d |
|
|
|
d8307d |
- unlock_return:
|
|
|
d8307d |
file_unlock (file_fd);
|
|
|
d8307d |
+ file_offset = write_offset + sizeof (struct utmp);
|
|
|
d8307d |
+ pbuf = (struct utmp *) data;
|
|
|
d8307d |
|
|
|
d8307d |
return pbuf;
|
|
|
d8307d |
}
|