xref: /titanic_41/usr/src/uts/common/fs/smbsrv/smb_fsinfo.c (revision 430b4c467020edf2445feb0c21db01c88b86243a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 #include <smbsrv/smb_kproto.h>
26 #include <smbsrv/smb_fsops.h>
27 #include <smbsrv/smbinfo.h>
28 
29 /*
30  * smb_fssize_t
31  * volume_units and volume avail are the total allocated and
32  * available units on the volume.
33  * caller_units and caller_avail are the allocated and available
34  * units on the volume for the user associated with the calling
35  * thread.
36  */
37 typedef struct smb_fssize {
38 	uint64_t	fs_volume_units;
39 	uint64_t	fs_volume_avail;
40 	uint64_t	fs_caller_units;
41 	uint64_t	fs_caller_avail;
42 	uint32_t	fs_sectors_per_unit;
43 	uint32_t	fs_bytes_per_sector;
44 } smb_fssize_t;
45 
46 /*
47  * File System Control Flags for smb_com_trans2_query|set_fs_information
48  * level SMB_FILE_FS_CONTROL_INFORMATION
49  */
50 #define	FILE_VC_QUOTA_TRACK		0x00000001
51 #define	FILE_VC_QUOTA_ENFORCE		0x00000002
52 #define	FILE_VC_CONTENT_INDEX_DISABLED	0x00000008
53 #define	FILE_VC_LOG_QUOTA_THRESHOLD	0x00000010
54 #define	FILE_VC_LOG_QUOTA_LIMIT		0x00000020
55 #define	FILE_VC_LOG_VOLUME_THRESHOLD	0x00000040
56 #define	FILE_VC_LOG_VOLUME_LIMIT	0x00000080
57 #define	FILE_VC_QUOTAS_INCOMPLETE	0x00000100
58 #define	FILE_VC_QUOTAS_REBUILDING	0x00000200
59 
60 static int smb_fssize(smb_request_t *, smb_fssize_t *);
61 static int smb_trans2_set_fs_ctrl_info(smb_request_t *, smb_xa_t *);
62 
63 /*
64  * smb_com_query_information_disk
65  *
66  * The SMB_COM_QUERY_INFORMATION_DISK command is used to determine the
67  * capacity and remaining free space on the drive hosting the directory
68  * structure indicated by Tid in the SMB header.
69  *
70  * The blocking/allocation units used in this response may be independent
71  * of the actual physical or logical blocking/allocation algorithm(s) used
72  * internally by the server.  However, they must accurately reflect the
73  * amount of space on the server.
74  *
75  * This SMB only returns 16 bits of information for each field, which may
76  * not be large enough for some disk systems.  In particular TotalUnits is
77  * commonly > 64K.  Fortunately, it turns out the all the client cares
78  * about is the total disk size, in bytes, and the free space, in bytes.
79  * So,  it is reasonable for a server to adjust the relative values of
80  * BlocksPerUnit and BlockSize to accommodate.  If after all adjustment,
81  * the numbers are still too high, the largest possible values for
82  * TotalUnit or FreeUnits (i.e. 0xFFFF) should be returned.
83  */
84 
85 smb_sdrc_t
86 smb_pre_query_information_disk(smb_request_t *sr)
87 {
88 	DTRACE_SMB_1(op__QueryInformationDisk__start, smb_request_t *, sr);
89 	return (SDRC_SUCCESS);
90 }
91 
92 void
93 smb_post_query_information_disk(smb_request_t *sr)
94 {
95 	DTRACE_SMB_1(op__QueryInformationDisk__done, smb_request_t *, sr);
96 }
97 
98 smb_sdrc_t
99 smb_com_query_information_disk(smb_request_t *sr)
100 {
101 	int			rc;
102 	fsblkcnt64_t		total_blocks, free_blocks;
103 	unsigned long		block_size, unit_size;
104 	unsigned short		blocks_per_unit, bytes_per_block;
105 	unsigned short		total_units, free_units;
106 	smb_fssize_t		fssize;
107 
108 	if (STYPE_ISIPC(sr->tid_tree->t_res_type)) {
109 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess);
110 		return (SDRC_ERROR);
111 	}
112 
113 	if (smb_fssize(sr, &fssize) != 0)
114 		return (SDRC_ERROR);
115 
116 	unit_size = fssize.fs_sectors_per_unit;
117 	block_size = fssize.fs_bytes_per_sector;
118 	total_blocks = fssize.fs_caller_units;
119 	free_blocks = fssize.fs_caller_avail;
120 
121 	/*
122 	 * It seems that DOS clients cannot handle block sizes
123 	 * bigger than 512 KB. So we have to set the block size at
124 	 * most to 512
125 	 */
126 	while (block_size > 512) {
127 		block_size >>= 1;
128 		unit_size <<= 1;
129 	}
130 
131 	/* adjust blocks and sizes until they fit into a word */
132 	while (total_blocks >= 0xFFFF) {
133 		total_blocks >>= 1;
134 		free_blocks >>= 1;
135 		if ((unit_size <<= 1) > 0xFFFF) {
136 			unit_size >>= 1;
137 			total_blocks = 0xFFFF;
138 			free_blocks <<= 1;
139 			break;
140 		}
141 	}
142 
143 	total_units = (total_blocks >= 0xFFFF) ?
144 	    0xFFFF : (unsigned short)total_blocks;
145 	free_units = (free_blocks >= 0xFFFF) ?
146 	    0xFFFF : (unsigned short)free_blocks;
147 	bytes_per_block = (unsigned short)block_size;
148 	blocks_per_unit = (unsigned short)unit_size;
149 
150 	rc = smbsr_encode_result(sr, 5, 0, "bwwww2.w",
151 	    5,
152 	    total_units,	/* total_units */
153 	    blocks_per_unit,	/* blocks_per_unit */
154 	    bytes_per_block,	/* blocksize */
155 	    free_units,		/* free_units */
156 	    0);			/* bcc */
157 
158 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
159 }
160 
161 /*
162  * smb_com_trans2_query_fs_information
163  *
164  * This transaction requests information about the filesystem.
165  * The following information levels are supported:
166  *
167  *  InformationLevel               	Value
168  *  ==================================  ======
169  *  SMB_INFO_ALLOCATION            	1
170  *  SMB_INFO_VOLUME                	2
171  *  SMB_QUERY_FS_VOLUME_INFO       	0x102
172  *  SMB_QUERY_FS_SIZE_INFO         	0x103
173  *  SMB_QUERY_FS_DEVICE_INFO       	0x104
174  *  SMB_QUERY_FS_ATTRIBUTE_INFO    	0x105
175  *  SMB_FILE_FS_VOLUME_INFORMATION	1001
176  *  SMB_FILE_FS_SIZE_INFORMATION	1003
177  *  SMB_FILE_FS_DEVICE_INFORMATION	1004
178  *  SMB_FILE_FS_ATTRIBUTE_INFORMATION	1005
179  *  SMB_FILE_FS_CONTROL_INFORMATION	1006
180  *  SMB_FILE_FS_FULLSIZE_INFORMATION	1007
181  *
182  * The fsid provides a system-wide unique file system ID.
183  * fsid.val[0] is the 32-bit dev for the file system of the share root
184  * smb_node.
185  * fsid.val[1] is the file system type.
186  */
187 smb_sdrc_t
188 smb_com_trans2_query_fs_information(smb_request_t *sr, smb_xa_t *xa)
189 {
190 	uint32_t		flags;
191 	char			*encode_str, *tmpbuf;
192 	uint64_t		max_int;
193 	uint16_t		infolev;
194 	int			rc, length, buflen;
195 	smb_tree_t		*tree;
196 	smb_node_t		*snode;
197 	char 			*fsname = "NTFS";
198 	fsid_t			fsid;
199 	smb_fssize_t		fssize;
200 	smb_msgbuf_t		mb;
201 
202 	tree = sr->tid_tree;
203 
204 	if (!STYPE_ISDSK(tree->t_res_type)) {
205 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
206 		    ERRDOS, ERROR_ACCESS_DENIED);
207 		return (SDRC_ERROR);
208 	}
209 
210 	if (smb_mbc_decodef(&xa->req_param_mb, "w", &infolev) != 0)
211 		return (SDRC_ERROR);
212 
213 	snode = tree->t_snode;
214 	fsid = SMB_NODE_FSID(snode);
215 
216 	switch (infolev) {
217 	case SMB_INFO_ALLOCATION:
218 		if (smb_fssize(sr, &fssize) != 0)
219 			return (SDRC_ERROR);
220 
221 		max_int = (uint64_t)UINT_MAX;
222 		if (fssize.fs_caller_units > max_int)
223 			fssize.fs_caller_units = max_int;
224 		if (fssize.fs_caller_avail > max_int)
225 			fssize.fs_caller_avail = max_int;
226 
227 		(void) smb_mbc_encodef(&xa->rep_data_mb, "llllw",
228 		    0,
229 		    fssize.fs_sectors_per_unit,
230 		    fssize.fs_caller_units,
231 		    fssize.fs_caller_avail,
232 		    fssize.fs_bytes_per_sector);
233 		break;
234 
235 	case SMB_INFO_VOLUME:
236 		/*
237 		 * In this response, the unicode volume label is NOT
238 		 * expected to be aligned. Encode ('U') into a temporary
239 		 * buffer, then encode buffer as a byte stream ('#c').
240 		 */
241 		if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) ||
242 		    (sr->session->native_os == NATIVE_OS_WIN95)) {
243 			length = smb_wcequiv_strlen(tree->t_volume);
244 			buflen = length + sizeof (smb_wchar_t);
245 			tmpbuf = smb_srm_zalloc(sr, buflen);
246 			smb_msgbuf_init(&mb, (uint8_t *)tmpbuf, buflen,
247 			    SMB_MSGBUF_UNICODE);
248 			rc = smb_msgbuf_encode(&mb, "U", tree->t_volume);
249 			if (rc >= 0) {
250 				rc = smb_mbc_encodef(&xa->rep_data_mb,
251 				    "%lb#c", sr, fsid.val[0],
252 				    length, length, tmpbuf);
253 			}
254 			smb_msgbuf_term(&mb);
255 		} else {
256 			length = strlen(tree->t_volume);
257 			rc = smb_mbc_encodef(&xa->rep_data_mb, "%lbs", sr,
258 			    fsid.val[0], length, tree->t_volume);
259 		}
260 
261 		if (rc < 0)
262 			return (SDRC_ERROR);
263 		break;
264 
265 	case SMB_QUERY_FS_VOLUME_INFO:
266 	case SMB_FILE_FS_VOLUME_INFORMATION:
267 		if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) ||
268 		    (sr->session->native_os == NATIVE_OS_WIN95)) {
269 			length = smb_wcequiv_strlen(tree->t_volume);
270 			encode_str = "%qllb.U";
271 		} else {
272 			length = strlen(tree->t_volume);
273 			encode_str = "%qllb.s";
274 		}
275 
276 		/*
277 		 * NT has the "supports objects" flag set to 1.
278 		 */
279 		(void) smb_mbc_encodef(&xa->rep_data_mb, encode_str, sr,
280 		    0ll,			/* Volume creation time */
281 		    fsid.val[0],		/* Volume serial number */
282 		    length,			/* label length */
283 		    0,				/* Supports objects */
284 		    tree->t_volume);
285 		break;
286 
287 	case SMB_QUERY_FS_SIZE_INFO:
288 	case SMB_FILE_FS_SIZE_INFORMATION:
289 		if (smb_fssize(sr, &fssize) != 0)
290 			return (SDRC_ERROR);
291 
292 		(void) smb_mbc_encodef(&xa->rep_data_mb, "qqll",
293 		    fssize.fs_caller_units,
294 		    fssize.fs_caller_avail,
295 		    fssize.fs_sectors_per_unit,
296 		    fssize.fs_bytes_per_sector);
297 		break;
298 
299 	case SMB_QUERY_FS_DEVICE_INFO:
300 	case SMB_FILE_FS_DEVICE_INFORMATION:
301 		(void) smb_mbc_encodef(&xa->rep_data_mb, "ll",
302 		    FILE_DEVICE_FILE_SYSTEM,
303 		    FILE_DEVICE_IS_MOUNTED);
304 		break;
305 
306 	case SMB_QUERY_FS_ATTRIBUTE_INFO:
307 	case SMB_FILE_FS_ATTRIBUTE_INFORMATION:
308 		if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) ||
309 		    (sr->session->native_os == NATIVE_OS_WINNT) ||
310 		    (sr->session->native_os == NATIVE_OS_WIN2000) ||
311 		    (sr->session->native_os == NATIVE_OS_WIN95) ||
312 		    (sr->session->native_os == NATIVE_OS_MACOS)) {
313 			length = smb_wcequiv_strlen(fsname);
314 			encode_str = "%lllU";
315 			sr->smb_flg2 |= SMB_FLAGS2_UNICODE;
316 		} else {
317 			length = strlen(fsname);
318 			encode_str = "%llls";
319 		}
320 
321 		flags = FILE_CASE_PRESERVED_NAMES;
322 
323 		if (tree->t_flags & SMB_TREE_UNICODE_ON_DISK)
324 			flags |= FILE_UNICODE_ON_DISK;
325 
326 		if (tree->t_flags & SMB_TREE_SUPPORTS_ACLS)
327 			flags |= FILE_PERSISTENT_ACLS;
328 
329 		if ((tree->t_flags & SMB_TREE_CASEINSENSITIVE) == 0)
330 			flags |= FILE_CASE_SENSITIVE_SEARCH;
331 
332 		if (tree->t_flags & SMB_TREE_STREAMS)
333 			flags |= FILE_NAMED_STREAMS;
334 
335 		if (tree->t_flags & SMB_TREE_QUOTA)
336 			flags |= FILE_VOLUME_QUOTAS;
337 
338 		if (tree->t_flags & SMB_TREE_SPARSE)
339 			flags |= FILE_SUPPORTS_SPARSE_FILES;
340 
341 		(void) smb_mbc_encodef(&xa->rep_data_mb, encode_str, sr,
342 		    flags,
343 		    MAXNAMELEN,	/* max name */
344 		    length,	/* label length */
345 		    fsname);
346 		break;
347 
348 	case SMB_FILE_FS_CONTROL_INFORMATION:
349 		if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) {
350 			smbsr_error(sr, NT_STATUS_NOT_SUPPORTED,
351 			    ERRDOS, ERROR_NOT_SUPPORTED);
352 			return (SDRC_ERROR);
353 		}
354 
355 		(void) smb_mbc_encodef(&xa->rep_data_mb, "qqqqqll",
356 		    0,		/* free space start filtering - MUST be 0 */
357 		    0,		/* free space threshold - MUST be 0 */
358 		    0,		/* free space stop filtering - MUST be 0 */
359 		    SMB_QUOTA_UNLIMITED,	/* default quota threshold */
360 		    SMB_QUOTA_UNLIMITED,	/* default quota limit */
361 		    FILE_VC_QUOTA_ENFORCE,	/* fs control flag */
362 		    0);				/* pad bytes */
363 		break;
364 
365 	case SMB_FILE_FS_FULLSIZE_INFORMATION:
366 		if (smb_fssize(sr, &fssize) != 0)
367 			return (SDRC_ERROR);
368 
369 		(void) smb_mbc_encodef(&xa->rep_data_mb, "qqqll",
370 		    fssize.fs_caller_units,
371 		    fssize.fs_caller_avail,
372 		    fssize.fs_volume_avail,
373 		    fssize.fs_sectors_per_unit,
374 		    fssize.fs_bytes_per_sector);
375 		break;
376 
377 	case SMB_FILE_FS_LABEL_INFORMATION:
378 	case SMB_FILE_FS_OBJECTID_INFORMATION:
379 	case SMB_FILE_FS_DRIVERPATH_INFORMATION:
380 		smbsr_error(sr, NT_STATUS_NOT_SUPPORTED,
381 		    ERRDOS, ERROR_NOT_SUPPORTED);
382 		return (SDRC_ERROR);
383 
384 	default:
385 		smbsr_error(sr, NT_STATUS_INVALID_LEVEL,
386 		    ERRDOS, ERROR_INVALID_LEVEL);
387 		return (SDRC_ERROR);
388 	}
389 
390 	return (SDRC_SUCCESS);
391 }
392 
393 /*
394  * smb_fssize
395  *
396  * File system size information, for the volume and for the user
397  * initiating the request.
398  *
399  * If there's no quota entry for the user initiating the request,
400  * caller_units and caller_avail are the total and available units
401  * for the volume (volume_units, volume_avail).
402  * If there is a quota entry for the user initiating the request,
403  * and it is not SMB_QUOTA_UNLIMITED, calculate caller_units and
404  * caller_avail as follows:
405  *   caller_units = quota limit / bytes_per_unit
406  *   caller_avail = remaining quota / bytes_per_unit
407  *
408  * A quota limit of SMB_QUOTA_UNLIMITED means that the user's quota
409  * is specfied as unlimited. A quota limit of 0 means there is no
410  * quota specified for the user.
411  *
412  * Returns: 0 - success
413  *         -1 - error. Error status set in sr.
414  */
415 static int
416 smb_fssize(smb_request_t *sr, smb_fssize_t *fssize)
417 {
418 	smb_node_t *node;
419 	struct statvfs64 df;
420 	uid_t uid;
421 	smb_quota_t quota;
422 	int rc, bytes_per_unit;
423 
424 	bzero(fssize, sizeof (smb_fssize_t));
425 	node = sr->tid_tree->t_snode;
426 	if ((rc = smb_fsop_statfs(sr->user_cr, node, &df)) != 0) {
427 		smbsr_errno(sr, rc);
428 		return (-1);
429 	}
430 
431 	fssize->fs_bytes_per_sector = 512;
432 	fssize->fs_sectors_per_unit = df.f_frsize >> 9;
433 	if (df.f_bavail > df.f_blocks)
434 		df.f_bavail = 0;
435 
436 	fssize->fs_volume_units = df.f_blocks;
437 	fssize->fs_volume_avail = df.f_bavail;
438 	fssize->fs_caller_units = df.f_blocks;
439 	fssize->fs_caller_avail = df.f_bavail;
440 	bytes_per_unit =
441 	    fssize->fs_bytes_per_sector * fssize->fs_sectors_per_unit;
442 
443 	if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA))
444 		return (0);
445 
446 	uid = crgetuid(sr->uid_user->u_cred);
447 	if (smb_quota_query_user_quota(sr, uid, &quota) != NT_STATUS_SUCCESS)
448 		return (0);
449 
450 	if ((quota.q_limit != SMB_QUOTA_UNLIMITED) && (quota.q_limit != 0)) {
451 		fssize->fs_caller_units = quota.q_limit / bytes_per_unit;
452 		if (quota.q_limit <= quota.q_used)
453 			fssize->fs_caller_avail = 0;
454 		else
455 			fssize->fs_caller_avail =
456 			    (quota.q_limit - quota.q_used) / bytes_per_unit;
457 	}
458 
459 	return (0);
460 }
461 
462 /*
463  * smb_com_trans2_set_fs_information
464  *
465  * This transaction sets filesystem information.
466  * The following information levels are supported:
467  *
468  *  InformationLevel               	Value
469  *  ==================================  ======
470  *  SMB_FILE_FS_CONTROL_INFORMATION	1006
471  */
472 smb_sdrc_t
473 smb_com_trans2_set_fs_information(smb_request_t *sr, smb_xa_t *xa)
474 {
475 	smb_tree_t		*tree;
476 	uint16_t		infolev;
477 
478 	tree = sr->tid_tree;
479 	if (!STYPE_ISDSK(tree->t_res_type)) {
480 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
481 		    ERRDOS, ERROR_ACCESS_DENIED);
482 		return (SDRC_ERROR);
483 	}
484 
485 	if (smb_mbc_decodef(&xa->req_param_mb, "ww",
486 	    &sr->smb_fid, &infolev) != 0)
487 		return (SDRC_ERROR);
488 
489 	switch (infolev) {
490 	case SMB_FILE_FS_CONTROL_INFORMATION:
491 		if (smb_trans2_set_fs_ctrl_info(sr, xa) != 0)
492 			return (SDRC_ERROR);
493 		break;
494 
495 	case SMB_FILE_FS_VOLUME_INFORMATION:
496 	case SMB_FILE_FS_LABEL_INFORMATION:
497 	case SMB_FILE_FS_SIZE_INFORMATION:
498 	case SMB_FILE_FS_DEVICE_INFORMATION:
499 	case SMB_FILE_FS_ATTRIBUTE_INFORMATION:
500 	case SMB_FILE_FS_FULLSIZE_INFORMATION:
501 	case SMB_FILE_FS_OBJECTID_INFORMATION:
502 	case SMB_FILE_FS_DRIVERPATH_INFORMATION:
503 		smbsr_error(sr, NT_STATUS_NOT_SUPPORTED,
504 		    ERRDOS, ERROR_NOT_SUPPORTED);
505 		return (SDRC_ERROR);
506 
507 	default:
508 		smbsr_error(sr, NT_STATUS_INVALID_LEVEL,
509 		    ERRDOS, ERROR_INVALID_LEVEL);
510 		return (SDRC_ERROR);
511 	}
512 
513 	return (SDRC_SUCCESS);
514 }
515 
516 /*
517  * smb_trans2_set_fs_ctrl_info
518  *
519  * Only users with Admin privileges (i.e. of the BUILTIN/Administrators
520  * group) will be allowed to set quotas.
521  *
522  * Currently QUOTAS are always ENFORCED and the default values
523  * are always SMB_QUOTA_UNLIMITED (none). Any attempt to set
524  * values other than these will result in NT_STATUS_NOT_SUPPORTED.
525  */
526 static int
527 smb_trans2_set_fs_ctrl_info(smb_request_t *sr, smb_xa_t *xa)
528 {
529 	int rc;
530 	uint64_t fstart, fthresh, fstop, qthresh, qlimit;
531 	uint32_t qctrl, qpad;
532 
533 	if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) {
534 		smbsr_error(sr, NT_STATUS_NOT_SUPPORTED,
535 		    ERRDOS, ERROR_NOT_SUPPORTED);
536 		return (-1);
537 	}
538 
539 	if (!smb_user_is_admin(sr->uid_user)) {
540 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
541 		    ERRDOS, ERROR_ACCESS_DENIED);
542 		return (-1);
543 	}
544 
545 	rc = smb_mbc_decodef(&xa->req_data_mb, "qqqqqll", &fstart,
546 	    &fthresh, &fstop, &qthresh, &qlimit, &qctrl, &qpad);
547 
548 	if ((rc != 0) || (fstart != 0) || (fthresh != 0) || (fstop != 0)) {
549 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
550 		    ERRDOS, ERROR_INVALID_PARAMETER);
551 		return (-1);
552 	}
553 
554 	/* Only support ENFORCED quotas with UNLIMITED default */
555 	if ((qctrl != FILE_VC_QUOTA_ENFORCE) ||
556 	    (qlimit != SMB_QUOTA_UNLIMITED) ||
557 	    (qthresh != SMB_QUOTA_UNLIMITED)) {
558 		smbsr_error(sr, NT_STATUS_NOT_SUPPORTED,
559 		    ERRDOS, ERROR_NOT_SUPPORTED);
560 		return (-1);
561 	}
562 
563 	return (0);
564 }
565