Skip to content

Commit

Permalink
JRUBY-4937: Add RubyFile.getUmaskSafe() to cache/sync and avoid troun…
Browse files Browse the repository at this point in the history
…cing umask

Signed-off-by: Hiro Asari <asari.ruby@gmail.com>
  • Loading branch information
dekellum authored and headius committed Sep 27, 2010
1 parent a12d176 commit d65e1a8
Showing 1 changed file with 30 additions and 4 deletions.
34 changes: 30 additions & 4 deletions src/org/jruby/RubyFile.java
Expand Up @@ -93,6 +93,9 @@ public class RubyFile extends RubyIO implements EncodingCapable {
private static final int FNM_CASEFOLD = 8;
private static final int FNM_SYSCASE;

private static int _cachedUmask = 0;
private static final Object _umaskLock = new Object();

static {
if (Platform.IS_WINDOWS) {
FNM_SYSCASE = FNM_CASEFOLD;
Expand Down Expand Up @@ -516,7 +519,8 @@ protected void sysopenInternal(String path, ModeFlags modes, int perm) throws In

openFile.setPath(path);
openFile.setMode(modes.getOpenFileFlags());
int umask = getRuntime().getPosix().umask(0);

int umask = getUmaskSafe( getRuntime() );
perm = perm - (perm & umask);

ChannelDescriptor descriptor = sysopen(path, modes, perm);
Expand Down Expand Up @@ -1602,10 +1606,13 @@ public static IRubyObject umask(ThreadContext context, IRubyObject recv, IRubyOb
Ruby runtime = context.getRuntime();
int oldMask = 0;
if (args.length == 0) {
oldMask = runtime.getPosix().umask(0);
runtime.getPosix().umask(oldMask);
oldMask = getUmaskSafe( runtime );
} else if (args.length == 1) {
oldMask = runtime.getPosix().umask((int) args[0].convertToInteger().getLongValue());
int newMask = (int) args[0].convertToInteger().getLongValue();
synchronized (_umaskLock) {
oldMask = runtime.getPosix().umask(newMask);
_cachedUmask = newMask;
}
} else {
runtime.newArgumentError("wrong number of arguments");
}
Expand Down Expand Up @@ -1686,6 +1693,25 @@ public static ZipEntry getDirOrFileEntry(ZipFile zf, String path) throws IOExcep
return entry;
}

/**
* Joy of POSIX, only way to get the umask is to set the umask,
* then set it back. That's unsafe in a threaded program. We
* minimize but may not totally remove this race by caching the
* obtained or previously set (see umask() above) umask and using
* that as the initial set value which, cross fingers, is a
* no-op. The cache access is then synchronized. TODO: Better?
*/
private static int getUmaskSafe( Ruby runtime ) {
synchronized (_umaskLock) {
final int umask = runtime.getPosix().umask(_cachedUmask);
if (_cachedUmask != umask ) {
runtime.getPosix().umask(umask);
_cachedUmask = umask;
}
return umask;
}
}

/**
* Extract a timeval (an array of 2 longs: seconds and microseconds from epoch) from
* an IRubyObject.
Expand Down

0 comments on commit d65e1a8

Please sign in to comment.