Date: | May 11, 2004 / year-entry #184 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20040511-00/?p=39403 |
Comments: | 19 |
Summary: | It's really not that complicated. If you permit, say, FILE_SHARE_READ, then you're saying, "I'm okay with other people reading this file while I have it open." And if you leave off the flag, then you're saying, "I do not want other people reading this file while I have it open." Now all that's left to... |
It's really not that complicated. If you permit, say, Now all that's left to do is work out what that means. So suppose you omit the flag, indicating that you don't want to let others read the file. Then when you attempt to open it yourself, the open will fail if anybody else has the file open for reading. And if the open succeeds, then the system will prevent anybody else from opening the file for reading until you close your handle. That's all. Of course, if the file is already open, then a corresponding check is made between your desired access and the file sharing mode of the people who already opened it. For example, if somebody already has the file open and denies read sharing, then if you try to open for read, you will get a sharing violation. These restrictions are cumulative, of course. If one person opens a file without Repeat the above logic for "delete" and "write" permission, and that's it in a nutshell. There is a big nasty table in MSDN that walks through all the combinations, but I personally think it confuses the matter rather than clarifying. Even more confusingly, the table uses "X" to mean that the combination is permitted. They should've used a bullet ("•") or a check mark ("ü"), since an "X" has the connotation of "not allowed". Let's look at one row of the table and see how the information in it is "obvious": Say, the row that reads "GENERIC_READ / GENERIC_WRITE / FILE_SHARE_READ". You are asking for read and write, and you permit read (and implicitly deny write). The requested access (read/write) requires that all previous openers have granted both read and write. There are three columns that correspond to this, namely the ones that say "FS_R FS_W". The requested sharing mode (read only) requires that all previous openers have requested read-only access. In other words, there can't be any G_W entries. That rules out two of the columns, leaving just "G_R FS_R FS_W", and indeed only one column is checked in the table. Notice that the file share bits you pass don't have to match up with your file access bits. The file share bits indicate what you want to allow other people to do. The access bits indicate what you want to do yourself. |
Comments (19)
Comments are closed. |
They really should have had borders on those table cells. Looks truly unusable in Opera.
Looks like pretty clear boolean logic, too. I like that share and access rights are separated.
Greetings,
It is this kind of thing that makes Win32 more powerful than UNIX, because traditional UNIX does not handle file sharing to well.
It is a cool trick to show students that you can open a file for reading two different ways, one in which you can delete the file from the file explorer while it is open and being read/written to in your program, and in another, you cannot delete the file in file explorer while it is open and being read/written to from your program. This all depends on which flags you choose when you open the file.
I would think that on UNIX, overwrite would be a problem (since the inode hasn’t changed); you need to do a delete/copy or a rename/copy.
SFP protects only OS files. Applications shouldn’t be messing with OS files anyway.
On UNIX you’d create the new version of the file under a temporary name and then rename() to the final name, which also has the benefit of being atomic. (although user-interface wise, I can see the logic of why Windows generally won’t let you delete a file that a program has openened)
Is there a way to atomically replace a file on Windows?
Rename the old version something else, then put the new one on the system.
There is an immense difference between the description in the base note and the description that programmers ordinarily find in MSDN such as http://msdn.microsoft.com/library/en-us/fileio/base/createfile.asp
There are also differences between the table which Mr. Chen provided a link to, and the MSDN page on CreateFile.
Furthermore, the MSDN page on CreateFile doesn’t even give a link to the MSDN page that Mr. Chen gave a link to. Furthermore, the MSDN page on CreateFile describes a flag FILE_SHARE_DELETE which the other page doesn’t describe. Could it possibly be that the table was outdated, wasn’t even correct at the time it was published, and has been forgotten about by MSDN maintainers?
Let’s see what CreateFile really says:
> FILE_SHARE_DELETE Enables subsequent open
> operations on the object to request delete
> access. Otherwise, other processes cannot
> open the object if they request delete
> access.
> Windows Me/98/95: This flag is not
> supported.
>
> FILE_SHARE_READ Enables subsequent open
> operations on the object to request read
> access. Otherwise, other processes cannot
> open the object if they request read
> access.
>
> FILE_SHARE_WRITE Enables subsequent open
> operations on the object to request write
> access. Otherwise, other processes cannot
> open the object if they request write
> access.
Notice the effect on SUBSEQUENT open operations. If someone else has a file open with FILE_SHARE_READ | FILE_SHARE_WRITE, and we want to open it for GENERIC_READ | GENERIC_WRITE but not share it with anyone else, we get the file and the previous opener still has the file. Only SUBSEQUENT attempts to open for reading or writing will fail. This is quite different from Mr. Chen’s statement:
FALSE> So suppose you omit the flag,
FALSE> indicating that you don’t want to let
FALSE> others read the file. Then when you
FALSE> attempt to open it yourself, the open
FALSE> will fail if anybody else has the file
FALSE> open for reading.
The wording of CreateFile also provides a number of other undesirable effects. For example, we want to open a file and allow FILE_SHARE_READ. Subsequently someone else want to open it with GENERIC_READ | GENERIC_WRITE. MSDN says they get it, because their request includes GENERIC_READ. The description of FILE_SHARE_DELETE is still so unclear that maybe developers of Windows file systems couldn’t even follow it properly, which might explain why sometimes you can’t delete a file because of a sharing violation even though no process has the file open.
This is the kind of thing that makes Windows less powerful than every other OS on the market. Neither Unix nor any other modern OS has fully copied the permissions structure that Honeywell got right 40 years ago, but at least each other modern OS is self-consistent. In Unix you have to do arcane things to get file operations performed reliably, but in Windows you have to do even worse arcane things.
(I’m again too busy to catch up with Mr. Chen’s recent writings, but this one was just too important to let pass.)
Your comment about checkmarks is interesting.
In Sweden a checkmark is the standard way of marking an error on a test or a paper. The first time my mother saw those old Borland messageboxes with a checkmark on the OK button and an X on the Cancel button she pressed Cancel because the checkmark to her denoted that there was something wrong and the X that she accepted (there’s a swedish expression "kryssa för" (cross for) which means to check something).
The FILE_SHARE_* flags prohibit subsequent opens. They also prevent the current CreateFile from succeeding if they conflict with prior opens. This is implied by the fact that the previous GENERIC_* open prevents subsequent opens from succeeding if they do not specify FILE_SHARE_*. The docs are correct but leave a lot of the logical deductions to you, the reader.
"Subsequently someone else want to open it with GENERIC_READ | GENERIC_WRITE. MSDN says they get it, because their request includes GENERIC_READ."
No, they don’t get it because their request includes GENERIC_WRITE and you didn’t allow write.
In order for the open to succeed, ALL requirements must be met. This is such a basic rule that I’m surprised it needed to be mentioned explicitly. Did you serious believe that "Enables subsequent open operations on the object to request read access." means that if you match the read sharing modes, then all other security/sharing flags become irrelevant? Did you really think that it would override the ACLs on the file?
Here’s another basic rule that is so obvious that it isn’t mentioned explicitly: The sharing modes applied to a handle are effective only as long as the handle remains open.
In fact the addition of "subsequent" was my attempt to state something that should have been blatantly obvious: CreateFile is not a time machine. Some people apparently thought that the absence of "subsequently" meant that you could use CreateFile to revoke previously-opened handles…
At some point you have to decide at what point you should stop saying obvious things in the documentation and leave the reader to use their own brainpower.
His question was how to atomically replace a file, a la Unix’s rename(), not how to replace a file on reboot.
A few metarules:
Specifically stated rules override basic rules.
Stated rules override intuitive rules.
The correctness of a basic rule or intuitive rule does not change an incorrect specific rule or incorrect stated rule into a correct one.
I am glad to see that these are bugs in the MSDN library rather than in the OS. Nonetheless, if the MSDN library is to be useful, its bugs must be fixed. "RTFM" has to mean that TFM is to be R’d rather than F’ed.
Okay then: You tell me how that section should be written, now that you understand what FILE_SHARE_READ means. Since apparently nothing I write meets your high standards.
What happens when you delete a file while it is open?
If there is a file already opened and you also want to open it, you need to know the share mode: FILE_SHARE_*