fopen and open produce files with different file permissions - cocoa

These two code snippets produce files with different file-permissions. Example 1 creates the expected default file-permissions but Example 2 does not. What's the explanation for this?
OS: Mac OS X version: 10.6.4
Xcode version: 3.2.2, 64 bit
// Example 1
FILE *fh1 = fopen("Test1.txt", "w+x");
if (fh1) {
fwrite("TEST1", 1, 5, fh1);
fclose(fh1);
}
Creates:
-rw-r--r-- 1 me staff 5 29 Jul 00:41 Test1.txt
// Example 2
int fh2 = open("Test2.txt", O_EXCL | O_CREAT | O_WRONLY);
if (fh2 >= 0) {
write(fh2, "TEST2", 5);
close(fh2);
}
Creates:
---------- 1 me staff 5 29 Jul 00:41 Test2.txt

When you use O_CREAT you need to add a third argument to open, the mode. For instance:
int fh2 = open("Test2.txt",
O_EXCL | O_CREAT | O_WRONLY,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
This would be equivalent to 0666. Be aware that this mode is then masked by the process's umask, meaning the permissions you specify will usually be reduced a bit. A typical umask is 0022, which would result in a mode of 0666 & ~0222 = 0644, i.e. -rw-r--r--.
From man open:
The oflag argument may indicate that the file is to be created if it does not exist (by specifying the O_CREAT flag). In this case, open requires a third argument mode_t mode; the file is created with mode mode as described in chmod(2) and modified by the process' umask value (see umask(2)).

int open(const char *pathname, int flags, mode_t mode);
The argument mode specifies the permissions to use in case a new file is created. See http://linux.about.com/od/commands/l/blcmdl2_open.htm
In your case, you'll want to set mode with value 0644

Related

How to get Monitor Handle from Index or Name?

A C++ version (How can I get an HMONITOR handle from a display device name?) has no solution provided (at least in my circumstances that require non-OOP code such as in AutoIt).
I'm adapting an AutoIt script that uses WinAPI functions to support multi-monitor Windows 7+ systems. I can provide either monitor/device Name or it's Index, but some functions require an HMONITOR handle instead.
I cannot get the HMONITOR by Window or by pixel or point, which would be quite easy. No, I need to get the handle from name or index only, and I need a non-OOP solution (ideally AutoIt & WinAPI calls but non-OOP pseudo-code would be fine).
The function below returns an array of the following structure:
| hMonitor | xPosMonitor | yPosMonitor | widthMonitor | heightMonitor |
| 0x00010001 | 0 | 0 | 1366 | 768 |
| 0x0001024 | 1366 | -236 | 1920 | 1080 |
Code:
#include-once
#include <Array.au3>
#include <WinAPIGdi.au3>
Func _getMonitorInfos()
Local $aPosition, $aMonitorData = _WinAPI_EnumDisplayMonitors()
If IsArray($aMonitorData) Then
ReDim $aMonitorData[$aMonitorData[0][0] + 1][5]
For $i = 1 To $aMonitorData[0][0] Step 1
$aPosition = _WinAPI_GetPosFromRect($aMonitorData[$i][1])
For $j = 0 To 3 Step 1
$aMonitorData[$i][$j + 1] = $aPosition[$j]
Next
Next
Return $aMonitorData
EndIf
EndFunc
Global $aMonitorData = _getMonitorInfos()
_ArrayDisplay($aMonitorData)
hMonitor value is contained by the array $aMonitorData[1][1].

Can't call `ioutil.ReadDir` on `/dev/fd` on MacOS

I tried to run the following Go code:
package main
import (
"fmt"
"io/ioutil"
)
func main() {
items, err := ioutil.ReadDir("/dev/fd")
if err != nil {
panic(err)
}
fmt.Println(items)
}
I just get this error:
panic: lstat /dev/fd/4: bad file descriptor
goroutine 1 [running]:
main.main()
/Users/andy/Desktop/demo.go:11 +0xe8
exit status 2
The /dev/fd folder definitely exists, and there is a /dev/fd/4 inside there when I ls it.
$ ls -Al /dev/fd
total 9
crw--w---- 1 andy tty 16, 4 Jan 25 00:16 0
crw--w---- 1 andy tty 16, 4 Jan 25 00:16 1
crw--w---- 1 andy tty 16, 4 Jan 25 00:16 2
dr--r--r-- 3 root wheel 4419 Jan 23 20:42 3/
dr--r--r-- 1 root wheel 0 Jan 23 20:42 4/
What's going on? Why can't I read this directory? I'm trying to port the ls command to Go here, so I would like to be able to read this directory in order to produce similar output to ls.
EDIT: I am running everything as non-root user. The executable bit on /dev/fd is set.
$ ls -al /dev | grep fd
dr-xr-xr-x 1 root wheel 0 Jan 23 20:42 fd/
$ stat /dev/fd/4 # same result with -L flag
stat: /dev/fd/4: stat: Bad file descriptor
First, let's remember that /dev/fd is special. Its contents are different for every process (since they reflect the file descriptors of that process), so it doesn't really mean anything that ls can list it because its contents will be different for ls and your program.
Anyway, here's a slightly updated version of your program where instead of letting ioutil do things behind our back, we do it ourselves to see what's going on:
package main
import (
"fmt"
"time"
"os"
"log"
)
func main() {
d, err := os.Open("/dev/fd")
if err != nil {
log.Fatal(err)
}
names, err := d.Readdirnames(0)
if err != nil {
log.Fatal(err)
}
for _, n := range names {
n = "/dev/fd/" + n
fmt.Printf("file: %s\n", n)
_, err := os.Lstat(n)
if err != nil {
fmt.Printf("lstat error: %s - %v\n", n, err)
}
}
time.Sleep(time.Second * 200)
}
And then when run it gives me:
file: /dev/fd/0
file: /dev/fd/1
file: /dev/fd/2
file: /dev/fd/3
file: /dev/fd/4
lstat error: /dev/fd/4 - lstat /dev/fd/4: bad file descriptor
So this is indeed the same problem. I added the sleep at the end so that the process doesn't die so that we can debug which file descriptors it has. In another terminal:
$ lsof -p 7861
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
foo 7861 art cwd DIR 1,4 2272 731702 /Users/art/src/go/src
foo 7861 art txt REG 1,4 1450576 8591078117 /private/var/folders/m7/d614cd9x61s0l3thb7cf3rkh0000gn/T/go-build268777304/command-line-arguments/_obj/exe/foo
foo 7861 art txt REG 1,4 837248 8590944844 /usr/lib/dyld
foo 7861 art 0u CHR 16,4 0t8129 645 /dev/ttys004
foo 7861 art 1u CHR 16,4 0t8129 645 /dev/ttys004
foo 7861 art 2u CHR 16,4 0t8129 645 /dev/ttys004
foo 7861 art 3r DIR 37,7153808 0 316 /dev/fd
foo 7861 art 4u KQUEUE count=0, state=0x8
We can see that fd 4 is a KQUEUE. Kqueue file descriptors are used for managing events on OSX, similar to epoll on Linux if you know that mechanism.
It appears that OSX does not allow stat on kqueue file descriptors in /dev/fd which we can verify with:
$ cat > foo.c
#include <sys/types.h>
#include <sys/event.h>
#include <sys/stat.h>
#include <stdio.h>
#include <err.h>
int
main(int argc, char **argv)
{
int fd = kqueue();
char path[16];
struct stat st;
snprintf(path, sizeof(path), "/dev/fd/%d", fd);
if (lstat(path, &st) == -1)
err(1, "lstat");
return 0;
}
$ cc -o foo foo.c && ./foo
foo: lstat: Bad file descriptor
$
Go programs on OSX need a kqueue to handle various events (not exactly sure which, but probably signals, timers and various file events).
You have four options here: don't stat, ignore the error, don't touch /dev/fd because it's weird, convince Apple that this is a bug.
Assuming that you're running your code as a non-root user, I'd guess that the problem is that the directory doesn't have the execute bit set, which prevents chdir-ing to that directory (which ReadDir might do before attempting to read the contents).

Windows / NTFS: Two files with identical long-names in the same directory?

I have been a lurker at stackoverflow.com for many years (great site and users here), but never had the need to ask a question. Now the time has come :-) Let me begin:
OS: x64 Windows 8.0 to Windows 10 (15063.14) (the issue exists since years, but I have never pursued it fully yet, so we can exclude that it is specific to a specific Windows version)
FS: NTFS
Issue: 2 files with the same (long) name in the same directory and I cannot figure out how this is even possible. This happens to me since years whenever I manually upgrade my Email client. The main .EXE file of it (MailClient.exe) is never asking for replacement if copying the new one over to the same directory. Instead they are both placed there, with the exact same long name.
The issue has nothing to do with a specific directory, I can copy around both .EXE files to freshly created directories on the NTFS drive without issues (also getting no "overwrite" question there).
Let me show you:
C:\temp\2>dir
Volume in drive C is SSD 840 Pro
Volume Serial Number is 0C6D-D489
Directory of C:\temp\2
13.04.2017 02:29 <DIR> .
13.04.2017 02:29 <DIR> ..
21.10.2016 17:10 24.742.760 MailClient.exe
27.12.2016 03:26 24.911.872 MailCliеnt.exe
2 File(s) 49.654.632 bytes
2 Dir(s) 78.503.038.976 bytes free
However, if doing a dir /x, this comes up:
C:\temp\2>dir /x
Volume in drive C is SSD 840 Pro
Volume Serial Number is 0C6D-D489
Directory of C:\temp\2
13.04.2017 02:29 <DIR> .
13.04.2017 02:29 <DIR> ..
21.10.2016 17:10 24.742.760 MAILCL~2.EXE MailClient.exe
27.12.2016 03:26 24.911.872 MAILCL~1.EXE MailCliеnt.exe
2 File(s) 49.654.632 bytes
2 Dir(s) 78.503.038.976 bytes free
So they obviously have a different 8.3 name, OK, but the exact same long name. Here is another screenshot of the situation. Both files show the same location within the Windows "properties" dialog (right click) too. Unfortunately I am not allowed to post images just yet (it seems) - just tried. So you will have to take my word.
I cannot figure out how this is possible and this is bugging me ;) As soon as I rename both files for example to 1.exe, Windows starts telling me that there is already a file with that name in the same directory. So it obviously has something to do with the filename, but they are both exactly identical, no extra spaces, nothing, as you can see from the DIR command.
I´ve also tried to rename them and re-wrote the exact wording "MailCient.exe" manually for both, to make sure the characters are EXCACTLY the same, Windows still won´t complain, they both go there once again under the same name. However, renaming them to "Mail.exe" and "Mail.exe" will NOT work, then Windows is saying that another file with that name already exists. However, naming them both back to "MailClient.exe" is just absolutely fine, no complains by Windows with that.
Another fun fact about this, if I dir for mailclient.exe directly, this happens:
C:\temp\2>dir mailclient.exe
Volume in drive C is SSD 840 Pro
Volume Serial Number is 0C6D-D489
Directory of C:\temp\2
21.10.2016 17:10 24.742.760 MailClient.exe
1 File(s) 24.742.760 bytes
0 Dir(s) 78.501.998.592 bytes free
However, if looking for *.exe, this happens:
C:\temp\2>dir *.exe
Volume in drive C is SSD 840 Pro
Volume Serial Number is 0C6D-D489
Directory of C:\temp\2
21.10.2016 17:10 24.742.760 MailClient.exe
27.12.2016 03:26 24.911.872 MailCliеnt.exe
2 File(s) 49.654.632 bytes
0 Dir(s) 78.501.990.400 bytes free
This yields also interesting results:
C:\temp\2>ren mailclient.exe *.bak
C:\temp\2>dir
Volume in drive C is SSD 840 Pro
Volume Serial Number is 0C6D-D489
Directory of C:\temp\2
13.04.2017 02:50 <DIR> .
13.04.2017 02:50 <DIR> ..
21.10.2016 17:10 24.742.760 MailClient.bak
27.12.2016 03:26 24.911.872 MailCliеnt.exe
2 File(s) 49.654.632 bytes
2 Dir(s) 78.501.990.400 bytes free
And back:
C:\temp\2>ren mailclient.bak MailClient.exe
C:\temp\2>dir
Volume in drive C is SSD 840 Pro
Volume Serial Number is 0C6D-D489
Directory of C:\temp\2
13.04.2017 02:51 <DIR> .
13.04.2017 02:51 <DIR> ..
21.10.2016 17:10 24.742.760 MailClient.exe
27.12.2016 03:26 24.911.872 MailCliеnt.exe
2 File(s) 49.654.632 bytes
2 Dir(s) 78.501.982.208 bytes free
I´ve also checked permissions on the files and took ownership, it changes nothing. Additionally I´ve cleared the NTFS Journal and even the transaction log + run chkdsk, which reveals no errors either.
Any ideas on this mysterious situation? What am I missing?
Thanks so much:)
UPDATE #1:
I´ve just tried this: going to Windows explorer and renaming both files after each other by truncating their names. So I first renamed the first "MailClient.exe" to "MailClien.exe", then the seconds "MailClient.exe" to "MailClien.exe". Again, no message by Windows that they have the same name, it just renamed both fine. I then continued to "MailClie.exe". Worked.
However, as soon as I tried to renamed both to "MailCli.exe", Windows complained and told me that there is already another file with that name. Trying to rename both back from there to "MailClient.exe" also does not work, just for one of them, because then Windows says (and right so too) that a file with that name already exists. So it seems to come down to the "e" possibly having another ANSI-character in both filenames? I, however, wouldn´t know of another one for "e", or am I missing something?
Harry Johnston is right: one of the filenames contains a Unicode character that just looks the same as an ANSI character.
Read Naming Files, Paths, and Namespaces:
On newer file systems, such as NTFS, exFAT, UDFS, and FAT32, Windows
stores the long file names on disk in Unicode, which means that the
original long file name is always preserved. This is true even if a
long file name contains extended characters, regardless of the code
page that is active during a disk read or write operation.
Use the following PowerShell script 43381802b.ps1 to detect and show non-ANSI file names (see different calls below):
param( [string[]]$Path = '.',
[switch]$Cpp, ### list any non-ANSI character in file names like a C++ literal
### i.e. a prefix \u followed by a four digit Unicode code point
[switch]$All ### list all files including pure ANSI-encoded file names
)
Set-StrictMode -Version latest
$strArr = Get-ChildItem -path $Path
$arrDiff = #()
for ($i=0; $i -lt $strArr.Count; $i++) {
$strDiff = 'ANSI'
$strName = ''
$auxName = $strArr[$i].Name
for ( $k=0; $k -lt $auxName.Length; $k++ ) {
if ( [int][char]$auxName[$k] -gt 255 ) {
$strDiff = 'UCS2'
$strName += '\u{0:X4}' -f [int][char]$auxName[$k]
} else {
$strName += $auxName[$k]
}
}
if ( $All.IsPresent -or $strDiff -eq 'UCS2' ) {
$strArr[$i] | Add-Member NoteProperty Code $strDiff
$strArr[$i] | Add-Member NoteProperty CppName $strName
$arrDiff += $strArr[$i]
}
}
if ( $Cpp.IsPresent ) {
$arrDiff | Select-Object -Property Code, Mode, LastWriteTime, Length, CppName | ft
} else {
$arrDiff | Select-Object -Property Code, Mode, LastWriteTime, Length, Name | ft
}
Output:
PS D:\PShell> .\SO\43381802b.ps1 'C:\testC\43381802'
Code Mode LastWriteTime Length Name
---- ---- ------------- ------ ----
UCS2 -a---- 02/05/2017 11:47:53 317 MailCliеnt.txt
UCS2 -a---- 02/05/2017 11:49:04 317 МailClient.txt
UCS2 -a---- 02/05/2017 11:50:16 399 МailCliеnt.txt
PS D:\PShell> .\SO\43381802b.ps1 'C:\testC\43381802' -Cpp
Code Mode LastWriteTime Length CppName
---- ---- ------------- ------ -------
UCS2 -a---- 02/05/2017 11:47:53 317 MailCli\u0435nt.txt
UCS2 -a---- 02/05/2017 11:49:04 317 \u041CailClient.txt
UCS2 -a---- 02/05/2017 11:50:16 399 \u041CailCli\u0435nt.txt
PS D:\PShell> .\SO\43381802b.ps1 'C:\testC\43381802' -Cpp -All
Code Mode LastWriteTime Length CppName
---- ---- ------------- ------ -------
ANSI -a---- 02/05/2017 11:44:05 235 MailClient.txt
UCS2 -a---- 02/05/2017 11:47:53 317 MailCli\u0435nt.txt
UCS2 -a---- 02/05/2017 11:49:04 317 \u041CailClient.txt
UCS2 -a---- 02/05/2017 11:50:16 399 \u041CailCli\u0435nt.txt
Use the following 43381802a.ps1 script to get more info about non-ANSI characters (see the first call bellow) and their position in file names (see the latter call bellow with -Detail switch):
param( [string[]] $strArr = #('ΗGreek', 'НCyril', 'HLatin'),
[switch]$Detail )
Set-StrictMode -Version latest
$auxArr = #()
if ( ( Get-Command -Name Get-CharInfo -ErrorAction SilentlyContinue ) -and
( -not $Detail.IsPresent ) ) {
$auxArr = $strArr | Get-CharInfo |
Where-Object { [int]$_.Codepoint.Replace('U+', '0x') -ge 128 }
} else {
foreach ($strStr in $strArr) {
for ($i = 0; $i -lt $strStr.Length; $i++ ) {
if ( [int][char]$strStr[$i] -ge 128 ) {
$auxArr += [PSCustomObject] #{
Char = $strStr[$i]
CodePoint = 'U+{0:x4}' -f [int][char]$strStr[$i]
Category = $i + 1 ### 1-based index
Description = $strStr ### string itself
}
}
}
}
}
$auxArr
Output:
PS D:\PShell> .\SO\43381802a.ps1 ( Get-childitem -path 'C:\testC\43381802' ).Name
Char CodePoint Category Description
---- --------- -------- -----------
е U+0435 LowercaseLetter Cyrillic Small Letter Ie
М U+041C UppercaseLetter Cyrillic Capital Letter Em
М U+041C UppercaseLetter Cyrillic Capital Letter Em
е U+0435 LowercaseLetter Cyrillic Small Letter Ie
PS D:\PShell> .\SO\43381802a.ps1 ( Get-childitem -path 'C:\testC\43381802' ).Name -detail
Char CodePoint Category Description
---- --------- -------- -----------
е U+0435 8 MailCliеnt.txt
М U+041c 1 МailClient.txt
М U+041c 1 МailCliеnt.txt
е U+0435 8 МailCliеnt.txt
Tested on files:
==> dir /-C /X /A-D C:\testC\43381802\
Volume in drive C has no label.
Volume Serial Number is …
Directory of C:\testC\43381802
02/05/2017 11:44 235 MAILCL~1.TXT MailClient.txt
02/05/2017 11:47 317 MAILCL~2.TXT MailCliеnt.txt
02/05/2017 11:49 317 AILCLI~1.TXT МailClient.txt
02/05/2017 11:50 399 AILCLI~2.TXT МailCliеnt.txt
4 File(s) 1268 bytes
0 Dir(s) 69914857472 bytes free
==>

