ZFS is all the rage now and there are lots of tutorials and how-to’s out there covering most of the topics. There is one issue, for which I could not find any ready solution. When sharing a zfs volume over Samba, Windows would report incorrect total volume size. More precisely, Windows would always show the same size for both total size and free size and both values will be changing as the volume gets used.
This is obviously not what we want. Some digging uncovered that Samba relies internally on the result of the df program, which will report incorrect values for ZFS systems. More digging lead to this page and to the man pages of smb.conf, showing that it is possible to override space usage detection behaviour by creating a custom script and pointing Samba server to it using the following entry in smb.conf:
[global]
dfree command = /usr/local/bin/dfree
The following bash script is where the magic lies (tested on FreeBSD):
#!/bin/sh CUR_PATH=`pwd` let USED=`zfs get -o value -Hp used $CUR_PATH` / 1024 > /dev/null let AVAIL=`zfs get -o value -Hp available $CUR_PATH` / 1024 > /dev/null let TOTAL = $USED + $AVAIL > /dev/null echo $TOTAL $AVAIL
And the following is a variation, which works on Linux (courtesy commenter nem):
#!/bin/bash CUR_PATH=`pwd` USED=$((`zfs get -o value -Hp used $CUR_PATH` / 1024)) > /dev/null AVAIL=$((`zfs get -o value -Hp available $CUR_PATH` / 1024)) > /dev/null TOTAL=$(($USED+$AVAIL)) > /dev/null echo $TOTAL $AVAIL
Make sure to check the comments section, as several variations of this script are posted there, for example taking account for both ZFS and non-ZFS shares on the same system!
I can’t use zpool list as it reports the total size for the pool, including parity disks, so the total size might be greater than the real usable total size.
zfs list could have been used if there was a way to display the information in bytes and not in human-readable form of varying granularity.
The solution was to use zfs get and then normalise the values reported to Samba to the 1024 byte blocks. (I tried providing the third, optional, parameter of 1 as mentioned in the man pages, but Samba seemed to have trouble parsing really large byte values, so I ended up doing the normalisation in the script).
Also, I can’t rely on the $1 input parameter to the script, as it turned out to always be equal to ‘.’, which is usable for df, but not for zfs. This ‘.’ lead me to check the working directory of the invocation and, bingo, it turned out to be the root path of the requested volume, so I could simply get the value from pwd and pass it to zfs.
I have long been searching for a solution to this annoying problem, so I was happy to find your blog today. I tried your script, but it doesn’t seem to work. Instead of properly reporting the free and total disk sizes, I get 1MB free of 2MB total. I actually have several hundred GB free on a 3TB raidz, so that isn’t quite right.
When I run the script from the command line, I get this:
$ /usr/local/bin/dfree
cannot open ‘/storage/data/private’: invalid dataset name
let: arithmetic expression: syntax error: “USED= / 1024”
cannot open ‘/storage/data/private’: invalid dataset name
let: arithmetic expression: syntax error: “AVAIL= / 1024”
0 0
Any ideas? Thanks!
I’ve figured it out. It seems that the leading slash from the pwd command causes zfs not to recognize a dataset. I’m not sure if that’s the intended behavior or not. But I added the following line to the script after the “CUR_PATH=`pwd`”:
CUR_PATH=${CUR_PATH#/}
That seems to have fixed the issue.
Good to hear that the script helped and that you found the solution to your specific issue.
Which versions do you use? I am on FreeBSD 9.0 with:
$ zpool get version zstore
NAME PROPERTY VALUE SOURCE
zstore version 28 default
$ zfs get -o value version zstore
VALUE
5
And in my case the following command (with the leading slash) produces the expected correct result:
$ zfs get -o value -Hp used /zstore/Generic
1128413938436
Hi!
Ive been searching for a fix for this and stumbled upon your fix here, however i cant get it to work.
Im on Ubuntu.
I added the line to smb.conf and made the script as you did above.
Output:
/usr/local/bin/dfree: 6: /usr/local/bin/dfree: let: not found
/usr/local/bin/dfree: 7: /usr/local/bin/dfree: let: not found
/usr/local/bin/dfree: 9: /usr/local/bin/dfree: let: not found
dfree script is a cut n paste job from your guide.
As you probably notice im fairly new to linux and i dont understand this as well as i should. However it seems that “let” command is not recognized ? If i type let in terminal it is recognized tho.
I also tried it manually using ” zfs get -o value -Hp used /datastore/here” and that returns the expected values..
Please advice,
Thanks!
Hi.
I am on FreeBSD, which has a more mature port of ZFS. 🙂
Which shell are you using as default? I set my /bin/sh to point to bash.
‘Let ‘ in bash is the operator used to assign a value after performing arithmetic operations. (http://ss64.com/bash/let.html)
You can try to change the first line in the script:
#!/bin/sh
to point to bash.
Run the following command:
type -a bash
and copy the reported path to after #!
Hi, thanks for your reply!
Took a while before i got a chance to try out what you said (Bash is indeed the default shell on Ubuntu) and while it removed the error regarding the command, it still wont run correctly.
Below is the output when running the script:
/usr/local/bin/dfree: line 6: let: /: syntax error: operand expected (error token is “/”)
/usr/local/bin/dfree: line 7: let: /: syntax error: operand expected (error token is “/”)
/usr/local/bin/dfree: line 9: let: =: syntax error: operand expected (error token is “=”)
2331817695840
Apparently its working, kinda. But the calculation part doesnt seem to work.. Any further tips?
This might be of help:
http://ubuntuforums.org/showthread.php?t=562978
variableA=$((variableB*variableC))
I don’t have a Linux installation to verify suggestions there.
Here is the updated code for use with Ubuntu and bash:
Thanks, nem. Added your Linux submission to the post.
Nice, I am going to test this on Ubuntu! However, how do I make sure this script is only used for ZFS shares? (Since I also share non-ZFS folders). Can I put “dfree command” also under a share instead of [global]?
I did some experimenting, and following code should work on Ubuntu/Linux. It tries to detect if given path is ZFS and then acts accordingly
See this gist for full code: https://gist.github.com/umito/9198097
I also created a gist that determines if the FS is zfs or not and calculates accordingly. This was tested on ubuntu 14.04, I needed this because some of my mounts are on zfs and some are not, and they have similar beginnings to the name ( /mnt/storage and /mnt/storage2 for example ) would return the disk free for the wrong volume at times, so here is the gist:
https://gist.github.com/sling00/cd69ffbef4b23ec48d10
Thanks for the contribution, sling!