From: SMTP%"RELAY-INFO-VAX@CRVAX.SRI.COM" 25-AUG-1993 08:59:49.87 To: EVERHART CC: Subj: Re: VAXC RTL and RMS Message-Id: <9308241532.AA24904@uu3.psi.com> Date: Tue, 24 Aug 93 10:26:37 EDT From: Jerry Leichter To: INFO-VAX@sri.com Subject: Re: VAXC RTL and RMS X-Vms-Mail-To: UUCP%"DAHMS@ifk20.mach.uni-karlsruhe.de" [A C program creating a file ignores the RMS default protection; a FORTRAN program does not.] Some interesting anwers even to previously discussed other VAXCRTL/ RMS topics: - Fortran does what I expect 8-) - C *can* read RMS default protection! - C creates with RMS default protection regardless of protection on ;-1 - C ignores default protection ACL! - C program exit code is 0, not remapped to 1! and I proofed (but don't include), also using another directory without ACLs: - Fortran uses RMS default protection for creating first version of a file - Fortran creates ;0 with same protection as ;-1 regardless of RMS def. prot. further tests with ACLs granting/prohibiting file/directory access for some UICs or rights identifiers left to the reader (or I'll do upon request)... I think that will suffice for the moment, Sigh. We've been around this bend before. It's another illustration of why all the "obvious" solutions that people on this list seem to believe the developers are ignoring out of spite or stupidity or something are not as obvious as they think - *if* they'd think about the bigger picture. The problem here is that C, unlike FORTRAN, comes with its own file protection semantics. It appears in the form of the umask() function, and its documented interaction with all subsequent file creations. umask() is the Unix/C equivalent of SET PROTECTION/DEFAULT or, more precisely, SYS$SETDFPROT. It applies to the current process/image (the two are more or less equivalent in Unix, since a new process is spawned by the Shell for each command) and to any processes it subsequently creates with fork(). Consider the implementation alternatives for umask(): - umask() can call SYS$SETDFPROT. Unfortunately, the default pro- tection is not a user mode setting that is automatically reset on exit, like a default directory established by SYS$SETDDIR. (Why? This is definitely one of the less well thought through areas of VMS.) There's no way for a user-mode library - and as one of the developers noted here recently, it's always been a requirement of the VAX C RTL project that it stick to user mode code - can guarantee to put the default protection back. Not putting it back would violate Unix semantics and would be undesireable, since there are probably many Unix programs that rely on this property and don't clean up after themselves. BTW, just to be accurate, umask() isn't really like SYS$SETDFPROT in one important way: It can only restrict access further, beyond what the restrictions the default mask for the process allow. It cannot be used to grant access by default. I'll ignore that distinction below, as it's not really significant for this discussion. - So the VAX C RTL has to keep its own record of what the current umask() value is. That value defines the protection to be handed to RMS when files are created. Clearly, its initial value should be taken from the RMS default protection - except when an image is run through vfork/exec, in which case it must be inherited from the creating process. This is, in fact, exactly what the VAX C RTL currently does. - Unfortunately, RMS protection semantics is very different from Unix file protection semantics. On Unix, you can't specify the protection to be given to a file directly as part of creating it; you either set the umask() value, or change it after the fact. That's one reason Unix programs call umask() so often. Using umask() to set a mask that happens to be equal to the current mask is a no-op. On VMS, on the other hand, the file creation call allows you to specify a protection - and there's a BIG difference between specifying no protection at all, and specifying the current process default protection. That's because the "current pro- cess default protection" is only one small piece of the de- fault protection environment used by RMS, which includes such things as the protections on previous versions of the file and the DEFAULT_PROTECTION ACE of the enclosing directory. - The current implementation provides an excellent match of Unix semantics; Unix programmers porting code to VMS will find it quite natural and comfortable. Unfortunately, it's a rather poor match to VMS semantics. - I long ago proposed a mechanism that would work somewhat better. Suppose the RTL added one more bit of state: Whether the umask() function had ever been called at all. If so, the current umask value would be used to define a protection when files were created. If not, no protection would be specified and the RMS defaults would be taken. Code written for VMS would never call umask() and would get pure VMS semantics. Code written for Unix which DID call umask() would get what it expected. This is not a panacea: o A Unix program that DOESN'T call umask() may not get quite the default it expects. Since this will only happen when a Unix program is run in a non-Unix kind of environment, where there are previous versions of files or ACE's around, one can live with this. o There's no way to return to the "umask() never called" state. Setting the mask back to the original default value (given the actual semantics of umask(), this would be a umask(0) call) shouldn't turn the bit off for proper Unix emulation, since a Unix program may make specific assumptions about what umask(0) will produce. o The biggest problem is hidden in the innocent words "[Whether umask] had ever been called at all." What should we do if a program that HAS called umask() does a vfork/exec? If the program it is exec'ing was written to expect Unix-style semantics, we'd better pass the bit on, along with the umask setting. But then if the program is expecting VMS semantics, it won't get them, even if the current umask value is 0. No matter what we do, some programs will end up being executed in the "wrong" environment. Fortunately, the situation of a Unix import program uses vfork/exec to run a native VMS C program is likely to be relatively rare; so passing the value of the bit is probably not a bad solution. (Of course, if someone decides to import a Unix Shell, all bets are off.) All thinks considered, I think my proposal is about as good a solution as one is likely to find. - For all the incompatibility with VMS semantics in favor of Unix semantics in umask/creat. it's curious that the implementation of system() does NOT follow Unix semantics. On Unix, system() is really just a fork() followed by exec of a Shell, so inher- its the umask value. For VMS, the closest approximation would be for the created process to get an RMS default protection based on the current umask(). But that isn't done. The intermingling of the C library with the Unix environment causes many headaches. This has, in effect, been recognized by the partitioning of stan- dardization efforts into ANSI C, which defined a minimal library that is not closely tied to Unix, and POSIX, which defines a much fuller C library but makes no bones about the fact that it's a full operating system environment specification, NOT a language specification. Unfortunately, the ingrained habits of all the Unix C programmers out there, and all the millions of lines of Unix-derived "portable" C code, are hard (impossible) to ignore. Oh, well: The one thing we can be thankful for in the PC world is that there are so many different C development environments that PC programmers haven't developed the attitude that whatever particular library routines they are used to HAVE to be part of any "legitimate" C implementation. Imagine if the VAX C RTL were called upon to somehow emulate the MS/DOS system calls, as it now does many of the Unix system calls. (umask() is a system call on Unix.) So far, about the only routine that all the PC dweebs ask for is kbhit(), so that they can write yet another silly game that responds as soon as someone types a key. -- Jerry PS: If you are still tempted to believe that there is a simple solution out there, take a look at the definition of access() in VMS. Then look long and hard at the Unix definition - and look at some Unix books that explain what access() is intended to be used for. Then see if you can come up with ANY definition of access() for VMS that would allow imported Unix programs that use access() to do something rational in all cases.