shell closes file descriptor num 19

Debugging my application I have found strange behaviour of shell interpreter(/bin/sh on Solaris, /bin/dash in Debian). While fork()ing in shell file descriptor by number 19(dec) is closed by the shell. In my case it leads to closing of communication socket pair between processes.
Looking at shell sources I have found this one and this:
for brevity:
/* used for input and output of shell */
#define INIO 19
and
if (input > 0) {
Ldup(input, INIO);
input = INIO;
}
...
static void
Ldup(int fa, int fb)
{
if (fa >= 0) {
if (fa != fb) {
close(fb);
fcntl(fa, 0, fb); /* normal dup */
close(fa);
}
fcntl(fb, 2, 1); /* autoclose for fb */
}
}
So the netto is simply closing FD number INIO(19);
Simple test for reproducing:
$ exec 19>&1
$ echo aaa >&19
aaa
$ bash -c 'echo aaa >&19'
aaa
$ dash -c 'echo aaa >&19'
dash: 1: Syntax error: Bad fd number
$ ksh -c 'echo aaa >&19'
aaa
The questions are:
What are the reasons for this strange behavior?
What is wrong with file descriptor 19 ?
19 is special because (long ago), the maximum number of open files was 20, e.g.,
#define _NFILE 20
in stdio.h
In POSIX, you may see other symbols such as OPEN_MAX via the sysconf interface.
File descriptors count from 0, and
are normally assigned in increasing order
so the "last possible" file descriptor would have been 19.
If there was an unused file descriptor, making it last would "work".
Both Solaris sh (in particular up through Solaris 10) and dash date back a while, and the detail you noticed probably was not breaking any legacy shell scripts that mattered (much).

