xattr.c 11.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
/*
 *   fs/cifs/xattr.c
 *
S
Steve French 已提交
4
 *   Copyright (c) International Business Machines  Corp., 2003, 2007
L
Linus Torvalds 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *   Author(s): Steve French (sfrench@us.ibm.com)
 *
 *   This library is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published
 *   by the Free Software Foundation; either version 2.1 of the License, or
 *   (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with this library; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <linux/fs.h>
#include <linux/posix_acl_xattr.h>
24
#include <linux/slab.h>
25
#include <linux/xattr.h>
L
Linus Torvalds 已提交
26 27 28 29 30 31 32 33
#include "cifsfs.h"
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifsproto.h"
#include "cifs_debug.h"

#define MAX_EA_VALUE_SIZE 65535
#define CIFS_XATTR_DOS_ATTRIB "user.DosAttrib"
34
#define CIFS_XATTR_CIFS_ACL "system.cifs_acl"
L
Linus Torvalds 已提交
35

36
/* BB need to add server (Samba e.g) support for security and trusted prefix */
L
Linus Torvalds 已提交
37

S
Steve French 已提交
38
int cifs_removexattr(struct dentry *direntry, const char *ea_name)
L
Linus Torvalds 已提交
39 40 41
{
	int rc = -EOPNOTSUPP;
#ifdef CONFIG_CIFS_XATTR
42
	unsigned int xid;
L
Linus Torvalds 已提交
43
	struct cifs_sb_info *cifs_sb;
44
	struct tcon_link *tlink;
45
	struct cifs_tcon *pTcon;
S
Steve French 已提交
46
	struct super_block *sb;
47
	char *full_path = NULL;
S
Steve French 已提交
48 49

	if (direntry == NULL)
L
Linus Torvalds 已提交
50
		return -EIO;
S
Steve French 已提交
51
	if (direntry->d_inode == NULL)
L
Linus Torvalds 已提交
52 53
		return -EIO;
	sb = direntry->d_inode->i_sb;
S
Steve French 已提交
54
	if (sb == NULL)
L
Linus Torvalds 已提交
55
		return -EIO;
S
Steve French 已提交
56

L
Linus Torvalds 已提交
57
	cifs_sb = CIFS_SB(sb);
58 59 60 61 62
	tlink = cifs_sb_tlink(cifs_sb);
	if (IS_ERR(tlink))
		return PTR_ERR(tlink);
	pTcon = tlink_tcon(tlink);

63
	xid = get_xid();
S
Steve French 已提交
64

L
Linus Torvalds 已提交
65
	full_path = build_path_from_dentry(direntry);
S
Steve French 已提交
66
	if (full_path == NULL) {
67
		rc = -ENOMEM;
68
		goto remove_ea_exit;
L
Linus Torvalds 已提交
69
	}
S
Steve French 已提交
70
	if (ea_name == NULL) {
71
		cFYI(1, "Null xattr names not supported");
72 73
	} else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)
		&& (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN))) {
S
Steve French 已提交
74
		cFYI(1,
75 76
		     "illegal xattr request %s (only user namespace supported)",
		     ea_name);
L
Linus Torvalds 已提交
77 78 79 80
		/* BB what if no namespace prefix? */
		/* Should we just pass them to server, except for
		system and perhaps security prefixes? */
	} else {
S
Steve French 已提交
81
		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
L
Linus Torvalds 已提交
82 83
			goto remove_ea_exit;

84
		ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */
S
Steve French 已提交
85
		rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, NULL,
86 87
			(__u16)0, cifs_sb->local_nls,
			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
L
Linus Torvalds 已提交
88 89
	}
remove_ea_exit:
J
Jesper Juhl 已提交
90
	kfree(full_path);
91
	free_xid(xid);
92
	cifs_put_tlink(tlink);
L
Linus Torvalds 已提交
93 94 95 96
#endif
	return rc;
}

S
Steve French 已提交
97 98
int cifs_setxattr(struct dentry *direntry, const char *ea_name,
		  const void *ea_value, size_t value_size, int flags)
