I had a lot of troubles with BULK INSERT on linux, where a valid path name was rejected:
# sqlcmd -S localhost -d leak -U sa -Q "BULK INSERT leak FROM '/tmp/data.txt'"
Msg 12703, Level 16, State 1, Server mssql, Line 1
Referenced external data source "(null)" not found.
# ll /tmp/data.txt
-rw-r--r-- 1 root root 30M Feb 20 02:40 /tmp/data.txt
And the suggested workaround to replace slashes with backslashes and prepending C:\ didn't work:
# sqlcmd -S localhost -d leak -U sa -Q "BULK INSERT leak FROM 'C:\\tmp\\data.txt'"
Msg 12703, Level 16, State 1, Server mssql, Line 1
Referenced external data source "(null)" not found.
So, I investigated a bit, and by running strace on the daemon I found this:
# strace -fp 3198 -e open
strace: Process 3198 attached with 175 threads
[pid 3202] open("/proc/self/status", O_RDONLY) = 170
[pid 3202] open("/proc/meminfo", O_RDONLY) = 170
[pid 3321] open("/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 175
[pid 3321] open("/tmp/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 175
[pid 3321] open("/tmp/data.txt", O_RDONLY) = 175
[pid 3321] open("/tmp/data.txt", O_RDONLY|O_DIRECT) = -1 EINVAL (Invalid argument)
The open() fails because O_DIRECT is not supported by the tmpfs filesystem. So I moved the file in the root and I made it world accessible:
# ll /data.txt
-rw-rw-rw- 1 root root 30M Feb 20 02:28 /data.txt
# ll /data.txt
-rw-rw-rw- 1 root root 30M Feb 20 02:28 /data.txt
# sqlcmd -S localhost -d leak -U sa -Q "BULK INSERT leak FROM '/data.txt'"
Msg 4860, Level 16, State 1, Server mssql, Line 1
Cannot bulk load. The file "/data.txt" does not exist or you don't have file access rights.
But this time the server doesn't even tries to access the file. Moving the file in a world accessible directory other than the root, fixed it:
# sqlcmd -S localhost -d leak -U sa -Q "BULK INSERT leak FROM '/media/data.txt'"
(1000000 rows affected)