On Apr 3, 2017, at 8:56 AM, Scott Talbert <email@hidden>
wrote:
It's getting a value from the getattrlist path.
The fact that the data is cached at mount time is an interesting
clue. It suggest that perhaps there may be some window just shortly
after mount that the incorrect data is getting reported and cached.
It's a FUSE filesystem. However, some of the more commonly-used FUSE
filesystems (e.g., sshfs) seem to behave the same way.
That sounds like a bug in FUSE. Once a file system is mounted, the kernel
sends out a VQ_MOUNT kernel event and that's what causes the
kNotifyVFSMount BSD notification to be sent. At that time, file systems
must be ready to respond to requests correctly. If they don't, they have a
bug.
- Jim
On Sat, 1 Apr 2017, Jim Luther wrote:
Scott, if you walk through that code, do you see
which path it's getting a value from -- pathconf or
getattrlist?
The code in our frameworks that gets that value
runs when a file system is first mounted. We have
code listening for kNotifyVFSMount BSD
notifications. When a notification comes in, we
look to see what new volume(s) are available, and
then collect and cache the volume's immutable
properties (like this capability).
Knowing more about the file system (what file
system type, how it's connected, etc) where you are
seeing the problem might help diagnose things.
- Jim
On Mar 31, 2017, at 5:10 PM, Scott
Talbert <email@hidden> wrote:
Thanks for the information.
Unfortunately, that leaves me even
more
confused as, using that code, I get the
correct answer:
#include <sys/attr.h>
#include <sys/param.h>
#include <sys/mount.h>
#import <Foundation/Foundation.h>
int getMaxFileSize(const char *path,
int64_t *maxFileSize)
{
int result;
struct statfs statfsBuf;
// get the file system's mount point
path for the input path
result = statfs(path, &statfsBuf);
if ( result == 0 ) {
long fileSizeBits =
pathconf(statfsBuf.f_mntonname,
_PC_FILESIZEBITS);
if (fileSizeBits == -1) {
// if _PC_FILESIZEBITS isn't
supported, check for
VOL_CAP_FMT_2TB_FILESIZE
bool
fileSystemSupports2TBFileSize = false;
// get the supported
capabilities
struct attrlist attrList;
struct volCapabilitiesBuf {
u_int32_t length;
vol_capabilities_attr_t
capabilities;
} __attribute__((aligned(4),
packed));
struct volCapabilitiesBuf
volCaps;
memset(&attrList, 0,
sizeof(attrList));
attrList.bitmapcount =
ATTR_BIT_MAP_COUNT;
attrList.volattr =
ATTR_VOL_INFO | ATTR_VOL_CAPABILITIES;
result =
getattrlist(statfsBuf.f_mntonname,
&attrList,
&volCaps, sizeof(volCaps), 0);
if ( result == 0 ) {
fileSystemSupports2TBFileSize
=
((volCaps.capabilities.capabilities[VOL_CAPABILITIES_FORMAT]
& VOL_CAP_FMT_2TB_FILESIZE) &&
(volCaps.capabilities.valid[VOL_CAPABILITIES_FORMAT]
&
VOL_CAP_FMT_2TB_FILESIZE));
}
if (
fileSystemSupports2TBFileSize ) {
// use
Supports2TBFileSize
*maxFileSize =
LONG_LONG_MAX;
}
else {
// otherwise, we don't
know
*maxFileSize = -1LL;
}
}
else if ( fileSizeBits > 64 ) {
// off_t is signed long long,
so it cannot be more than
LONG_LONG_MAX
*maxFileSize = LONG_LONG_MAX;
}
else if ( fileSizeBits < 32 ) {
// POSIX spec says 'Minimum
Acceptable Value: 32' for
FILESIZEBITS
*maxFileSize = INT_MAX;
}
else {
// 32...64 bits: shift off
the bits we don't need
*maxFileSize =
(int64_t)((u_int64_t)0xffffffffffffffffLL
>>
(u_int64_t)(65 - fileSizeBits));
}
}
return result;
}
int main()
{
const char *mount =
"/Volumes/Filesystem";
int64_t maxFileSize;
int ret1 = getMaxFileSize(mount,
&maxFileSize);
NSLog(@"getMaxFileSize %d %lld", ret1,
maxFileSize);
NSString *path = [NSString
stringWithUTF8String:mount];
NSURL *url = [NSURL
fileURLWithPath:path];
NSNumber *maxfilesize;
NSError *error;
BOOL ret = [url
getResourceValue:&maxfilesize
forKey:NSURLVolumeMaximumFileSizeKey
error:&error];
NSLog(@"NSURLVolumeMaximumFileSize: %d
%@ %@", ret, maxfilesize, error);
}
$ ./blahm
2017-03-31 20:04:34.358
blahm[45735:2028260] getMaxFileSize 0
9223372036854775807
2017-03-31 20:04:34.365
blahm[45735:2028260]
NSURLVolumeMaximumFileSize: 1
(null) (null)
As -[NSURL
getResourceValue:forKey:error:]
is documented, "If this
method
returns YES and the value
is populated with nil, it
means that the
resource property is not
available for the specified
resource, and that no
errors occurred when
determining that the
resource property was
unavailable." So YES and
nil is a valid response.
The
NSURLVolumeMaximumFileSizeKey
property value comes from
two sources:
pathconf() with
_PC_FILESIZEBITS, or from
getattrlist() from the
ATTR_VOL_CAPABILITIES
attribute and the
VOL_CAP_FMT_2TB_FILESIZE
capability.
Here's code (not the real
code but the exact same
algorithm) that shows
how the value is
calculated:
int getMaxFileSize(const
char *path, int64_t
*maxFileSize)
{
int result;
struct statfs statfsBuf;
// get the file system's
mount point path for the
input path
result = statfs(path,
&statfsBuf);
if ( result == 0 ) {
long fileSizeBits =
pathconf(statfsBuf.f_mntonname,
_PC_FILESIZEBITS);
if (fileSizeBits ==
-1) {
// if
_PC_FILESIZEBITS isn't
supported, check for
VOL_CAP_FMT_2TB_FILESIZE
bool
fileSystemSupports2TBFileSize
= false;
// get the
supported capabilities
struct attrlist
attrList;
struct
volCapabilitiesBuf {
u_int32_t
length;
vol_capabilities_attr_t
capabilities;
}
__attribute__((aligned(4),
packed));
struct
volCapabilitiesBuf volCaps;
memset(&attrList,
0, sizeof(attrList));
attrList.bitmapcount
= ATTR_BIT_MAP_COUNT;
attrList.volattr
= ATTR_VOL_INFO |
ATTR_VOL_CAPABILITIES;
result =
getattrlist(statfsBuf.f_mntonname,
&attrList,
&volCaps, sizeof(volCaps),
0);
if ( result == 0
) {
fileSystemSupports2TBFileSize
=
((volCaps.capabilities.capabilities[VOL_CAPABILITIES_FORMAT]
& VOL_CAP_FMT_2TB_FILESIZE)
&&
(volCaps.capabilities.valid[VOL_CAPABILITIES_FORMAT]
&
VOL_CAP_FMT_2TB_FILESIZE));
}
if (
fileSystemSupports2TBFileSize
) {
// use
Supports2TBFileSize
*maxFileSize
= LONG_LONG_MAX;
}
else {
// otherwise,
we don't know
*maxFileSize
= -1LL;
}
}
else if (
fileSizeBits > 64 ) {
// off_t is
signed long long, so it
cannot be more than
LONG_LONG_MAX
*maxFileSize =
LONG_LONG_MAX;
}
else if (
fileSizeBits < 32 ) {
// POSIX spec
says 'Minimum Acceptable
Value: 32' for
FILESIZEBITS
*maxFileSize =
INT_MAX;
}
else {
// 32...64 bits:
shift off the bits we don't
need
*maxFileSize =
(int64_t)((u_int64_t)0xffffffffffffffffLL
>>
(u_int64_t)(65 -
fileSizeBits));
}
}
return result;
}
if
NSURLVolumeMaximumFileSizeKey
is returning nil with no
errors,
maxFileSize is -1LL, and
the only way maxFileSize
will be -1LL is if
pathconf() returned an
error, and the file system
is saying
VOL_CAP_FMT_2TB_FILESIZE is
not valid and set
(capabilities).
Hope that helpsâ?¢
- Jim
On Mar 31,
2017, at 2:33
PM, Scott
Talbert
<email@hidden>
wrote:
Hello,
Can anyone tell
me why calling
NSURL
-getResourceValue:forKey:error:
with key
NSURLVolumeMaximumFileSizeKey
would return a
nil resource
value
for a given
volume? The
function
returns YES and
error is nil
also.
I can see from
running dtruss
that it in turn
calls
getattrlist().
If I
call
getattrlist()
myself on the
same volume, I
can see the
bits I would
think it is
looking at are
set correctly -
VOL_CAP_FMT_2TB_FILESIZE.
Thanks,
Scott
_______________________________________________
Do not post
admin requests
to the list.
They will be
ignored.
Filesystem-dev
mailing list
(email@hidden)
Help/Unsubscribe/Update
your
Subscription:
m
This email sent
to
email@hidden