-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Added support for UF_HIDDEN flag on macOS and FreeBSD (#29323) #34560
Added support for UF_HIDDEN flag on macOS and FreeBSD (#29323) #34560
Conversation
| // approximation for the validation done by the Win32 function. | ||
| const FileAttributes allValidFlags = | ||
| FileAttributes.Archive | FileAttributes.Compressed | FileAttributes.Device | | ||
| FileAttributes.Directory | FileAttributes.Encrypted | FileAttributes.Hidden | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FileAttributes.Hidden attribute was twice in the list.
|
Builds failed because of SIGABRT, I got those during development when I didn't use private bits of CoreCLR (I don't know whether that is intended). So I guess the change in |
| int64_t BirthTimeNsec; // nanosecond part | ||
| int64_t Dev; // ID of the device containing the file | ||
| int64_t Ino; // inode number of the file | ||
| uint32_t UserFlags; // user defined flags |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is going to be problematic.
Because of how we've moved code around and shared it between the coreclr and corefx repos, some code in System.Private.CoreLib in the coreclr repo depends on System.Native.so/dylib and this type. When we add this field here, we'll end up corrupting data in System.Private.CoreLib until it can be updated accordingly, but that can't happen until these changes are merged. Catch-22.
I see two immediate ways around that:
- Temporarily introduce a new set of APIs/structs that this code uses, e.g. Xx2 instead of Xx. Then once everything has flown to coreclr and it's been updated accordingly and those bits consumed back into corefx, the old ones can be deleted. Then repeat the process to rename everything back to the "good" names.
- Try to work within the confines of the existing struct as it's defined, e.g. use the Flags field (which we introduced as part of the PAL and so can do with it what we want) to pass the relevant data rather than defining a new UserFlags field.
How would things look if we did (2)? Is it possible? Could it be done in a relatively clean way, or would it be messy? My preference right now would be (2) if it can be done in at all a reasonable way.
cc: @jkotas, @danmosemsft (longer term we may want to rethink where the shared shims live)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
x2 instead of Xx.
We do have x2 APIs for this today because of similar type of change some time ago. We can use this to go back to APIs without the 2 suffix.
I see two immediate ways around that:
3rd option would be to first add the field and let the change propagate everywhere. Once the field is everywhere start actually using it.
This is not that different from how any changes on the CoreCLR/CoreFX boundaries need to be staged, e.g. changes of public APIs that are in use by CoreFX have to be often staged in similar way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3rd option
Ah, right. I like that option, too.
We do have x2 APIs for this today because of similar type of change some time ago. We can use this to go back to APIs without the 2 suffix.
You're right. Let's do that, then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understood it correctly I should extract this change to separate PR and this PR will be merged once it is propagated everywhere, correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes @davidkaya I believe htat's right. (We didn't notice you were waiting on this q)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, we first add the field, and then add the rest subsequently.
| #endif | ||
|
|
||
| #if HAVE_STAT_FLAGS | ||
| dst->UserFlags = src->st_flags; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if it applies to UF_HIDDEN, but in the past we've seen a lot of these defines have different values across platforms, which is one of the reasons we have these shims. As such, if we stick with the UserFlags (see my other previous comment about that), we should define a PAL_ version in pal_io.h that has values which match the managed side, regardless of how the native platform defines it, and then here do the translation, e.g.
dst->UserFlags = (src->st_flags & UF_HIDDEN == UF_HIDDEN) ? PAL_UF_HIDDEN : 0;There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to limit the flags only for the UF_HIDDEN one? As I went through the possible flags, I've noticed that you can also set SF_ARCHIVED (0x00010000) in OSX which can be mapped to FileAttributes.Archived and currently it is not. Is it ok to limit it currently only to UF_HIDDEN for now and if e.g SF_ARCHIVED was to be implemented in the future, then the one implementing it should change the implementation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it ok to limit it currently only to UF_HIDDEN for now and if e.g SF_ARCHIVED was to be implemented in the future, then the one implementing it should change the implementation?
Yup. Let's stick with just hidden for now.
| if ((attributes & FileAttributes.Hidden) != 0 && (_fileStatus.UserFlags & (uint)Interop.Sys.UserFlags.UF_HIDDEN) != (uint)Interop.Sys.UserFlags.UF_HIDDEN) | ||
| { | ||
| // If Hidden flag is set and cached file status does not have the flag set then set it | ||
| Interop.Sys.LChflags(path, (uint)Interop.Sys.UserFlags.UF_HIDDEN); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it ok that we're ignoring errors? Assuming yes, a comment to that effect (saying why) would be good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked the Windows implementation of SetAttributes and it throws when the operation returned != 0 code
corefx/src/System.IO.FileSystem/src/System/IO/FileSystem.Windows.cs
Lines 569 to 575 in 7709675
| if (!Interop.Kernel32.SetFileAttributes(fullPath, (int)attributes)) | |
| { | |
| int errorCode = Marshal.GetLastWin32Error(); | |
| if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) | |
| throw new ArgumentException(SR.Arg_InvalidFileAttrs, nameof(attributes)); | |
| throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); | |
| } |
also Unix one throws
| Interop.CheckIo(Interop.Sys.ChMod(path, newMode), path, InitiallyDirectory); |
I will wrap it in the Interop.CheckIo to match the existing unix implementation which uses Interop.ChMod.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good.
| // If Hidden flag is set and cached file status does not have the flag set then set it | ||
| Interop.Sys.LChflags(path, (uint)Interop.Sys.UserFlags.UF_HIDDEN); | ||
| } | ||
| else if ((attributes & FileAttributes.Hidden) == 0 && (_fileStatus.UserFlags & (uint)Interop.Sys.UserFlags.UF_HIDDEN) == (uint)Interop.Sys.UserFlags.UF_HIDDEN) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like this would be better structured as:
if ((attributes & FileAttributes.Hidden) != 0)
{
if (_fileStatus.UserFlags & (uint)Interop.Sys.UserFlags.UF_HIDDEN) != (uint)Interop.Sys.UserFlags.UF_HIDDEN)
{
...
}
}
else
{
if (_fileStatus.UserFlags & (uint)Interop.Sys.UserFlags.UF_HIDDEN) == (uint)Interop.Sys.UserFlags.UF_HIDDEN)
{
}
}otherwise we're unnecessarily doing the attributes & Hidden test again for the else block if the if condition is true in the first clause but false in the second. Plus it avoids needing to write the same condition check (one inverted) twice.
|
Thanks for working on this. |
| internal long BirthTimeNsec; | ||
| internal long Dev; | ||
| internal long Ino; | ||
| internal uint UserFlags; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stephentoub Should I use the new UserFlags enum here or leave it as uint?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's stick with uint.
|
@stephentoub made changes based on review, I don't know which changes I should extract to separate PR, could you help me out with that? Only |
|
@stephentoub there are some questions for you above it looks like. |
|
@stephentoub ping? |
I will incorporate changes as soon as I get some feedback :) |
|
/azp run |
|
Azure Pipelines successfully started running 4 pipeline(s). |
|
It seems like the change to pal_io.h should come out first @davidkaya. Since it is ABI breaking change it should come out first before we start writing to that field. If I'm not mistaken, rest can come all together e.g. start using new field when every part of the code base sees updated size. |
|
Sorry, somehow I managed to accidentally mute this thread and wasn't getting any notifications from it. |
| <Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.UTimensat.cs"> | ||
| <Link>Common\Interop\Unix\Interop.UTimensat.cs</Link> | ||
| </Compile> | ||
| <Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.LChflags.cs"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit...sorted list?
| { | ||
| if ((attributes & FileAttributes.Hidden) != 0) | ||
| { | ||
| if ((_fileStatus.UserFlags & (uint)Interop.Sys.UserFlags.UF_HIDDEN) != (uint)Interop.Sys.UserFlags.UF_HIDDEN) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this style terser
if ((_fileStatus.UserFlags &(uint)Interop.Sys.UserFlags.UF_HIDDEN) == 0)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, it might. Will change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case you might also change if ((_fileStatus.UserFlags & (uint)Interop.Sys.UserFlags.UF_HIDDEN) == (uint)Interop.Sys.UserFlags.UF_HIDDEN) to if ((_fileStatus.UserFlags & (uint)Interop.Sys.UserFlags.UF_HIDDEN) != 0)
|
Incorporated all comments, extracted Also removed UserFlags fields from the cs file. Will merge master into the branch once the second PR gets merged. |
|
/azp run |
|
Azure Pipelines successfully started running 4 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 4 pipeline(s). |
|
Tests are clean save unrelated http test |
|
Any more review feedback? |
|
@JeremyKuhne @stephentoub @jkotas any other feedback |
|
Sorry for making the changes based on review this late. Unexpected business trip. |
stephentoub
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for working on this.
Backport of dotnet/corefx#34560 Added HAVE_LCHFLAGS & HAVE_STAT_FLAGS to configure.
Backport of dotnet/corefx#34560 Added HAVE_LCHFLAGS & HAVE_STAT_FLAGS to configure. Commit migrated from mono/mono@0269f7b
) * Added UserFlags field * Added UserFlags Commit migrated from dotnet/corefx@8c66b0f
…29323) (dotnet/corefx#34560) * Added support for UF_HIDDEN flag on macOS and FreeBSD (dotnet/corefx#29323) * Changes based on review * Reverted UserFlags field * Sorted include in csproj * Changed comparison style * Reverted UserFlags field * Fixed parentheses * Changes based on PR review * Add missing newline * Add missing newline Commit migrated from dotnet/corefx@b37f018
Closes #29323
I am not sure about the change in
Interop.Stat.cswhether it should be merged to CoreFX or whether I should revert it here and make a PR with that change to CoreCLR.cc: @JeremyKuhne @danmosemsft