system/nxpkg: Protect live locks from filesystem clock skew.

Record a per-boot token and owner PID in every package, installed-database, and synchronization lock. A contender now keeps a lock held while its recorded owner task is alive, regardless of unreliable FAT modification timestamps.

Reclaim locks from exited owners or earlier boots immediately, while retaining timestamp-based migration handling for legacy empty lock files. This prevents a just-created live lock from being mistaken for a decades-old stale file on targets whose mounted filesystem clock does not match CLOCK_REALTIME.

Assisted-by: OpenAI Codex:gpt-5.6-sol
Signed-off-by: aviralgarg05 <gargaviral99@gmail.com>
This commit is contained in:
aviralgarg05 2026-07-23 23:08:07 +05:30
parent 38c608326a
commit f363a8f444
4 changed files with 204 additions and 61 deletions

View file

@ -261,6 +261,7 @@ int pkg_resolve_icon_source(FAR char *buffer, size_t size,
FAR const struct pkg_manifest_s *manifest);
int pkg_acquire_source(FAR const char *source, FAR const char *dest,
FAR const char *renew_lock_path);
int pkg_lock_create(FAR const char *path);
void pkg_reclaim_stale_lock(FAR const char *path);
int pkg_sync(FAR const char *source);
int pkg_install(FAR const char *name);

View file

