|
@@ -1258,13 +1258,39 @@ out:
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(do_settimeofday64);
|
|
EXPORT_SYMBOL(do_settimeofday64);
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Validates if a timespec/timeval used to inject a time offset is valid.
|
|
|
|
+ * Offsets can be postive or negative. The value of the timeval/timespec
|
|
|
|
+ * is the sum of its fields, but *NOTE*: the field tv_usec/tv_nsec must
|
|
|
|
+ * always be non-negative.
|
|
|
|
+ */
|
|
|
|
+static inline bool timeval_inject_offset_valid(const struct timeval *tv)
|
|
|
|
+{
|
|
|
|
+ /* We don't check the tv_sec as it can be positive or negative */
|
|
|
|
+
|
|
|
|
+ /* Can't have more microseconds then a second */
|
|
|
|
+ if (tv->tv_usec < 0 || tv->tv_usec >= USEC_PER_SEC)
|
|
|
|
+ return false;
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline bool timespec_inject_offset_valid(const struct timespec *ts)
|
|
|
|
+{
|
|
|
|
+ /* We don't check the tv_sec as it can be positive or negative */
|
|
|
|
+
|
|
|
|
+ /* Can't have more nanoseconds then a second */
|
|
|
|
+ if (ts->tv_nsec < 0 || ts->tv_nsec >= NSEC_PER_SEC)
|
|
|
|
+ return false;
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* timekeeping_inject_offset - Adds or subtracts from the current time.
|
|
* timekeeping_inject_offset - Adds or subtracts from the current time.
|
|
* @tv: pointer to the timespec variable containing the offset
|
|
* @tv: pointer to the timespec variable containing the offset
|
|
*
|
|
*
|
|
* Adds or subtracts an offset value from the current time.
|
|
* Adds or subtracts an offset value from the current time.
|
|
*/
|
|
*/
|
|
-int timekeeping_inject_offset(struct timespec *ts)
|
|
|
|
|
|
+static int timekeeping_inject_offset(struct timespec *ts)
|
|
{
|
|
{
|
|
struct timekeeper *tk = &tk_core.timekeeper;
|
|
struct timekeeper *tk = &tk_core.timekeeper;
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
@@ -1303,7 +1329,40 @@ error: /* even if we error out, we forwarded the time, so call update */
|
|
|
|
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
-EXPORT_SYMBOL(timekeeping_inject_offset);
|
|
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Indicates if there is an offset between the system clock and the hardware
|
|
|
|
+ * clock/persistent clock/rtc.
|
|
|
|
+ */
|
|
|
|
+int persistent_clock_is_local;
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Adjust the time obtained from the CMOS to be UTC time instead of
|
|
|
|
+ * local time.
|
|
|
|
+ *
|
|
|
|
+ * This is ugly, but preferable to the alternatives. Otherwise we
|
|
|
|
+ * would either need to write a program to do it in /etc/rc (and risk
|
|
|
|
+ * confusion if the program gets run more than once; it would also be
|
|
|
|
+ * hard to make the program warp the clock precisely n hours) or
|
|
|
|
+ * compile in the timezone information into the kernel. Bad, bad....
|
|
|
|
+ *
|
|
|
|
+ * - TYT, 1992-01-01
|
|
|
|
+ *
|
|
|
|
+ * The best thing to do is to keep the CMOS clock in universal time (UTC)
|
|
|
|
+ * as real UNIX machines always do it. This avoids all headaches about
|
|
|
|
+ * daylight saving times and warping kernel clocks.
|
|
|
|
+ */
|
|
|
|
+void timekeeping_warp_clock(void)
|
|
|
|
+{
|
|
|
|
+ if (sys_tz.tz_minuteswest != 0) {
|
|
|
|
+ struct timespec adjust;
|
|
|
|
+
|
|
|
|
+ persistent_clock_is_local = 1;
|
|
|
|
+ adjust.tv_sec = sys_tz.tz_minuteswest * 60;
|
|
|
|
+ adjust.tv_nsec = 0;
|
|
|
|
+ timekeeping_inject_offset(&adjust);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
|
|
/**
|
|
/**
|
|
* __timekeeping_set_tai_offset - Sets the TAI offset from UTC and monotonic
|
|
* __timekeeping_set_tai_offset - Sets the TAI offset from UTC and monotonic
|
|
@@ -2247,6 +2306,66 @@ ktime_t ktime_get_update_offsets_now(unsigned int *cwsseq, ktime_t *offs_real,
|
|
return base;
|
|
return base;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * ntp_validate_timex - Ensures the timex is ok for use in do_adjtimex
|
|
|
|
+ */
|
|
|
|
+static int ntp_validate_timex(struct timex *txc)
|
|
|
|
+{
|
|
|
|
+ if (txc->modes & ADJ_ADJTIME) {
|
|
|
|
+ /* singleshot must not be used with any other mode bits */
|
|
|
|
+ if (!(txc->modes & ADJ_OFFSET_SINGLESHOT))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ if (!(txc->modes & ADJ_OFFSET_READONLY) &&
|
|
|
|
+ !capable(CAP_SYS_TIME))
|
|
|
|
+ return -EPERM;
|
|
|
|
+ } else {
|
|
|
|
+ /* In order to modify anything, you gotta be super-user! */
|
|
|
|
+ if (txc->modes && !capable(CAP_SYS_TIME))
|
|
|
|
+ return -EPERM;
|
|
|
|
+ /*
|
|
|
|
+ * if the quartz is off by more than 10% then
|
|
|
|
+ * something is VERY wrong!
|
|
|
|
+ */
|
|
|
|
+ if (txc->modes & ADJ_TICK &&
|
|
|
|
+ (txc->tick < 900000/USER_HZ ||
|
|
|
|
+ txc->tick > 1100000/USER_HZ))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (txc->modes & ADJ_SETOFFSET) {
|
|
|
|
+ /* In order to inject time, you gotta be super-user! */
|
|
|
|
+ if (!capable(CAP_SYS_TIME))
|
|
|
|
+ return -EPERM;
|
|
|
|
+
|
|
|
|
+ if (txc->modes & ADJ_NANO) {
|
|
|
|
+ struct timespec ts;
|
|
|
|
+
|
|
|
|
+ ts.tv_sec = txc->time.tv_sec;
|
|
|
|
+ ts.tv_nsec = txc->time.tv_usec;
|
|
|
|
+ if (!timespec_inject_offset_valid(&ts))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ if (!timeval_inject_offset_valid(&txc->time))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Check for potential multiplication overflows that can
|
|
|
|
+ * only happen on 64-bit systems:
|
|
|
|
+ */
|
|
|
|
+ if ((txc->modes & ADJ_FREQUENCY) && (BITS_PER_LONG == 64)) {
|
|
|
|
+ if (LLONG_MIN / PPM_SCALE > txc->freq)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ if (LLONG_MAX / PPM_SCALE < txc->freq)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* do_adjtimex() - Accessor function to NTP __do_adjtimex function
|
|
* do_adjtimex() - Accessor function to NTP __do_adjtimex function
|
|
*/
|
|
*/
|