-
Notifications
You must be signed in to change notification settings - Fork 460
Fix handling of objects with many xattrs on FreeBSD #766
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -126,17 +126,26 @@ ssize_t sys_llistxattr(const char *path, char *list, size_t size) | |
| unsigned char keylen; | ||
| ssize_t off, len = extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size); | ||
|
|
||
| if (len <= 0 || (size_t)len > size) | ||
| if (len <= 0 || size == 0) | ||
| return len; | ||
|
|
||
| if ((size_t)len >= size) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The equal test is not required anymore just do
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem is that if extattr_list_link(..., size) returns len==size then we don't know if we got all the data or if there is more data to read. It might be exactly 'size' bytes that was available there, or it might be more, but since extattr_list_link() will return just 'size' in both cases then that will fail. Now the code after that which parses the array might return EINVAL if we just got a partial buffer - but there is a small chance that the list of extattrs is so perfectly aligned that it terminates exactly at the 'size' border - but there are still more extattrs that could have been read if the buffer was bigger... |
||
| /* FreeBSD extattr_list_xx() returns 'size' as 'len' in case there are | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can make the comment simpler : "buffer is too small, behave as linux and request a bigger buffer" |
||
| more data available, truncating the output, we solve this by signalling | ||
| ERANGE in case len == size so that the code in xattrs.c will retry with | ||
| a bigger buffer */ | ||
| errno = ERANGE; | ||
| return -1; | ||
| } | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now we can perform the real call to retrieve the buffer: Edit: Catch an invalid memory access error |
||
| /* FreeBSD puts a single-byte length before each string, with no '\0' | ||
| * terminator. We need to change this into a series of null-terminted | ||
| * strings. Since the size is the same, we can simply transform the | ||
| * output in place. */ | ||
| for (off = 0; off < len; off += keylen + 1) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This for loop is quite inefficient performing multiple memmove(2) calls, what about doing it one: Edited: Added a final test to ensure second
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That looks like a good suggestion, I never looked at that part of the code :-). However, isn't the list[len-1] = '\0' overwriting the next keylen before you read it?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No because we did the memmove before changing list[len-1] before |
||
| keylen = ((unsigned char*)list)[off]; | ||
| if (off + keylen >= len) { | ||
| /* Should be impossible, but kernel bugs happen! */ | ||
| /* Should be impossible, but bugs happen! */ | ||
| errno = EINVAL; | ||
| return -1; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Called this way
extattr_list_link(2)behaves likeread(2)and return the amount of bytes there was able to write in the list buffer which is not that informative and can misleading if list has the required size.I suggest to perform a call of
extattr_list_link(2)with a null pointer which returns us the exact amount of bytes required in the buffer without perform any write.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, and no. Doing it that way has a potential to introduce a race condition if someone changes the extattr in the (very short) time between then call with NULL was done and when you actually do the real read. Doing it the way it's done now removes that race.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ask myself the same question when doing my tests.
But if you consider that rsync process extended attributes by
the race condition you describe can occurs at any time (eg. if you start removing xattrs when rsync is working)
If you are looking for full consistent remote copy tool, rsync only is probably not the most appropriate tool and you should consider make zfs snapshots, mount them then use rsync for the file transfer.
In fact, in worst case scenario, we can have a truncated key value which makes a particular file transfer fail because the value associated with the broken key is missing. But I don't expect any crash.