L
Linus Torvalds 已提交
99 100 101
{
	int rc = -EOPNOTSUPP;
#ifdef CONFIG_CIFS_XATTR
102
	unsigned int xid;
L
Linus Torvalds 已提交
103
	struct cifs_sb_info *cifs_sb;
104
	struct tcon_link *tlink;
105
	struct cifs_tcon *pTcon;
S
Steve French 已提交
106 107
	struct super_block *sb;
	char *full_path;
L
Linus Torvalds 已提交
108

S
Steve French 已提交
109
	if (direntry == NULL)
L
Linus Torvalds 已提交
110
		return -EIO;
S
Steve French 已提交
111
	if (direntry->d_inode == NULL)
L
Linus Torvalds 已提交
112 113
		return -EIO;
	sb = direntry->d_inode->i_sb;
S
Steve French 已提交
114
	if (sb == NULL)
L
Linus Torvalds 已提交
115 116 117
		return -EIO;

	cifs_sb = CIFS_SB(sb);
118 119 120 121 122
	tlink = cifs_sb_tlink(cifs_sb);
	if (IS_ERR(tlink))
		return PTR_ERR(tlink);
	pTcon = tlink_tcon(tlink);

123
	xid = get_xid();
L
Linus Torvalds 已提交
124 125

	full_path = build_path_from_dentry(direntry);
S
Steve French 已提交
126
	if (full_path == NULL) {
127
		rc = -ENOMEM;
128
		goto set_ea_exit;
L
Linus Torvalds 已提交
129 130 131 132 133
	}
	/* return dos attributes as pseudo xattr */
	/* return alt name if available as pseudo attr */

	/* if proc/fs/cifs/streamstoxattr is set then
S
Steve French 已提交
134
		search server for EAs or streams to
L
Linus Torvalds 已提交
135
		returns as xattrs */
S
Steve French 已提交
136
	if (value_size > MAX_EA_VALUE_SIZE) {
137
		cFYI(1, "size of EA value too large");
138 139
		rc = -EOPNOTSUPP;
		goto set_ea_exit;
L
Linus Torvalds 已提交
140 141
	}

S
Steve French 已提交
142
	if (ea_name == NULL) {
143
		cFYI(1, "Null xattr names not supported");
144 145
	} else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)
		   == 0) {
S
Steve French 已提交
146
		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
L
Linus Torvalds 已提交
147
			goto set_ea_exit;
S
Steve French 已提交
148
		if (strncmp(ea_name, CIFS_XATTR_DOS_ATTRIB, 14) == 0)
149
			cFYI(1, "attempt to set cifs inode metadata");
S
Steve French 已提交
150

151
		ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */
S
Steve French 已提交
152
		rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value,
153 154
			(__u16)value_size, cifs_sb->local_nls,
			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
155 156
	} else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN)
		   == 0) {
S
Steve French 已提交
157
		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
L
Linus Torvalds 已提交
158 159
			goto set_ea_exit;

160
		ea_name += XATTR_OS2_PREFIX_LEN; /* skip past os2. prefix */
S
Steve French 已提交
161
		rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value,
162 163
			(__u16)value_size, cifs_sb->local_nls,
			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
164 165
	} else if (strncmp(ea_name, CIFS_XATTR_CIFS_ACL,
			strlen(CIFS_XATTR_CIFS_ACL)) == 0) {
166 167
#ifdef CONFIG_CIFS_ACL
		struct cifs_ntsd *pacl;
168 169 170 171 172 173 174 175
		pacl = kmalloc(value_size, GFP_KERNEL);
		if (!pacl) {
			cFYI(1, "%s: Can't allocate memory for ACL",
					__func__);
			rc = -ENOMEM;
		} else {
			memcpy(pacl, ea_value, value_size);
			rc = set_cifs_acl(pacl, value_size,
176
				direntry->d_inode, full_path, CIFS_ACL_DACL);
177 178 179
			if (rc == 0) /* force revalidate of the inode */
				CIFS_I(direntry->d_inode)->time = 0;
			kfree(pacl);
180
		}
181 182 183
#else
			cFYI(1, "Set CIFS ACL not supported yet");
#endif /* CONFIG_CIFS_ACL */
L
Linus Torvalds 已提交
184
	} else {
S
Steve French 已提交
185 186
		int temp;
		temp = strncmp(ea_name, POSIX_ACL_XATTR_ACCESS,
L
Linus Torvalds 已提交
187 188 189
			strlen(POSIX_ACL_XATTR_ACCESS));
		if (temp == 0) {
#ifdef CONFIG_CIFS_POSIX
S
Steve French 已提交
190 191 192 193 194
			if (sb->s_flags & MS_POSIXACL)
				rc = CIFSSMBSetPosixACL(xid, pTcon, full_path,
					ea_value, (const int)value_size,
					ACL_TYPE_ACCESS, cifs_sb->local_nls,
					cifs_sb->mnt_cifs_flags &
195
						CIFS_MOUNT_MAP_SPECIAL_CHR);
196
			cFYI(1, "set POSIX ACL rc %d", rc);
L
Linus Torvalds 已提交
197
#else
198
			cFYI(1, "set POSIX ACL not supported");
L
Linus Torvalds 已提交
199
#endif
S
Steve French 已提交
200 201
		} else if (strncmp(ea_name, POSIX_ACL_XATTR_DEFAULT,
				   strlen(POSIX_ACL_XATTR_DEFAULT)) == 0) {
L
Linus Torvalds 已提交
202
#ifdef CONFIG_CIFS_POSIX
S
Steve French 已提交
203 204 205
			if (sb->s_flags & MS_POSIXACL)
				rc = CIFSSMBSetPosixACL(xid, pTcon, full_path,
					ea_value, (const int)value_size,
206
					ACL_TYPE_DEFAULT, cifs_sb->local_nls,
S
Steve French 已提交
207
					cifs_sb->mnt_cifs_flags &
208
						CIFS_MOUNT_MAP_SPECIAL_CHR);
209
			cFYI(1, "set POSIX default ACL rc %d", rc);
L
Linus Torvalds 已提交
210
#else
211
			cFYI(1, "set default POSIX ACL not supported");
L
Linus Torvalds 已提交
212 213
#endif
		} else {
214 215
			cFYI(1, "illegal xattr request %s (only user namespace"
				" supported)", ea_name);
L
Linus Torvalds 已提交
216
		  /* BB what if no namespace prefix? */
S
Steve French 已提交
217
		  /* Should we just pass them to server, except for
L
Linus Torvalds 已提交
218 219 220 221 222
		  system and perhaps security prefixes? */
		}
	}

