otp icon indicating copy to clipboard operation
otp copied to clipboard

file:read_file_info fails on Windows with {error,badarg} for files on SMB share if last access time is not set

Open chi-lambda opened this issue 2 years ago • 1 comments

Describe the bug file:read_file_info fails on Windows (native) when called with a file on an SMB share, no matter if UNC path or mapped drive, if no last access time is set.

To Reproduce Create a file from the client on an SMB share. Confirm in explorer that last access time is not set (displays as 00:00:00). This may require a Linux samba server instead of an actual Windows server. Then call file:read_file_info on that file on the client. It should fail with {error,badarg}.

Expected behavior file:read_file_info returns the file information, similar to this (WSL1 output):

{ok,{file_info,583,regular,read_write,
               {{30828,9,14},{4,48,5}},
               {{2022,10,10},{8,27,53}},
               {{2022,10,10},{8,27,53}},
               33279,1,21,0,6831742,0,0}}

Affected versions Found in OTP 25.1.

Additional context Erlang in WSL1 shows garbage for last access time (with a year in the 30000s), but doesn't fail.

Calling touch on the file on the server "fixes" the issue, as it sets the last access time.

chi-lambda avatar Oct 10 '22 20:10 chi-lambda

I think I have figured out what's happening:

  • If last access time is not set, its value is 2^63-1.
  • That, divided by TICKS_PER_SECOND==10000000, is 922337203685, subtract EPOCH_DIFFERENCE==11644473600 and you get 910692730085.
  • In prim_file.erl:634, we call adjust_time with TimeType==local, which calls erlang:universaltime_to_localtime(erlang:posixtime_to_universaltime(910692730085))
  • erlang:posixtime_to_universaltime(910692730085)=={{30828,9,14},{2,48,5}}, but on native Windows, erlang:universaltime_to_localtime({{30828,9,14},{2,48,5}}) fails with
** exception error: bad argument
     in function  erlang:posixtime_to_universaltime/1
        called as erlang:posixtime_to_universaltime({{30828,9,14},{2,48,5}})
        *** argument 1: not an integer
  • The cause (I think) for that is that {{30828,9,14},{2,48,5}} is not a valid SYSTEMTIME, which ends in 30827, so sys_localtime_r fails. (On Linux and other Unix-like OSs, we just call localtime_r, which doesn't have that problem.)

I haven't localized the exact place where it fails, because in the end, this is just a case of "garbage in, garbage out". If there is no valid data in the last-access-time field, we should return nothing there or any arbitrary, unproblematic value (say {{9999,12,31},{23,59,59}}).

Side note: Who reads the last access time anyway – especially on Windows, where it's usually disabled?

chi-lambda avatar Oct 12 '22 12:10 chi-lambda

Thanks for your report, and sorry for the late reply. I got back from long-term leave late last week and didn't manage to fix this in time for the upcoming OTP 25.2 patch. I'll make a PR with the fix as soon as that is released.

jhogberg avatar Dec 05 '22 09:12 jhogberg

I've merged a fix into maint now, which will be released as 25.3 early next year. Thanks again for your report!

jhogberg avatar Dec 22 '22 08:12 jhogberg