SSHFS: fix for wrong file permissions on server

Linux 17 Comments »

SSHFS normally propagates the umask of the client to the server. This works great when the sftp server doesn’t care about the umask while creating files or directories. But the problem is, that the openssh sftp server indeed cares about the server side umask. For every file or directory created through the sftp server the umask from the server is subtracted. This means the maximum mode you can get for directories is 0777 – ${server_side_umask}. On my gentoo box the default umask is 0022, so this would result in:

0777 – 0022 = 0755 (drwxr-xr-x)

As you can see the created folder would not be group writable. This doesn’t matter as long as you work alone in your home directory. But what if you wanna share data with other members of a group?

Before the final solution is discussed I want to discuss two other approaches:

Approach 1: using fuse umask option

Fuse offers a umask option:

1
2
$ sshfs --help | grep umask
-o umask=M             set file permissions (octal)

This option suggests a solution for the problem described above. Let’s look whether this solution works:

1
2
3
4
5
6
7
8
9
# mount remote directory:
client $ sshfs -f -o umask=0002 andre@fs:sshfstest /home/andre/tmp/sshfstest

# create a test directory on the mountpoint
client $ mkdir ~/tmp/sshfstest/testdir1
client $ ls -al ~/tmp/sshfstest/
insgesamt 20
[...]
drwxrwxr-x  1 andre users  4096  3. Aug 16:59 testdir1

Looks like file permissions are set correctly. But wait, we still have to look at the server side:

1
2
3
4
server $ ls -al sshfstest/
total 12
[...]
drwxr-xr-x  2 andre users 4096  3. Aug 16:59 testdir1

As you can see the file permissions are NOT propagated to the server. Because fuse works on the client side, the result is not really surprising. It can only emulate umasks on the client side due to the fact that fuse doesn’t know how sshfs operates. This means this approach does not solve the described problem. Let’s look to another approach:

Approach 2: chmod every created element

As pointed out, file and directory creation causes problems with permissions. Maybe chmod fixes the problem, let’s look:

1
2
3
4
5
6
7
client $ sshfs -f andre@fs:sshfstest /home/andre/tmp/sshfstest
client $ mkdir ~/tmp/sshfstest/testdir2
client $ chmod a=rwx ~/tmp/sshfstest/testdir2/
client $ ls -al ~/tmp/sshfstest/
insgesamt 24
[...]
drwxrwxrwx  1 andre users  4096  3. Aug 17:05 testdir2

Seems like chmod worked on the client side. Now let’s have a look at the server side:

1
2
3
server $ ls -al sshfstest/
[...]
drwxrwxrwx  2 andre users 4096  3. Aug 17:05 testdir2

File permissions are also on the server as desired. In principle you can chmod every created file or directory by your own. Of course you can run a cronjob to do that regularly, but this is a really terrible solution. Better look at the next approach:

(final) Approach 3: apply a patch to sshfs

Approach 1 and Approch 2 hit me on an idea. Why not integrate those two approaches in sshfs ? A look to the sourcecode of sshfs told me that it is simple to fix the permission issue. So I finally came up with a patch to sshfs. The patched version of sshfs runs the chmod itself on every created file or directory:

1
2
3
4
client $ sshfs -f -o remote_dmask=0777 -o remote_fmask=0777 andre@fs:sshfstest ~/tmp/sshfstest/
client $ ls -al tmp/sshfstest/
[...]
drwxrwxrwx  1 andre users  4096  3. Aug 17:10 testdir3

Looks good, now look at the server side:

1
2
3
server $ ls -al sshfstest/
[...]
drwxrwxrwx  2 andre users 4096  3. Aug 17:10 testdir3

Works also as desired. The two parameters remote_dmask and remote_fmask tell the patched sshfs to do a chmod after every file or directoy creation. As you can see this works perfectly and only means a little overhead compared to run a cronjob every x minutes or to care yourself about it.

SSHFS Patch

You can download the patch from here (version 2.2): Patch for sshfs 2.2 that allows arbitrary file and directory creation mask on server (2.73 kB)

UPDATE: The patch for sshfs 2.4: Patch for sshfs 2.4 that allows arbitrary file and directory creation mask on server (2.74 kB)

Or if you want the ready to build distribution, you can download it from here: sshfs-fuse-2.2 fixed version that allows arbitrary file and dir creation mask on server (282.96 kB)

AUTO_INCREMENT workaround for innodb

Linux No Comments »

As the MySQL Documentation under http://dev.mysql.com/doc/refman/5.0/en/innodb-auto-increment-handling.html points out, InnoDB stores AUTO_INCREMENT values only in memory. This has the impact, that auto_increment values don’t remain stable due to server restarts. To clearify the problem, we consider the following table structure with a starting auto_increment of 1000000:

1
2
3
4
CREATE TABLE auto_increment_test (
  id INT(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  some_content VARCHAR(255) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=1000000;

Now insert a new record, delete it afterwards and restart the Server:

1
2
mysql> INSERT INTO auto_increment_test (some_content) VALUES ('this is a test');
Query OK, 1 ROW affected (0.00 sec)
1
2
3
4
5
6
7
mysql> SELECT * FROM auto_increment_test;
+---------+----------------+
| id      | some_content   |
+---------+----------------+
| 1000000 | this IS a test |
+---------+----------------+
1 ROW IN SET (0.00 sec)
1
2
mysql> DELETE FROM auto_increment_test;
Query OK, 1 ROW affected (0.02 sec)
1
# /etc/init.d/mysql restart

Now redo the example from above:

1
2
mysql> INSERT INTO auto_increment_test (some_content) VALUES ('this is a test');
Query OK, 1 ROW affected (0.01 sec)
1
2
3
4
5
6
7
mysql> SELECT * FROM auto_increment_test;
+----+----------------+
| id | some_content   |
+----+----------------+
|  1 | this IS a test |
+----+----------------+
1 ROW IN SET (0.00 sec)

This is really weird, because the auto_increment starts from now on by 1. This is not what I wanted with the CREATE TABLE … AUTO_INCREMENT=1000000 statement. You don’t even get a notification, that things don’t work as you expect.

My solution for this problem is a trigger, that verifies the auto_increment value and sets it appropriate. It uses the mysql information_schema to lookup the current auto_increment value, so this should really be a really small overhead. Have look at it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
delimiter //

CREATE TRIGGER trigger_auto_increment_test BEFORE INSERT ON auto_increment_test
FOR EACH ROW
BEGIN
DECLARE auto_incr BIGINT;

SELECT AUTO_INCREMENT INTO auto_incr FROM information_schema.TABLES WHERE table_schema=DATABASE() AND TABLE_NAME='auto_increment_test';
  IF (auto_incr < 1000000) THEN
      SET NEW.id = 1000000;
  END IF;
END;//

delimiter ;

All in all MySQL should really print a warning about that, because the syntax of the CREATE TABLE statement suggest another behaviour. Nevertheless you can use the above mentioned solution to avoid the problem.