1/*
2 * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26#include <unistd.h>
27#include <assert.h>
28#include <sys/types.h>
29#include <sys/time.h>
30#include <sys/stat.h>
31#ifdef MACOSX
32#include <sys/param.h>
33#include <sys/mount.h>
34#else
35#include <sys/statvfs.h>
36#endif
37#include <string.h>
38#include <stdlib.h>
39#include <dlfcn.h>
40#include <limits.h>
41#include <errno.h>
42#include <fcntl.h>
43#include <dirent.h>
44
45#include "jni.h"
46#include "jni_util.h"
47#include "jlong.h"
48#include "io_util.h"
49#include "io_util_md.h"
50#include "java_io_FileSystem.h"
51#include "java_io_UnixFileSystem.h"
52
53#if defined(_AIX)
54 #if !defined(NAME_MAX)
55 #define NAME_MAX MAXNAMLEN
56 #endif
57 #define DIR DIR64
58 #define dirent dirent64
59 #define opendir opendir64
60 #define readdir readdir64
61 #define closedir closedir64
62 #define stat stat64
63#endif
64
65#if defined(__solaris__) && !defined(NAME_MAX)
66 #define NAME_MAX MAXNAMLEN
67#endif
68
69#if defined(_ALLBSD_SOURCE)
70 #ifndef MACOSX
71 #define statvfs64 statvfs
72 #define stat64 stat
73 #endif
74#endif
75
76/* -- Field IDs -- */
77
78static struct {
79 jfieldID path;
80} ids;
81
82
83JNIEXPORT void JNICALL
84Java_java_io_UnixFileSystem_initIDs(JNIEnv *env, jclass cls)
85{
86 jclass fileClass = (*env)->FindClass(env, "java/io/File");
87 if (!fileClass) return;
88 ids.path = (*env)->GetFieldID(env, fileClass,
89 "path", "Ljava/lang/String;");
90}
91
92/* -- Path operations -- */
93
94extern int canonicalize(char *path, const char *out, int len);
95
96JNIEXPORT jstring JNICALL
97Java_java_io_UnixFileSystem_canonicalize0(JNIEnv *env, jobject this,
98 jstring pathname)
99{
100 jstring rv = NULL;
101
102 WITH_PLATFORM_STRING(env, pathname, path) {
103 char canonicalPath[PATH_MAX];
104 if (canonicalize((char *)path,
105 canonicalPath, PATH_MAX) < 0) {
106 JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
107 } else {
108#ifdef MACOSX
109 rv = newStringPlatform(env, canonicalPath);
110#else
111 rv = JNU_NewStringPlatform(env, canonicalPath);
112#endif
113 }
114 } END_PLATFORM_STRING(env, path);
115 return rv;
116}
117
118
119/* -- Attribute accessors -- */
120
121
122static jboolean
123statMode(const char *path, int *mode)
124{
125 struct stat64 sb;
126 if (stat64(path, &sb) == 0) {
127 *mode = sb.st_mode;
128 return JNI_TRUE;
129 }
130 return JNI_FALSE;
131}
132
133
134JNIEXPORT jint JNICALL
135Java_java_io_UnixFileSystem_getBooleanAttributes0(JNIEnv *env, jobject this,
136 jobject file)
137{
138 jint rv = 0;
139
140 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
141 int mode;
142 if (statMode(path, &mode)) {
143 int fmt = mode & S_IFMT;
144 rv = (jint) (java_io_FileSystem_BA_EXISTS
145 | ((fmt == S_IFREG) ? java_io_FileSystem_BA_REGULAR : 0)
146 | ((fmt == S_IFDIR) ? java_io_FileSystem_BA_DIRECTORY : 0));
147 }
148 } END_PLATFORM_STRING(env, path);
149 return rv;
150}
151
152JNIEXPORT jboolean JNICALL
153Java_java_io_UnixFileSystem_checkAccess(JNIEnv *env, jobject this,
154 jobject file, jint a)
155{
156 jboolean rv = JNI_FALSE;
157 int mode = 0;
158 switch (a) {
159 case java_io_FileSystem_ACCESS_READ:
160 mode = R_OK;
161 break;
162 case java_io_FileSystem_ACCESS_WRITE:
163 mode = W_OK;
164 break;
165 case java_io_FileSystem_ACCESS_EXECUTE:
166 mode = X_OK;
167 break;
168 default: assert(0);
169 }
170 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
171 int res;
172 RESTARTABLE(access(path, mode), res);
173 if (res == 0) {
174 rv = JNI_TRUE;
175 }
176 } END_PLATFORM_STRING(env, path);
177 return rv;
178}
179
180
181JNIEXPORT jboolean JNICALL
182Java_java_io_UnixFileSystem_setPermission(JNIEnv *env, jobject this,
183 jobject file,
184 jint access,
185 jboolean enable,
186 jboolean owneronly)
187{
188 jboolean rv = JNI_FALSE;
189
190 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
191 int amode = 0;
192 int mode;
193 int res;
194 switch (access) {
195 case java_io_FileSystem_ACCESS_READ:
196 if (owneronly)
197 amode = S_IRUSR;
198 else
199 amode = S_IRUSR | S_IRGRP | S_IROTH;
200 break;
201 case java_io_FileSystem_ACCESS_WRITE:
202 if (owneronly)
203 amode = S_IWUSR;
204 else
205 amode = S_IWUSR | S_IWGRP | S_IWOTH;
206 break;
207 case java_io_FileSystem_ACCESS_EXECUTE:
208 if (owneronly)
209 amode = S_IXUSR;
210 else
211 amode = S_IXUSR | S_IXGRP | S_IXOTH;
212 break;
213 default:
214 assert(0);
215 }
216 if (statMode(path, &mode)) {
217 if (enable)
218 mode |= amode;
219 else
220 mode &= ~amode;
221 RESTARTABLE(chmod(path, mode), res);
222 if (res == 0) {
223 rv = JNI_TRUE;
224 }
225 }
226 } END_PLATFORM_STRING(env, path);
227 return rv;
228}
229
230JNIEXPORT jlong JNICALL
231Java_java_io_UnixFileSystem_getLastModifiedTime(JNIEnv *env, jobject this,
232 jobject file)
233{
234 jlong rv = 0;
235
236 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
237 struct stat64 sb;
238 if (stat64(path, &sb) == 0) {
239#if defined(_AIX)
240 rv = (jlong)sb.st_mtime * 1000;
241 rv += (jlong)sb.st_mtime_n / 1000000;
242#elif defined(MACOSX)
243 rv = (jlong)sb.st_mtimespec.tv_sec * 1000;
244 rv += (jlong)sb.st_mtimespec.tv_nsec / 1000000;
245#else
246 rv = (jlong)sb.st_mtim.tv_sec * 1000;
247 rv += (jlong)sb.st_mtim.tv_nsec / 1000000;
248#endif
249 }
250 } END_PLATFORM_STRING(env, path);
251 return rv;
252}
253
254
255JNIEXPORT jlong JNICALL
256Java_java_io_UnixFileSystem_getLength(JNIEnv *env, jobject this,
257 jobject file)
258{
259 jlong rv = 0;
260
261 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
262 struct stat64 sb;
263 if (stat64(path, &sb) == 0) {
264 rv = sb.st_size;
265 }
266 } END_PLATFORM_STRING(env, path);
267 return rv;
268}
269
270
271/* -- File operations -- */
272
273
274JNIEXPORT jboolean JNICALL
275Java_java_io_UnixFileSystem_createFileExclusively(JNIEnv *env, jclass cls,
276 jstring pathname)
277{
278 jboolean rv = JNI_FALSE;
279
280 WITH_PLATFORM_STRING(env, pathname, path) {
281 FD fd;
282 /* The root directory always exists */
283 if (strcmp (path, "/")) {
284 fd = handleOpen(path, O_RDWR | O_CREAT | O_EXCL, 0666);
285 if (fd < 0) {
286 if (errno != EEXIST)
287 JNU_ThrowIOExceptionWithLastError(env, "Could not open file");
288 } else {
289 if (close(fd) == -1)
290 JNU_ThrowIOExceptionWithLastError(env, "Could not close file");
291 rv = JNI_TRUE;
292 }
293 }
294 } END_PLATFORM_STRING(env, path);
295 return rv;
296}
297
298
299JNIEXPORT jboolean JNICALL
300Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this,
301 jobject file)
302{
303 jboolean rv = JNI_FALSE;
304
305 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
306 if (remove(path) == 0) {
307 rv = JNI_TRUE;
308 }
309 } END_PLATFORM_STRING(env, path);
310 return rv;
311}
312
313
314JNIEXPORT jobjectArray JNICALL
315Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this,
316 jobject file)
317{
318 DIR *dir = NULL;
319 struct dirent *ptr;
320 int len, maxlen;
321 jobjectArray rv, old;
322 jclass str_class;
323
324 str_class = JNU_ClassString(env);
325 CHECK_NULL_RETURN(str_class, NULL);
326
327 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
328 dir = opendir(path);
329 } END_PLATFORM_STRING(env, path);
330 if (dir == NULL) return NULL;
331
332 /* Allocate an initial String array */
333 len = 0;
334 maxlen = 16;
335 rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
336 if (rv == NULL) goto error;
337
338 /* Scan the directory */
339 while ((ptr = readdir(dir)) != NULL) {
340 jstring name;
341 if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
342 continue;
343 if (len == maxlen) {
344 old = rv;
345 rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
346 if (rv == NULL) goto error;
347 if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
348 (*env)->DeleteLocalRef(env, old);
349 }
350#ifdef MACOSX
351 name = newStringPlatform(env, ptr->d_name);
352#else
353 name = JNU_NewStringPlatform(env, ptr->d_name);
354#endif
355 if (name == NULL) goto error;
356 (*env)->SetObjectArrayElement(env, rv, len++, name);
357 (*env)->DeleteLocalRef(env, name);
358 }
359 closedir(dir);
360
361 /* Copy the final results into an appropriately-sized array */
362 if (len < maxlen) {
363 old = rv;
364 rv = (*env)->NewObjectArray(env, len, str_class, NULL);
365 if (rv == NULL) {
366 return NULL;
367 }
368 if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
369 return NULL;
370 }
371 }
372 return rv;
373
374 error:
375 closedir(dir);
376 return NULL;
377}
378
379
380JNIEXPORT jboolean JNICALL
381Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this,
382 jobject file)
383{
384 jboolean rv = JNI_FALSE;
385
386 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
387 if (mkdir(path, 0777) == 0) {
388 rv = JNI_TRUE;
389 }
390 } END_PLATFORM_STRING(env, path);
391 return rv;
392}
393
394
395JNIEXPORT jboolean JNICALL
396Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this,
397 jobject from, jobject to)
398{
399 jboolean rv = JNI_FALSE;
400
401 WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) {
402 WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) {
403 if (rename(fromPath, toPath) == 0) {
404 rv = JNI_TRUE;
405 }
406 } END_PLATFORM_STRING(env, toPath);
407 } END_PLATFORM_STRING(env, fromPath);
408 return rv;
409}
410
411JNIEXPORT jboolean JNICALL
412Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
413 jobject file, jlong time)
414{
415 jboolean rv = JNI_FALSE;
416
417 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
418 struct stat64 sb;
419
420 if (stat64(path, &sb) == 0) {
421 struct timeval tv[2];
422
423 /* Preserve access time */
424#if defined(_AIX)
425 tv[0].tv_sec = sb.st_atime;
426 tv[0].tv_usec = sb.st_atime_n / 1000;
427#elif defined(MACOSX)
428 tv[0].tv_sec = sb.st_atimespec.tv_sec;
429 tv[0].tv_usec = sb.st_atimespec.tv_nsec / 1000;
430#else
431 tv[0].tv_sec = sb.st_atim.tv_sec;
432 tv[0].tv_usec = sb.st_atim.tv_nsec / 1000;
433#endif
434 /* Change last-modified time */
435 tv[1].tv_sec = time / 1000;
436 tv[1].tv_usec = (time % 1000) * 1000;
437
438 if (utimes(path, tv) == 0)
439 rv = JNI_TRUE;
440 }
441 } END_PLATFORM_STRING(env, path);
442
443 return rv;
444}
445
446
447JNIEXPORT jboolean JNICALL
448Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this,
449 jobject file)
450{
451 jboolean rv = JNI_FALSE;
452
453 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
454 int mode;
455 int res;
456 if (statMode(path, &mode)) {
457 RESTARTABLE(chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)), res);
458 if (res == 0) {
459 rv = JNI_TRUE;
460 }
461 }
462 } END_PLATFORM_STRING(env, path);
463 return rv;
464}
465
466JNIEXPORT jlong JNICALL
467Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this,
468 jobject file, jint t)
469{
470 jlong rv = 0L;
471
472 WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
473#ifdef MACOSX
474 struct statfs fsstat;
475#else
476 struct statvfs64 fsstat;
477 int res;
478#endif
479 memset(&fsstat, 0, sizeof(fsstat));
480#ifdef MACOSX
481 if (statfs(path, &fsstat) == 0) {
482 switch(t) {
483 case java_io_FileSystem_SPACE_TOTAL:
484 rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
485 long_to_jlong(fsstat.f_blocks));
486 break;
487 case java_io_FileSystem_SPACE_FREE:
488 rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
489 long_to_jlong(fsstat.f_bfree));
490 break;
491 case java_io_FileSystem_SPACE_USABLE:
492 rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
493 long_to_jlong(fsstat.f_bavail));
494 break;
495 default:
496 assert(0);
497 }
498 }
499#else
500 RESTARTABLE(statvfs64(path, &fsstat), res);
501 if (res == 0) {
502 switch(t) {
503 case java_io_FileSystem_SPACE_TOTAL:
504 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
505 long_to_jlong(fsstat.f_blocks));
506 break;
507 case java_io_FileSystem_SPACE_FREE:
508 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
509 long_to_jlong(fsstat.f_bfree));
510 break;
511 case java_io_FileSystem_SPACE_USABLE:
512 rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
513 long_to_jlong(fsstat.f_bavail));
514 break;
515 default:
516 assert(0);
517 }
518 }
519#endif
520 } END_PLATFORM_STRING(env, path);
521 return rv;
522}
523
524JNIEXPORT jlong JNICALL
525Java_java_io_UnixFileSystem_getNameMax0(JNIEnv *env, jobject this,
526 jstring pathname)
527{
528 jlong length = -1;
529 WITH_PLATFORM_STRING(env, pathname, path) {
530 length = (jlong)pathconf(path, _PC_NAME_MAX);
531 } END_PLATFORM_STRING(env, path);
532 return length != -1 ? length : (jlong)NAME_MAX;
533}
534