run a program with tcsetattr raw mode in background

I need to run a program as is in the background. The catch is that the program does a tcsetattr() call and sets the raw mode as following :
struct termios tio;
if (tcgetattr(fileno(stdin), &tio) == -1) {
perror("tcgetattr");
return;
}
_saved_tio = tio;
tio.c_iflag |= IGNPAR;
tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
// #ifdef IEXTEN
tio.c_lflag &= ~IEXTEN;
// #endif
tio.c_oflag &= ~OPOST;
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) == -1)
perror("tcsetattr");
else
_in_raw_mode = 1;
The implication is that as soon as I run my program with '&' and press enter, the process shows 'stopped'. Even the ps aux output shows 'T' as the process state which means it is not running.
How can I make this program running in background.Issue is I cant modify this program.
For complete details, actually I need to use ipmitool with 'sol' as a background process.
Any help is appreciated !
Thanks
It is hard to give a complete answer on what is going wrong without knowledge of how ipmitool is actually used/started but I'll try to add some details.
So all the options in the question are needed to adjust i/o for the program (see comments):
// ignorance of errors of parity bit
tio.c_iflag |= IGNPAR;
// removed any interpretation of symbols (CR, NL) for input and control signals
tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
// switch off generation of signals for special characters, non-canonical mode is on,
// no echo, no reaction to kill character etc
tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
// removed recognition of some spec characters
// #ifdef IEXTEN
tio.c_lflag &= ~IEXTEN;
// #endif
// disable special impl-based output processing
tio.c_oflag &= ~OPOST;
// minimum number of characters to read in non-canonical mode
tio.c_cc[VMIN] = 1;
// timeout -> 0
tio.c_cc[VTIME] = 0;
// accurately make all the adjustments then it will be possible
if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) == -1)
perror("tcsetattr");
else
_in_raw_mode = 1;
More details on terminal configuring are here and here.
In other words this part of code configures the standard input of the process to a "complete silent" or "raw" mode.
In spite of the lack of the information you may also try "kill -cont %PID%" to the process or try to provide some file as standard input for it.

Resources