@ -25,13 +25,11 @@
****************************************************************************/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "pkg.h"
@ -43,7 +41,6 @@
static int pkg_install_acquire_lock(FAR const char *name, FAR char *path,
size_t size)
{
int fd;
int ret;
ret = pkg_store_ensure_package_root(name);
@ -58,20 +55,14 @@ static int pkg_install_acquire_lock(FAR const char *name, FAR char *path,
return ret;
}
fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
if (fd < 0 && errno == EEXIST)
ret = pkg_lock_create(path);
if (ret == -EEXIST)
{
pkg_reclaim_stale_lock(path);
fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
ret = pkg_lock_create(path);
}
if (fd < 0)
{
return errno == EEXIST ? -EBUSY : -errno;
}
close(fd);
return 0;
return ret == -EEXIST ? -EBUSY : ret;
}
/****************************************************************************
@ -85,7 +76,6 @@ static int pkg_install_acquire_lock(FAR const char *name, FAR char *path,
static int pkg_install_acquire_installed_lock(FAR char *path, size_t size)
{
int fd;
int ret;
int tries;
@ -102,16 +92,15 @@ static int pkg_install_acquire_installed_lock(FAR char *path, size_t size)
for (tries = 0; tries < 100; tries++)
{
fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
if (fd >= 0)
ret = pkg_lock_create(path);
if (ret == 0)
{
close(fd);
return 0;
}
if (errno != EEXIST)
if (ret != -EEXIST)
{
return -errno;
return ret;
}
pkg_reclaim_stale_lock(path);
@ -337,42 +326,6 @@ out:
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: pkg_reclaim_stale_lock
*
* Description:
* Remove "path" if it is old enough that it cannot belong to a still-
* running holder (see PKG_LOCK_STALE_SECONDS). Best-effort: any
* stat()/unlink() failure just falls through to the normal -EBUSY
* result, since a lock we can't inspect should be treated as held.
* Generic across every lock file this package uses (per-package
* install locks, the shared installed-db lock, pkg_repo.c's sync
* lock) - not install-specific despite living in this file.
*
****************************************************************************/
void pkg_reclaim_stale_lock(FAR const char *path)
{
struct stat st;
time_t now;
if (stat(path, &st) < 0)
{
return;
}
now = time(NULL);
if (now < st.st_mtime ||
(now - st.st_mtime) < PKG_LOCK_STALE_SECONDS)
{
return;
}
pkg_error("reclaiming stale lock '%s' (age %ld s)",
path, (long)(now - st.st_mtime));
unlink(path);
}
int pkg_install(FAR const char *name)
{
FAR struct pkg_index_s *index;

View file

@ -544,7 +544,6 @@ int pkg_acquire_source(FAR const char *source, FAR const char *dest,
static int pkg_repo_acquire_sync_lock(FAR char *path, size_t size)
{
int fd;
int ret;
int tries;
@ -561,16 +560,15 @@ static int pkg_repo_acquire_sync_lock(FAR char *path, size_t size)
for (tries = 0; tries < 100; tries++)
{
fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
if (fd >= 0)
ret = pkg_lock_create(path);
if (ret == 0)
{
close(fd);
return 0;
}
if (errno != EEXIST)
if (ret != -EEXIST)
{
return -errno;
return ret;
}
pkg_reclaim_stale_lock(path);

View file

@ -27,6 +27,10 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <pthread.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@ -36,10 +40,84 @@
#include "pkg.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define PKG_LOCK_RECORD_MAGIC "NXPKG1"
#define PKG_LOCK_RECORD_SIZE 64
/****************************************************************************
* Private Data
****************************************************************************/
static pthread_once_t g_pkg_lock_boot_once = PTHREAD_ONCE_INIT;
static uint64_t g_pkg_lock_boot_id;
/****************************************************************************
* Private Functions
****************************************************************************/
static void pkg_lock_init_boot_id(void)
{
arc4random_buf(&g_pkg_lock_boot_id, sizeof(g_pkg_lock_boot_id));
if (g_pkg_lock_boot_id == 0)
{
g_pkg_lock_boot_id = 1;
}
}
static uint64_t pkg_lock_get_boot_id(void)
{
if (pthread_once(&g_pkg_lock_boot_once, pkg_lock_init_boot_id) != 0)
{
return 1;
}
return g_pkg_lock_boot_id;
}
static int pkg_lock_read_owner(FAR const char *path,
FAR uint64_t *boot_id,
FAR pid_t *owner)
{
char record[PKG_LOCK_RECORD_SIZE];
unsigned long long parsed_boot;
long parsed_owner;
ssize_t nread;
int fd;
int ret;
fd = open(path, O_RDONLY);
if (fd < 0)
{
return -errno;
}
nread = read(fd, record, sizeof(record) - 1);
if (nread < 0)
{
ret = -errno;
close(fd);
return ret;
}
close(fd);
record[nread] = '\0';
ret = sscanf(record, PKG_LOCK_RECORD_MAGIC " %llx %ld",
&parsed_boot, &parsed_owner);
if (ret != 2 || parsed_owner <= 0 ||
(long)(pid_t)parsed_owner != parsed_owner)
{
return -EINVAL;
}
*boot_id = (uint64_t)parsed_boot;
*owner = (pid_t)parsed_owner;
return 0;
}
static int pkg_store_format(FAR char *buffer, size_t size,
FAR const char *fmt,
FAR const char *name,
@ -194,6 +272,119 @@ int pkg_store_prepare_layout(void)
return pkg_store_mkdirs(PKG_TMP_PKG_DIR);
}
int pkg_lock_create(FAR const char *path)
{
char record[PKG_LOCK_RECORD_SIZE];
int fd;
int ret;
fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
if (fd < 0)
{
return -errno;
}
ret = snprintf(record, sizeof(record), PKG_LOCK_RECORD_MAGIC
" %016" PRIx64 " %ld\n",
pkg_lock_get_boot_id(), (long)getpid());
if (ret < 0 || (size_t)ret >= sizeof(record))
{
ret = ret < 0 ? ret : -ENAMETOOLONG;
goto errout;
}
ret = pkg_store_write_all(fd, record, (size_t)ret);
if (ret < 0)
{
goto errout;
}
if (fsync(fd) < 0)
{
ret = -errno;
goto errout;
}
if (close(fd) < 0)
{
ret = -errno;
unlink(path);
return ret;
}
return 0;
errout:
close(fd);
unlink(path);
return ret;
}
void pkg_reclaim_stale_lock(FAR const char *path)
{
struct stat st;
uint64_t boot_id;
pid_t owner;
time_t now;
int ret;
ret = pkg_lock_read_owner(path, &boot_id, &owner);
if (ret == -EINVAL)
{
/* A creator may have completed open(O_EXCL) but not its first write.
* Give that very small window time to close before treating the file
* as a legacy timestamp-only lock.
*/
usleep(20 * 1000);
ret = pkg_lock_read_owner(path, &boot_id, &owner);
}
if (ret == 0)
{
if (boot_id != pkg_lock_get_boot_id())
{
pkg_error("reclaiming lock from an earlier boot '%s'", path);
unlink(path);
return;
}
if (kill(owner, 0) == 0 || errno == EPERM)
{
return;
}
if (errno == ESRCH)
{
pkg_error("reclaiming lock from exited task %ld '%s'",
(long)owner, path);
unlink(path);
}
return;
}
/* Compatibility for empty lock files created by older nxpkg images.
* Their only ownership information is the filesystem timestamp.
*/
if (stat(path, &st) < 0)
{
return;
}
now = time(NULL);
if (now < st.st_mtime ||
(now - st.st_mtime) < PKG_LOCK_STALE_SECONDS)
{
return;
}
pkg_error("reclaiming legacy stale lock '%s' (age %ld s)",
path, (long)(now - st.st_mtime));
unlink(path);
}
int pkg_store_ensure_package_root(FAR const char *name)
{
char path[PATH_MAX];