set_ea_exit:
J
Jesper Juhl 已提交
223
	kfree(full_path);
224
	free_xid(xid);
225
	cifs_put_tlink(tlink);
L
Linus Torvalds 已提交
226 227 228 229
#endif
	return rc;
}

S
Steve French 已提交
230 231
ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
	void *ea_value, size_t buf_size)
L
Linus Torvalds 已提交
232 233 234
{
	ssize_t rc = -EOPNOTSUPP;
#ifdef CONFIG_CIFS_XATTR
235
	unsigned int xid;
L
Linus Torvalds 已提交
236
	struct cifs_sb_info *cifs_sb;
237
	struct tcon_link *tlink;
238
	struct cifs_tcon *pTcon;
S
Steve French 已提交
239 240
	struct super_block *sb;
	char *full_path;
L
Linus Torvalds 已提交
241

S
Steve French 已提交
242
	if (direntry == NULL)
L
Linus Torvalds 已提交
243
		return -EIO;
S
Steve French 已提交
244
	if (direntry->d_inode == NULL)
L
Linus Torvalds 已提交
245 246
		return -EIO;
	sb = direntry->d_inode->i_sb;
S
Steve French 已提交
247
	if (sb == NULL)
L
Linus Torvalds 已提交
248 249 250
		return -EIO;

	cifs_sb = CIFS_SB(sb);
251 252 253 254 255
	tlink = cifs_sb_tlink(cifs_sb);
	if (IS_ERR(tlink))
		return PTR_ERR(tlink);
	pTcon = tlink_tcon(tlink);

256
	xid = get_xid();
L
Linus Torvalds 已提交
257 258

	full_path = build_path_from_dentry(direntry);
S
Steve French 已提交
259
	if (full_path == NULL) {
260
		rc = -ENOMEM;
261
		goto get_ea_exit;
L
Linus Torvalds 已提交
262 263 264
	}
	/* return dos attributes as pseudo xattr */
	/* return alt name if available as pseudo attr */
S
Steve French 已提交
265
	if (ea_name == NULL) {
266
		cFYI(1, "Null xattr names not supported");
267 268
	} else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)
		   == 0) {
S
Steve French 已提交
269
		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
L
Linus Torvalds 已提交
270 271
			goto get_ea_exit;

S
Steve French 已提交
272
		if (strncmp(ea_name, CIFS_XATTR_DOS_ATTRIB, 14) == 0) {
273
			cFYI(1, "attempt to query cifs inode metadata");
L
Linus Torvalds 已提交
274 275
			/* revalidate/getattr then populate from inode */
		} /* BB add else when above is implemented */
276
		ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */
277
		rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value,
278 279
			buf_size, cifs_sb->local_nls,
			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
280
	} else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) == 0) {
S
Steve French 已提交
281
		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
L
Linus Torvalds 已提交
282 283
			goto get_ea_exit;

284
		ea_name += XATTR_OS2_PREFIX_LEN; /* skip past os2. prefix */
285
		rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value,
286 287
			buf_size, cifs_sb->local_nls,
			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
S
Steve French 已提交
288
	} else if (strncmp(ea_name, POSIX_ACL_XATTR_ACCESS,
289
			  strlen(POSIX_ACL_XATTR_ACCESS)) == 0) {
L
Linus Torvalds 已提交
290
#ifdef CONFIG_CIFS_POSIX
S
Steve French 已提交
291
		if (sb->s_flags & MS_POSIXACL)
292
			rc = CIFSSMBGetPosixACL(xid, pTcon, full_path,
S
Steve French 已提交
293
				ea_value, buf_size, ACL_TYPE_ACCESS,
294
				cifs_sb->local_nls,
S
Steve French 已提交
295
				cifs_sb->mnt_cifs_flags &
296
					CIFS_MOUNT_MAP_SPECIAL_CHR);
S
Steve French 已提交
297
#else
298
		cFYI(1, "Query POSIX ACL not supported yet");
L
Linus Torvalds 已提交
299
#endif /* CONFIG_CIFS_POSIX */
S
Steve French 已提交
300
	} else if (strncmp(ea_name, POSIX_ACL_XATTR_DEFAULT,
301
			  strlen(POSIX_ACL_XATTR_DEFAULT)) == 0) {
L
Linus Torvalds 已提交
302
#ifdef CONFIG_CIFS_POSIX
S
Steve French 已提交
303
		if (sb->s_flags & MS_POSIXACL)
304
			rc = CIFSSMBGetPosixACL(xid, pTcon, full_path,
S
Steve French 已提交
305
				ea_value, buf_size, ACL_TYPE_DEFAULT,
306
				cifs_sb->local_nls,
S
Steve French 已提交
307
				cifs_sb->mnt_cifs_flags &
308
					CIFS_MOUNT_MAP_SPECIAL_CHR);
S
Steve French 已提交
309
#else
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
		cFYI(1, "Query POSIX default ACL not supported yet");
#endif /* CONFIG_CIFS_POSIX */
	} else if (strncmp(ea_name, CIFS_XATTR_CIFS_ACL,
				strlen(CIFS_XATTR_CIFS_ACL)) == 0) {
#ifdef CONFIG_CIFS_ACL
			u32 acllen;
			struct cifs_ntsd *pacl;

			pacl = get_cifs_acl(cifs_sb, direntry->d_inode,
						full_path, &acllen);
			if (IS_ERR(pacl)) {
				rc = PTR_ERR(pacl);
				cERROR(1, "%s: error %zd getting sec desc",
						__func__, rc);
			} else {
				if (ea_value) {
					if (acllen > buf_size)
						acllen = -ERANGE;
					else
						memcpy(ea_value, pacl, acllen);
				}
				rc = acllen;
				kfree(pacl);
			}
#else
		cFYI(1, "Query CIFS ACL not supported yet");
#endif /* CONFIG_CIFS_ACL */
S
Steve French 已提交
337
	} else if (strncmp(ea_name,
338
		  XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) == 0) {
339
		cFYI(1, "Trusted xattr namespace not supported yet");
S
Steve French 已提交
340
	} else if (strncmp(ea_name,
341
		  XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) == 0) {
342
		cFYI(1, "Security xattr namespace not supported yet");
S
Steve French 已提交
343
	} else
