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

Stop using process.umask() #6

Merged
merged 7 commits into from
May 10, 2020
Merged

Conversation

coreyfarrell
Copy link
Member

Ref nodejs/node#32321


This can result in a behavior change in an edge case:

  • We attempt to create ./dir without providing customMode.
  • ./dir already exists but has a difference mode than the current process.umask().

I think the current release would reset the existing directory to match the current umask where the new code will leave it alone.

@coreyfarrell
Copy link
Member Author

Sorry for pushing with broken tests, I realize this cannot be merged as is but hopefully showing the test errors can help us find a way to deal with this.

@phated
Copy link
Member

phated commented Apr 22, 2020

I don't really understand umask and pulled it from some other code. Nor do I really understand what is going on in the linked issues.

I assume this would be a breaking change so we probably couldn't upstream into gulp 4. Is there an interim solution?

@coreyfarrell
Copy link
Member Author

I don't know an interim solution. The issue is that libc does not provide a function to "query umask". The libc function is:

// Set the umask and return the old value
mode_t umask(mode_t mask);

So process.umask() is doing the equivilent of:

const old = process.umask(0);
process.umask(old);

So there is race condition where other threads would create files using an invalid umask of 0 (all new files would have 0o000 permission).

In theory a half-broken solution would be to create our own replacement umask function:

let oldMask = 0;
function getUmask() {
  let newMask = process.umask(oldMask);
  if (newMask !== oldMask) {
    process.umask(newMask);
    oldMask = newMask;
  }

  return newMask
}

Using this getUmask would avoid the deprecation error but would still be susceptible to race conditions during initialization as well as the first time getUmask is run after someone changes umask (example if someone uses process.umask(0o600)).

@phated
Copy link
Member

phated commented May 1, 2020

@coreyfarrell if we make this a breaking change, what do you think we should do? I just checked my default install of node and it looks like the default umask is 022, should we just use that as the mask?

@coreyfarrell
Copy link
Member Author

@phated it looks like I was incorrect in my previous assessment. This is not a breaking change, the existing code does only performs chmod on existing paths if a mode is explicitly set. The tests were failing because my first commit had a bug where it was passing the wrong mode value to recursive calls. Sorry for the noise.

About the default umask, node.js does not set this. It is actually set per user profile. For example on my system the umask for my normal user is 002 and the umask for root is 022.

@coreyfarrell
Copy link
Member Author

Additional note the CI failure at npm install is not caused by this PR. I tried nvm i 5 && npm i locally and got the same failure against both master and my own branch.

@phated
Copy link
Member

phated commented May 1, 2020

@coreyfarrell I still think we are talking past each other. The point of the masking in this was to replicate the behavior of the mkdir command on linux, which sets the mode on a directory if it exists but the mode is different. Nodejs doesn't do that for mkdir on MacOS, so we have to force it. I'm pretty sure these commands mask it.

@coreyfarrell
Copy link
Member Author

We have two paths for what to do when a directory already exists. When mode is provided the directory is chmod'ed (see https://github.com/gulpjs/fs-mkdirp-stream/blob/master/test/mkdirp.js#L201). When mode is not provided the existing mode is left alone (see https://github.com/gulpjs/fs-mkdirp-stream/blob/master/test/mkdirp.js#L146).

I'm going to be out for a while but when I get back I can work on enabling CI for MacOS?

@phated
Copy link
Member

phated commented May 1, 2020

I have a new scaffold to apply here that will add github actions with MacOS support. Should I do that and then we can continue work on this?

@coreyfarrell
Copy link
Member Author

I think that would be a good idea.

@phated
Copy link
Member

phated commented May 3, 2020

@coreyfarrell all set! Please let me know how else I can help here.

I'm wondering if we need some more tests to demonstrate how node-core's behavior is different between Linux and MacOS to highlight why the mask mode is needed.

@coreyfarrell
Copy link
Member Author

@phated I'm unsure why the CI is failing, it is giving an error I've never seen on the Checkout step. I tried refreshing the master branch of my fork to make sure it had the required github actions files then did a force-push of my own branch but this did not resolve the problem.

@phated
Copy link
Member

phated commented May 9, 2020

@coreyfarrell I'm sorry you were the first to run into this. It turns out that my usage of the prettier action was extremely flawed. I've adjusted the workflow to only format code that is pushed to master, so this should work for you now.

@phated
Copy link
Member

phated commented May 9, 2020

According to isaacs/node-mkdirp#22, the umask is applied at the operating system level. As with most things deep inside node, I don't trust it and would like to test that assumption.

@phated phated closed this May 9, 2020
@phated phated reopened this May 9, 2020
@phated
Copy link
Member

phated commented May 9, 2020

I disabled the coveralls warnings now.

@phated
Copy link
Member

phated commented May 9, 2020

Just pushed a change that updates the applyUmask test utility function that I believe helps test my other comment.

@phated
Copy link
Member

phated commented May 9, 2020

For completeness' sake, I just tried fs.mkdir(..., { mode: mode, recursive: true }) and it fails our test suite on MacOS. Of course, this was expected because libuv only defers to the underlying operating system and MacOS mkdir behaves differently than the one on linux. I might create a branch with those failures.

@phated
Copy link
Member

phated commented May 9, 2020

Alright, I just figured out the test case that I was concerned about, and I also think I figured out the way to solve it. I will get that shipped over sometime this weekend.

Sorry for the spam.

@phated
Copy link
Member

phated commented May 9, 2020

@coreyfarrell this went a bit off the rails, but I think I feel confident I understand how everything is working now.

Could you take a look at the changeset and the test runs with verbose logging enabled to see if you agree with everything that I did?

@coreyfarrell
Copy link
Member Author

@phated I'm not on a computer now but I'll be able to review at some point this weekend. Thanks for helping with the CI issues.

mkdirp.js Outdated
dirpath = path.resolve(dirpath);

fs.mkdir(dirpath, mode, onMkdir);
fs.mkdir(dirpath, { mode: mode }, onMkdir);
Copy link
Member Author

Choose a reason for hiding this comment

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

The options object form of fs.mkdir requires node.js v10.12.0. If you want to be compatible with node.js 10.0.0 then this needs to switch back to fs.mkdir(dirname, mode, onMkdir). If not I'd lean towards saying that package.json#engines.node should specify >=10.12.0.

Other than this I think LGTM!

Copy link
Member

Choose a reason for hiding this comment

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

Good catch! I'll put that back.

@phated phated merged commit d4eeaae into gulpjs:master May 10, 2020
@coreyfarrell coreyfarrell deleted the no-process-umask branch May 10, 2020 22:31
phated added a commit that referenced this pull request Jan 10, 2022
@github-actions github-actions bot mentioned this pull request Jan 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants