Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2#define _GNU_SOURCE
3#include <errno.h>
4#include <fcntl.h>
5#include <math.h>
6#include <sched.h>
7#include <stdio.h>
8#include <stdbool.h>
9#include <stdlib.h>
10#include <sys/stat.h>
11#include <sys/syscall.h>
12#include <sys/types.h>
13#include <time.h>
14#include <unistd.h>
15#include <time.h>
16
17#include "log.h"
18#include "timens.h"
19
20/*
21 * Test shouldn't be run for a day, so add 10 days to child
22 * time and check parent's time to be in the same day.
23 */
24#define MAX_TEST_TIME_SEC (60*5)
25#define DAY_IN_SEC (60*60*24)
26#define TEN_DAYS_IN_SEC (10*DAY_IN_SEC)
27
28#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
29
30static int child_ns, parent_ns;
31
32static int switch_ns(int fd)
33{
34 if (setns(fd, CLONE_NEWTIME))
35 return pr_perror("setns()");
36
37 return 0;
38}
39
40static int init_namespaces(void)
41{
42 char path[] = "/proc/self/ns/time_for_children";
43 struct stat st1, st2;
44
45 parent_ns = open(path, O_RDONLY);
46 if (parent_ns <= 0)
47 return pr_perror("Unable to open %s", path);
48
49 if (fstat(parent_ns, &st1))
50 return pr_perror("Unable to stat the parent timens");
51
52 if (unshare_timens())
53 return -1;
54
55 child_ns = open(path, O_RDONLY);
56 if (child_ns <= 0)
57 return pr_perror("Unable to open %s", path);
58
59 if (fstat(child_ns, &st2))
60 return pr_perror("Unable to stat the timens");
61
62 if (st1.st_ino == st2.st_ino)
63 return pr_err("The same child_ns after CLONE_NEWTIME");
64
65 if (_settime(CLOCK_BOOTTIME, TEN_DAYS_IN_SEC))
66 return -1;
67
68 return 0;
69}
70
71static int read_proc_uptime(struct timespec *uptime)
72{
73 unsigned long up_sec, up_nsec;
74 FILE *proc;
75
76 proc = fopen("/proc/uptime", "r");
77 if (proc == NULL) {
78 pr_perror("Unable to open /proc/uptime");
79 return -1;
80 }
81
82 if (fscanf(proc, "%lu.%02lu", &up_sec, &up_nsec) != 2) {
83 if (errno) {
84 pr_perror("fscanf");
85 return -errno;
86 }
87 pr_err("failed to parse /proc/uptime");
88 return -1;
89 }
90 fclose(proc);
91
92 uptime->tv_sec = up_sec;
93 uptime->tv_nsec = up_nsec;
94 return 0;
95}
96
97static int check_uptime(void)
98{
99 struct timespec uptime_new, uptime_old;
100 time_t uptime_expected;
101 double prec = MAX_TEST_TIME_SEC;
102
103 if (switch_ns(parent_ns))
104 return pr_err("switch_ns(%d)", parent_ns);
105
106 if (read_proc_uptime(&uptime_old))
107 return 1;
108
109 if (switch_ns(child_ns))
110 return pr_err("switch_ns(%d)", child_ns);
111
112 if (read_proc_uptime(&uptime_new))
113 return 1;
114
115 uptime_expected = uptime_old.tv_sec + TEN_DAYS_IN_SEC;
116 if (fabs(difftime(uptime_new.tv_sec, uptime_expected)) > prec) {
117 pr_fail("uptime in /proc/uptime: old %ld, new %ld [%ld]",
118 uptime_old.tv_sec, uptime_new.tv_sec,
119 uptime_old.tv_sec + TEN_DAYS_IN_SEC);
120 return 1;
121 }
122
123 ksft_test_result_pass("Passed for /proc/uptime\n");
124 return 0;
125}
126
127int main(int argc, char *argv[])
128{
129 int ret = 0;
130
131 nscheck();
132
133 ksft_set_plan(1);
134
135 if (init_namespaces())
136 return 1;
137
138 ret |= check_uptime();
139
140 if (ret)
141 ksft_exit_fail();
142 ksft_exit_pass();
143 return ret;
144}