Removed
Link Here
|
1 |
/******************************************************************************* |
2 |
* Copyright (c) 2000, 2009 IBM Corporation and others. |
3 |
* All rights reserved. This program and the accompanying materials |
4 |
* are made available under the terms of the Eclipse Public License v1.0 |
5 |
* which accompanies this distribution, and is available at |
6 |
* http://www.eclipse.org/legal/epl-v10.html |
7 |
* |
8 |
* Contributors: |
9 |
* IBM Corporation - initial API and implementation |
10 |
* Andre Weinand (OTI Labs) |
11 |
*******************************************************************************/ |
12 |
|
13 |
#include <jni.h> |
14 |
#include <sys/types.h> |
15 |
#include <sys/stat.h> |
16 |
#include <unistd.h> |
17 |
#include <utime.h> |
18 |
#include <stdlib.h> |
19 |
#include <string.h> |
20 |
|
21 |
#include "../localfile.h" |
22 |
|
23 |
#include <CoreServices/CoreServices.h> |
24 |
|
25 |
#define USE_IMMUTABLE_FLAG 1 |
26 |
#define USE_ARCHIVE_FLAG 0 |
27 |
#define EFS_SYMLINK_SUPPORT 1 |
28 |
|
29 |
mode_t process_umask; |
30 |
|
31 |
/* |
32 |
* Get a null-terminated byte array from a java char array. |
33 |
* The byte array contains UTF8 encoding. |
34 |
* The returned bytearray needs to be freed when not used |
35 |
* anymore. Use free(result) to do that. |
36 |
*/ |
37 |
static jbyte* getUTF8ByteArray(JNIEnv *env, jcharArray target) { |
38 |
|
39 |
jchar *temp= (*env)->GetCharArrayElements(env, target, 0); |
40 |
jsize n= (*env)->GetArrayLength(env, target); |
41 |
|
42 |
CFStringRef sr= CFStringCreateWithCharacters(kCFAllocatorDefault, temp, n); |
43 |
CFIndex argStringSize= CFStringGetMaximumSizeForEncoding(n, kCFStringEncodingUTF8) + 1; |
44 |
jbyte *result= (jbyte*) calloc(argStringSize, sizeof(jbyte)); |
45 |
CFStringGetCString(sr, (char*) result, argStringSize, kCFStringEncodingUTF8); |
46 |
CFRelease(sr); |
47 |
|
48 |
(*env)->ReleaseCharArrayElements(env, target, temp, 0); |
49 |
|
50 |
return result; |
51 |
} |
52 |
|
53 |
/* |
54 |
* Class: org_eclipse_core_internal_filesystem_local_LocalFileNatives |
55 |
* Method: nativeAttributes |
56 |
* Signature: ()I |
57 |
*/ |
58 |
JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_filesystem_local_LocalFileNatives_nativeAttributes |
59 |
(JNIEnv *env, jclass clazz) { |
60 |
#if defined(EFS_SYMLINK_SUPPORT) |
61 |
return ATTRIBUTE_READ_ONLY | ATTRIBUTE_EXECUTABLE | ATTRIBUTE_SYMLINK | ATTRIBUTE_LINK_TARGET; |
62 |
#else |
63 |
return ATTRIBUTE_READ_ONLY | ATTRIBUTE_EXECUTABLE; |
64 |
#endif |
65 |
} |
66 |
|
67 |
/* |
68 |
* Class: org_eclipse_core_internal_filesystem_local_LocalFileNatives |
69 |
* Method: internalIsUnicode |
70 |
* Signature: ()Z |
71 |
*/ |
72 |
JNIEXPORT jboolean JNICALL Java_org_eclipse_core_internal_filesystem_local_LocalFileNatives_internalIsUnicode |
73 |
(JNIEnv *env, jclass clazz) { |
74 |
return JNI_TRUE; // MacOS X supports Unicode-based file names in UTF-8 encoding |
75 |
} |
76 |
|
77 |
/* |
78 |
* Converts a stat structure to IFileInfo |
79 |
*/ |
80 |
jboolean convertStatToFileInfo (JNIEnv *env, struct stat info, jobject fileInfo) { |
81 |
jclass cls; |
82 |
jmethodID mid; |
83 |
jboolean readOnly; |
84 |
|
85 |
cls = (*env)->GetObjectClass(env, fileInfo); |
86 |
if (cls == 0) return JNI_FALSE; |
87 |
|
88 |
// select interesting information |
89 |
//exists |
90 |
mid = (*env)->GetMethodID(env, cls, "setExists", "(Z)V"); |
91 |
if (mid == 0) return JNI_FALSE; |
92 |
(*env)->CallVoidMethod(env, fileInfo, mid, JNI_TRUE); |
93 |
|
94 |
// last modified |
95 |
mid = (*env)->GetMethodID(env, cls, "setLastModified", "(J)V"); |
96 |
if (mid == 0) return JNI_FALSE; |
97 |
(*env)->CallVoidMethod(env, fileInfo, mid, ((jlong) info.st_mtime) * 1000); /* lower bits */ |
98 |
|
99 |
// file length |
100 |
mid = (*env)->GetMethodID(env, cls, "setLength", "(J)V"); |
101 |
if (mid == 0) return JNI_FALSE; |
102 |
(*env)->CallVoidMethod(env, fileInfo, mid, (jlong)info.st_size); |
103 |
|
104 |
// folder or file? |
105 |
if (S_ISDIR(info.st_mode)) { |
106 |
mid = (*env)->GetMethodID(env, cls, "setAttribute", "(IZ)V"); |
107 |
if (mid == 0) return JNI_FALSE; |
108 |
(*env)->CallVoidMethod(env, fileInfo, mid, ATTRIBUTE_DIRECTORY, JNI_TRUE); |
109 |
} |
110 |
|
111 |
// read-only? |
112 |
readOnly = (info.st_mode & S_IWRITE) != S_IWRITE; |
113 |
#if USE_IMMUTABLE_FLAG |
114 |
if (!readOnly && ((info.st_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) != 0)) |
115 |
readOnly = true; |
116 |
#endif |
117 |
if (readOnly) { |
118 |
mid = (*env)->GetMethodID(env, cls, "setAttribute", "(IZ)V"); |
119 |
if (mid == 0) return JNI_FALSE; |
120 |
(*env)->CallVoidMethod(env, fileInfo, mid, ATTRIBUTE_READ_ONLY, JNI_TRUE); |
121 |
} |
122 |
|
123 |
#if USE_ARCHIVE_FLAG |
124 |
// archive? |
125 |
if ((info.st_flags & SF_ARCHIVED) == SF_ARCHIVED) { |
126 |
mid = (*env)->GetMethodID(env, cls, "setAttribute", "(IZ)V"); |
127 |
if (mid == 0) return JNI_FALSE; |
128 |
(*env)->CallVoidMethod(env, fileInfo, mid, ATTRIBUTE_ARCHIVE, JNI_TRUE); |
129 |
} |
130 |
#endif |
131 |
|
132 |
// executable? |
133 |
if ((info.st_mode & S_IXUSR) == S_IXUSR) { |
134 |
mid = (*env)->GetMethodID(env, cls, "setAttribute", "(IZ)V"); |
135 |
if (mid == 0) return JNI_FALSE; |
136 |
(*env)->CallVoidMethod(env, fileInfo, mid, ATTRIBUTE_EXECUTABLE, JNI_TRUE); |
137 |
} |
138 |
return JNI_TRUE; |
139 |
} |
140 |
|
141 |
#if defined(EFS_SYMLINK_SUPPORT) |
142 |
/* |
143 |
* Set symbolic link information in IFileInfo |
144 |
*/ |
145 |
jboolean setSymlinkInFileInfo (JNIEnv *env, jobject fileInfo, jstring linkTarget) { |
146 |
jclass cls; |
147 |
jmethodID mid; |
148 |
|
149 |
cls = (*env)->GetObjectClass(env, fileInfo); |
150 |
if (cls == 0) return JNI_FALSE; |
151 |
|
152 |
// set symlink attribute |
153 |
mid = (*env)->GetMethodID(env, cls, "setAttribute", "(IZ)V"); |
154 |
if (mid == 0) return JNI_FALSE; |
155 |
(*env)->CallVoidMethod(env, fileInfo, mid, ATTRIBUTE_SYMLINK, JNI_TRUE); |
156 |
|
157 |
// set link target |
158 |
mid = (*env)->GetMethodID(env, cls, "setStringAttribute", "(ILjava/lang/String;)V"); |
159 |
if (mid == 0) return JNI_FALSE; |
160 |
(*env)->CallVoidMethod(env, fileInfo, mid, ATTRIBUTE_LINK_TARGET, linkTarget); |
161 |
|
162 |
return JNI_TRUE; |
163 |
} |
164 |
#endif |
165 |
|
166 |
/* |
167 |
* Class: org_eclipse_core_internal_filesystem_local_LocalFileNatives |
168 |
* Method: internalGetFileInfo |
169 |
* Signature: ([CLorg/eclipse/core/filesystem/IFileInfo;)Z |
170 |
*/ |
171 |
JNIEXPORT jboolean JNICALL Java_org_eclipse_core_internal_filesystem_local_LocalFileNatives_internalGetFileInfo |
172 |
(JNIEnv *env, jclass clazz, jbyteArray target, jobject fileInfo) { |
173 |
// shouldn't ever be called - there is only a Unicode-specific call on MacOS X |
174 |
return JNI_FALSE; |
175 |
} |
176 |
|
177 |
/* |
178 |
* Class: org_eclipse_core_internal_filesystem_local_LocalFileNatives |
179 |
* Method: internalGetFileInfoW |
180 |
* Signature: ([CLorg/eclipse/core/filesystem/IFileInfo;)Z |
181 |
*/ |
182 |
JNIEXPORT jboolean JNICALL Java_org_eclipse_core_internal_filesystem_local_LocalFileNatives_internalGetFileInfoW |
183 |
(JNIEnv *env, jclass clazz, jcharArray target, jobject fileInfo) { |
184 |
|
185 |
struct stat info; |
186 |
jint code; |
187 |
jstring linkTarget = NULL; |
188 |
|
189 |
/* get stat */ |
190 |
char *name= (char*) getUTF8ByteArray(env, target); |
191 |
#if defined(EFS_SYMLINK_SUPPORT) |
192 |
//do an lstat first to see if it is a symbolic link |
193 |
code = lstat(name, &info); |
194 |
if (code == 0 && (S_ISLNK(info.st_mode))) { |
195 |
//symbolic link: read link target |
196 |
char buf[PATH_MAX+1]; |
197 |
int len; |
198 |
len = readlink((const char*)name, buf, PATH_MAX); |
199 |
if (len>0) { |
200 |
buf[len]=0; |
201 |
} else { |
202 |
buf[0]=0; |
203 |
} |
204 |
// Mac OS encodes symlink target using UTF-8, ignoring platform default |
205 |
linkTarget = (*env)->NewStringUTF(env, buf); |
206 |
setSymlinkInFileInfo(env, fileInfo, linkTarget); |
207 |
|
208 |
//stat link target (will fail for broken links) |
209 |
code = stat((const char*)name, &info); |
210 |
} |
211 |
#else |
212 |
code = stat(name, &info); |
213 |
#endif |
214 |
free(name); |
215 |
|
216 |
/* test if an error occurred */ |
217 |
if (code == -1) |
218 |
return JNI_FALSE; |
219 |
return convertStatToFileInfo(env, info, fileInfo); |
220 |
} |
221 |
|
222 |
/* |
223 |
* Class: org_eclipse_core_internal_filesystem_local_LocalFileNatives |
224 |
* Method: internalCopyAttributes |
225 |
* Signature: ([B[BZ)Z |
226 |
*/ |
227 |
JNIEXPORT jboolean JNICALL Java_org_eclipse_core_internal_filesystem_local_LocalFileNatives_internalCopyAttributes |
228 |
(JNIEnv *env, jclass clazz, jbyteArray source, jbyteArray destination, jboolean copyLastModified) { |
229 |
// shouldn't ever be called - there is only a Unicode-specific call on MacOS X |
230 |
return JNI_FALSE; |
231 |
} |
232 |
|
233 |
/* |
234 |
* Class: org_eclipse_core_internal_filesystem_local_LocalFileNatives |
235 |
* Method: internalCopyAttributesW |
236 |
* Signature: ([C[CZ)Z |
237 |
*/ |
238 |
JNIEXPORT jboolean JNICALL Java_org_eclipse_core_internal_filesystem_local_LocalFileNatives_internalCopyAttributesW |
239 |
(JNIEnv *env, jclass clazz, jcharArray source, jcharArray destination, jboolean copyLastModified) { |
240 |
|
241 |
struct stat info; |
242 |
struct utimbuf ut; |
243 |
int code; |
244 |
char *sourceFile= (char*) getUTF8ByteArray(env, source); |
245 |
char *destinationFile= (char*) getUTF8ByteArray(env, destination); |
246 |
|
247 |
code= stat(sourceFile, &info); |
248 |
if (code != 0) goto fail; |
249 |
code= chmod(destinationFile, info.st_mode); |
250 |
if (code != 0) goto fail; |
251 |
|
252 |
chflags(destinationFile, info.st_flags); // ignore return code |
253 |
if (copyLastModified) { |
254 |
ut.actime= info.st_atime; |
255 |
ut.modtime= info.st_mtime; |
256 |
code= utime(destinationFile, &ut); |
257 |
} |
258 |
|
259 |
fail: |
260 |
free(sourceFile); |
261 |
free(destinationFile); |
262 |
return code == 0; |
263 |
} |
264 |
|
265 |
/* |
266 |
* Class: org_eclipse_core_internal_filesystem_local_LocalFileNatives |
267 |
* Method: internalSetFileInfo |
268 |
* Signature: ([BLorg/eclipse/core/filesystem/IFileInfo;)Z |
269 |
*/ |
270 |
JNIEXPORT jboolean JNICALL Java_org_eclipse_core_internal_filesystem_local_LocalFileNatives_internalSetFileInfo |
271 |
(JNIEnv *env, jclass clazz, jcharArray target, jobject obj) { |
272 |
// shouldn't ever be called - there is only a Unicode-specific call on MacOS X |
273 |
return JNI_FALSE; |
274 |
|
275 |
} |
276 |
|
277 |
mode_t getumask() { |
278 |
// in case something goes wrong just return 63 which is 0077 in octals |
279 |
mode_t mask = 63; |
280 |
|
281 |
int fds[2]; |
282 |
if (pipe(fds) == -1) { |
283 |
return mask; |
284 |
} |
285 |
|
286 |
pid_t child_pid; |
287 |
child_pid = fork(); |
288 |
|
289 |
if (child_pid == 0) { |
290 |
// child process |
291 |
ssize_t bytes_written = 0; |
292 |
close(fds[0]); |
293 |
mask = umask(0); |
294 |
while (1) { |
295 |
ssize_t written = write(fds[1], &mask + bytes_written, sizeof(mask) - bytes_written); |
296 |
if (written == -1) { |
297 |
if (errno != EINTR) { |
298 |
break; |
299 |
} |
300 |
} else { |
301 |
bytes_written += written; |
302 |
if (bytes_written == sizeof(mask)) { |
303 |
break; |
304 |
} |
305 |
} |
306 |
} |
307 |
close(fds[1]); |
308 |
_exit(0); |
309 |
} else if (child_pid != -1) { |
310 |
// parent process, fork succeded |
311 |
int stat_loc; |
312 |
ssize_t bytes_read = 0; |
313 |
mode_t buf; |
314 |
close(fds[1]); |
315 |
while (1) { |
316 |
ssize_t b_read = read(fds[0], &buf + bytes_read, sizeof(buf) - bytes_read); |
317 |
if (b_read == -1) { |
318 |
if (errno != EINTR) { |
319 |
break; |
320 |
} |
321 |
} else { |
322 |
if (b_read == 0) { |
323 |
break; |
324 |
} |
325 |
bytes_read += b_read; |
326 |
if (bytes_read == sizeof(mask)) { |
327 |
break; |
328 |
} |
329 |
} |
330 |
} |
331 |
if (bytes_read == sizeof(mask)) { |
332 |
mask = buf; |
333 |
} |
334 |
close(fds[0]); |
335 |
waitpid(child_pid, &stat_loc, 0); |
336 |
} else { |
337 |
// parent process, fork failed |
338 |
close(fds[0]); |
339 |
close(fds[1]); |
340 |
} |
341 |
|
342 |
return mask; |
343 |
} |
344 |
|
345 |
/* |
346 |
* Class: org_eclipse_core_internal_filesystem_local_LocalFileNatives |
347 |
* Method: internalSetFileInfoW |
348 |
* Signature: ([BLorg/eclipse/core/filesystem/IFileInfo;)Z |
349 |
*/ |
350 |
JNIEXPORT jboolean JNICALL Java_org_eclipse_core_internal_filesystem_local_LocalFileNatives_internalSetFileInfoW |
351 |
(JNIEnv *env, jclass clazz, jcharArray target, jobject obj, jint options) { |
352 |
jint code = -1; |
353 |
jmethodID mid; |
354 |
jboolean executable, readOnly, archive; |
355 |
jclass cls; |
356 |
|
357 |
/* find out if we need to set the readonly bit */ |
358 |
cls = (*env)->GetObjectClass(env, obj); |
359 |
mid = (*env)->GetMethodID(env, cls, "getAttribute", "(I)Z"); |
360 |
if (mid == 0) goto fail; |
361 |
readOnly = (*env)->CallBooleanMethod(env, obj, mid, ATTRIBUTE_READ_ONLY); |
362 |
|
363 |
/* find out if we need to set the executable bit */ |
364 |
executable = (*env)->CallBooleanMethod(env, obj, mid, ATTRIBUTE_EXECUTABLE); |
365 |
|
366 |
/* find out if we need to set the archive bit */ |
367 |
archive = (*env)->CallBooleanMethod(env, obj, mid, ATTRIBUTE_ARCHIVE); |
368 |
|
369 |
/* get the current permissions */ |
370 |
jbyte *name = getUTF8ByteArray(env, target); |
371 |
struct stat info; |
372 |
int result= stat((char*)name, &info); |
373 |
if (result != 0) goto fail; |
374 |
|
375 |
/* create the mask for the relevant bits */ |
376 |
mode_t mask = info.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); |
377 |
mode_t oldmask = mask; |
378 |
int flags = info.st_flags; |
379 |
|
380 |
#if USE_ARCHIVE_FLAG |
381 |
if (archive) |
382 |
flags |= SF_ARCHIVED; // set archive bit |
383 |
else |
384 |
flags &= ~SF_ARCHIVED; // clear archive bit |
385 |
#endif |
386 |
|
387 |
if (executable) |
388 |
mask |= S_IXUSR | ((S_IXGRP | S_IXOTH) & ~process_umask); |
389 |
else |
390 |
mask &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // clear all 'x' |
391 |
|
392 |
if (readOnly) { |
393 |
mask &= ~(S_IWUSR | S_IWGRP | S_IWOTH); // clear all 'w' |
394 |
#if USE_IMMUTABLE_FLAG |
395 |
flags |= UF_IMMUTABLE; // set immutable flag for usr |
396 |
#endif |
397 |
} else { |
398 |
mask |= S_IRUSR | S_IWUSR | ((S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) & ~process_umask); |
399 |
#if USE_IMMUTABLE_FLAG |
400 |
flags &= ~UF_IMMUTABLE; // try to clear immutable flags for usr |
401 |
#endif |
402 |
} |
403 |
|
404 |
/* call chmod & chflags syscalls in correct order */ |
405 |
if (readOnly) { |
406 |
if (mask != oldmask) |
407 |
result |= chmod((char*)name, mask); |
408 |
if (flags != info.st_flags) |
409 |
result |= chflags((char*)name, flags); |
410 |
} else { |
411 |
if (flags != info.st_flags) |
412 |
result |= chflags((char*)name, flags); |
413 |
if (mask != oldmask) |
414 |
result |= chmod((char*)name, mask); |
415 |
} |
416 |
|
417 |
fail: |
418 |
free(name); |
419 |
return result == 0; |
420 |
} |
421 |
|
422 |
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { |
423 |
process_umask = getumask(); |
424 |
return JNI_VERSION_1_1; |
425 |
} |