Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8273111: Default timezone should return zone ID if /etc/localtime is valid but not canonicalization on linux #5327

Closed
wants to merge 5 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -38,6 +38,7 @@

#include "jvm.h"
#include "TimeZone_md.h"
#include "path_util.h"

static char *isFileIdentical(char* buf, size_t size, char *pathname);

@@ -77,6 +78,39 @@ static const char *ETC_ENVIRONMENT_FILE = "/etc/environment";

#if defined(__linux__) || defined(MACOSX)

/*
* remove repeated path separators ('/') in the giving 'path'.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given?

Copy link
Author

@wuyan0 wuyan0 Oct 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, fix it.

*/
static void
removeDuplicateSlashes(char* path)
{
char *left = path;
char *right = path;
char *end = path + strlen(path);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"char* end"? better to align with existing code style

Copy link
Author

@wuyan0 wuyan0 Oct 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, fixed it.

/*
* Find first '//'
*/
for (; right < end; right++) {
if (*right == '/' && *(right + 1) == '/') break;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this for loop necessary? Seems it's ok to merge with the nested loop below.

Copy link
Author

@wuyan0 wuyan0 Oct 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your suggestion, this for loop is indeed unnecessary. Removed it.


for (left = right; right < end; right++) {
// Skip sequence of multiple path-separators.
while (*right == '/' && *(right + 1) == '/') {
right++;
}

while (*right != '\0' && !(*right == '/' && *(right + 1) == '/')) {
*left++ = *right++;
}

if (*right == '\0') {
*left = '\0';
break;
}
}
}

Copy link
Member

@naotoj naotoj Oct 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few *(right + 1) references in the loops. Is there any possibility that it would run over the buffer?

Copy link
Author

@wuyan0 wuyan0 Oct 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wouldn't run over the buffer. Here end points to '\0'. At line 94 and line 99, right is less than end, so right + 1 is at most end. At line 103, if right equals end, *right != '\0' will be false and !(*right == '/' && *(right + 1) == '/') will not executed.

/*
* Returns a pointer to the zone ID portion of the given zoneinfo file
* name, or NULL if the given string doesn't contain "zoneinfo/".
@@ -287,17 +321,18 @@ getPlatformTimeZoneID()
* from /etc/localtime.)
*/
if (S_ISLNK(statbuf.st_mode)) {
/* canonicalize the path */
char resolvedpath[PATH_MAX + 1];
char *path = realpath(DEFAULT_ZONEINFO_FILE, resolvedpath);
if (path == NULL) {
if (errno != ENOTDIR) {
jio_fprintf(stderr, (const char *) "can't to get a symlink of %s\n",
DEFAULT_ZONEINFO_FILE);
}
char linkbuf[PATH_MAX+1];
int len;

if ((len = readlink(DEFAULT_ZONEINFO_FILE, linkbuf, sizeof(linkbuf)-1)) == -1) {
jio_fprintf(stderr, (const char *) "can't get a symlink of %s\n",
DEFAULT_ZONEINFO_FILE);
return NULL;
}
tz = getZoneName(resolvedpath);
linkbuf[len] = '\0';
removeDuplicateSlashes(linkbuf);
collapse(linkbuf);
tz = getZoneName(linkbuf);
if (tz != NULL) {
tz = strdup(tz);
return tz;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,155 +33,13 @@
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
#if !defined(_ALLBSD_SOURCE)
#include <alloca.h>
#endif

#include "jdk_util.h"
#include "path_util.h"

/* Note: The comments in this file use the terminology
defined in the java.io.File class */


/* Check the given name sequence to see if it can be further collapsed.
Return zero if not, otherwise return the number of names in the sequence. */

static int
collapsible(char *names)
{
char *p = names;
int dots = 0, n = 0;

while (*p) {
if ((p[0] == '.') && ((p[1] == '\0')
|| (p[1] == '/')
|| ((p[1] == '.') && ((p[2] == '\0')
|| (p[2] == '/'))))) {
dots = 1;
}
n++;
while (*p) {
if (*p == '/') {
p++;
break;
}
p++;
}
}
return (dots ? n : 0);
}


/* Split the names in the given name sequence,
replacing slashes with nulls and filling in the given index array */

static void
splitNames(char *names, char **ix)
{
char *p = names;
int i = 0;

while (*p) {
ix[i++] = p++;
while (*p) {
if (*p == '/') {
*p++ = '\0';
break;
}
p++;
}
}
}


/* Join the names in the given name sequence, ignoring names whose index
entries have been cleared and replacing nulls with slashes as needed */

static void
joinNames(char *names, int nc, char **ix)
{
int i;
char *p;

for (i = 0, p = names; i < nc; i++) {
if (!ix[i]) continue;
if (i > 0) {
p[-1] = '/';
}
if (p == ix[i]) {
p += strlen(p) + 1;
} else {
char *q = ix[i];
while ((*p++ = *q++));
}
}
*p = '\0';
}


/* Collapse "." and ".." names in the given path wherever possible.
A "." name may always be eliminated; a ".." name may be eliminated if it
follows a name that is neither "." nor "..". This is a syntactic operation
that performs no filesystem queries, so it should only be used to cleanup
after invoking the realpath() procedure. */

static void
collapse(char *path)
{
char *names = (path[0] == '/') ? path + 1 : path; /* Preserve first '/' */
int nc;
char **ix;
int i, j;
char *p, *q;

nc = collapsible(names);
if (nc < 2) return; /* Nothing to do */
ix = (char **)alloca(nc * sizeof(char *));
splitNames(names, ix);

for (i = 0; i < nc; i++) {
int dots = 0;

/* Find next occurrence of "." or ".." */
do {
char *p = ix[i];
if (p[0] == '.') {
if (p[1] == '\0') {
dots = 1;
break;
}
if ((p[1] == '.') && (p[2] == '\0')) {
dots = 2;
break;
}
}
i++;
} while (i < nc);
if (i >= nc) break;

/* At this point i is the index of either a "." or a "..", so take the
appropriate action and then continue the outer loop */
if (dots == 1) {
/* Remove this instance of "." */
ix[i] = 0;
}
else {
/* If there is a preceding name, remove both that name and this
instance of ".."; otherwise, leave the ".." as is */
for (j = i - 1; j >= 0; j--) {
if (ix[j]) break;
}
if (j < 0) continue;
ix[j] = 0;
ix[i] = 0;
}
/* i will be incremented at the top of the loop */
}

joinNames(names, nc, ix);
}


/* Convert a pathname to canonical form. The input path is assumed to contain
no duplicate slashes. On Solaris we can use realpath() to do most of the
work, though once that's done we still must collapse any remaining "." and