S
Steve French 已提交
344
		cFYI(1,
345 346
		    "illegal xattr request %s (only user namespace supported)",
		     ea_name);
L
Linus Torvalds 已提交
347

S
Steve French 已提交
348
	/* We could add an additional check for streams ie
L
Linus Torvalds 已提交
349
	    if proc/fs/cifs/streamstoxattr is set then
S
Steve French 已提交
350
		search server for EAs or streams to
L
Linus Torvalds 已提交
351 352
		returns as xattrs */

S
Steve French 已提交
353 354
	if (rc == -EINVAL)
		rc = -EOPNOTSUPP;
L
Linus Torvalds 已提交
355 356

get_ea_exit:
J
Jesper Juhl 已提交
357
	kfree(full_path);
358
	free_xid(xid);
359
	cifs_put_tlink(tlink);
L
Linus Torvalds 已提交
360 361 362 363
#endif
	return rc;
}

S
Steve French 已提交
364
ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
L
Linus Torvalds 已提交
365 366 367
{
	ssize_t rc = -EOPNOTSUPP;
#ifdef CONFIG_CIFS_XATTR
368
	unsigned int xid;
L
Linus Torvalds 已提交
369
	struct cifs_sb_info *cifs_sb;
370
	struct tcon_link *tlink;
371
	struct cifs_tcon *pTcon;
S
Steve French 已提交
372 373
	struct super_block *sb;
	char *full_path;
L
Linus Torvalds 已提交
374

S
Steve French 已提交
375
	if (direntry == NULL)
L
Linus Torvalds 已提交
376
		return -EIO;
S
Steve French 已提交
377
	if (direntry->d_inode == NULL)
L
Linus Torvalds 已提交
378 379
		return -EIO;
	sb = direntry->d_inode->i_sb;
S
Steve French 已提交
380
	if (sb == NULL)
L
Linus Torvalds 已提交
381 382 383
		return -EIO;

	cifs_sb = CIFS_SB(sb);
S
Steve French 已提交
384
	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
385 386
		return -EOPNOTSUPP;

387 388 389 390 391
	tlink = cifs_sb_tlink(cifs_sb);
	if (IS_ERR(tlink))
		return PTR_ERR(tlink);
	pTcon = tlink_tcon(tlink);

392
	xid = get_xid();
393

L
Linus Torvalds 已提交
394
	full_path = build_path_from_dentry(direntry);
S
Steve French 已提交
395
	if (full_path == NULL) {
396
		rc = -ENOMEM;
397
		goto list_ea_exit;
L
Linus Torvalds 已提交
398 399 400 401 402
	}
	/* return dos attributes as pseudo xattr */
	/* return alt name if available as pseudo attr */

	/* if proc/fs/cifs/streamstoxattr is set then
S
Steve French 已提交
403
		search server for EAs or streams to
L
Linus Torvalds 已提交
404
		returns as xattrs */
405 406
	rc = CIFSSMBQAllEAs(xid, pTcon, full_path, NULL, data,
				buf_size, cifs_sb->local_nls,
S
Steve French 已提交
407
				cifs_sb->mnt_cifs_flags &
408
					CIFS_MOUNT_MAP_SPECIAL_CHR);
L
Linus Torvalds 已提交
409

410
list_ea_exit:
J
Jesper Juhl 已提交
411
	kfree(full_path);
412
	free_xid(xid);
413
	cifs_put_tlink(tlink);
L
Linus Torvalds 已提交
414 415 416
#endif
	return rc;
}