source: branches/samba-3.2.x/source/smbd/dosmode.c@ 206

Last change on this file since 206 was 204, checked in by Herwig Bauernfeind, 16 years ago

Update 3.2 branch to 3.2.4

File size: 19.3 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3 dos mode handling functions
4 Copyright (C) Andrew Tridgell 1992-1998
5 Copyright (C) James Peach 2006
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19*/
20
21#include "includes.h"
22
23static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
24{
25#if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
26 if (sbuf->st_size > sbuf->st_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
27 return FILE_ATTRIBUTE_SPARSE;
28 }
29#endif
30 return 0;
31}
32
33/****************************************************************************
34 Change a dos mode to a unix mode.
35 Base permission for files:
36 if creating file and inheriting (i.e. parent_dir != NULL)
37 apply read/write bits from parent directory.
38 else
39 everybody gets read bit set
40 dos readonly is represented in unix by removing everyone's write bit
41 dos archive is represented in unix by the user's execute bit
42 dos system is represented in unix by the group's execute bit
43 dos hidden is represented in unix by the other's execute bit
44 if !inheriting {
45 Then apply create mask,
46 then add force bits.
47 }
48 Base permission for directories:
49 dos directory is represented in unix by unix's dir bit and the exec bit
50 if !inheriting {
51 Then apply create mask,
52 then add force bits.
53 }
54****************************************************************************/
55
56mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname,
57 const char *inherit_from_dir)
58{
59 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
60 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
61 * inheriting. */
62
63 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
64 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
65 }
66
67 if (fname && (inherit_from_dir != NULL)
68 && lp_inherit_perms(SNUM(conn))) {
69 SMB_STRUCT_STAT sbuf;
70
71 DEBUG(2, ("unix_mode(%s) inheriting from %s\n", fname,
72 inherit_from_dir));
73 if (SMB_VFS_STAT(conn, inherit_from_dir, &sbuf) != 0) {
74 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n", fname,
75 inherit_from_dir, strerror(errno)));
76 return(0); /* *** shouldn't happen! *** */
77 }
78
79 /* Save for later - but explicitly remove setuid bit for safety. */
80 dir_mode = sbuf.st_mode & ~S_ISUID;
81 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
82 /* Clear "result" */
83 result = 0;
84 }
85
86 if (IS_DOS_DIR(dosmode)) {
87 /* We never make directories read only for the owner as under DOS a user
88 can always create a file in a read-only directory. */
89 result |= (S_IFDIR | S_IWUSR);
90
91 if (dir_mode) {
92 /* Inherit mode of parent directory. */
93 result |= dir_mode;
94 } else {
95 /* Provisionally add all 'x' bits */
96 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
97
98 /* Apply directory mask */
99 result &= lp_dir_mask(SNUM(conn));
100 /* Add in force bits */
101 result |= lp_force_dir_mode(SNUM(conn));
102 }
103 } else {
104 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
105 result |= S_IXUSR;
106
107 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
108 result |= S_IXGRP;
109
110 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
111 result |= S_IXOTH;
112
113 if (dir_mode) {
114 /* Inherit 666 component of parent directory mode */
115 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
116 } else {
117 /* Apply mode mask */
118 result &= lp_create_mask(SNUM(conn));
119 /* Add in force bits */
120 result |= lp_force_create_mode(SNUM(conn));
121 }
122 }
123
124 DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
125 return(result);
126}
127
128/****************************************************************************
129 Change a unix mode to a dos mode.
130****************************************************************************/
131
132static uint32 dos_mode_from_sbuf(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
133{
134 int result = 0;
135 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
136
137 if (ro_opts == MAP_READONLY_YES) {
138 /* Original Samba method - map inverse of user "w" bit. */
139#ifndef __OS2__x
140 if ((sbuf->st_mode & S_IWUSR) == 0) {
141#else
142 if(os2_isattribute(path,aRONLY)==0) {
143#endif
144 result |= aRONLY;
145 }
146 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
147 /* Check actual permissions for read-only. */
148 if (!can_write_to_file(conn, path, sbuf)) {
149 result |= aRONLY;
150 }
151 } /* Else never set the readonly bit. */
152
153#ifndef __OS2__x
154 if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
155#else
156 if(os2_isattribute(path,aARCH)==0)
157#endif
158 result |= aARCH;
159
160#ifndef __OS2__x
161 if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
162#else
163 if(os2_isattribute(path,aSYSTEM)==0)
164#endif
165 result |= aSYSTEM;
166
167#ifndef __OS2__x
168 if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
169#else
170 if(os2_isattribute(path,aHIDDEN)==0)
171#endif
172
173 if (S_ISDIR(sbuf->st_mode))
174 result = aDIR | (result & aRONLY);
175
176 result |= set_sparse_flag(sbuf);
177
178#ifdef S_ISLNK
179#if LINKS_READ_ONLY
180 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
181 result |= aRONLY;
182#endif
183#endif
184
185 DEBUG(8,("dos_mode_from_sbuf returning "));
186
187 if (result & aHIDDEN) DEBUG(8, ("h"));
188 if (result & aRONLY ) DEBUG(8, ("r"));
189 if (result & aSYSTEM) DEBUG(8, ("s"));
190 if (result & aDIR ) DEBUG(8, ("d"));
191 if (result & aARCH ) DEBUG(8, ("a"));
192
193 DEBUG(8,("\n"));
194 return result;
195}
196
197/****************************************************************************
198 Get DOS attributes from an EA.
199****************************************************************************/
200
201static bool get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr)
202{
203 ssize_t sizeret;
204 fstring attrstr;
205 unsigned int dosattr;
206
207 if (!lp_store_dos_attributes(SNUM(conn))) {
208 return False;
209 }
210
211 /* Don't reset pattr to zero as we may already have filename-based attributes we
212 need to preserve. */
213
214 sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
215 if (sizeret == -1) {
216#if defined(ENOTSUP) && defined(ENOATTR)
217 if ((errno != ENOTSUP) && (errno != ENOATTR) && (errno != EACCES) && (errno != EPERM)) {
218 DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
219 path, strerror(errno) ));
220 set_store_dos_attributes(SNUM(conn), False);
221 }
222#endif
223 return False;
224 }
225 /* Null terminate string. */
226 attrstr[sizeret] = 0;
227 DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
228
229 if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
230 sscanf(attrstr, "%x", &dosattr) != 1) {
231 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
232 return False;
233 }
234
235 if (S_ISDIR(sbuf->st_mode)) {
236 dosattr |= aDIR;
237 }
238 *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
239
240 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
241
242 if (dosattr & aHIDDEN) DEBUG(8, ("h"));
243 if (dosattr & aRONLY ) DEBUG(8, ("r"));
244 if (dosattr & aSYSTEM) DEBUG(8, ("s"));
245 if (dosattr & aDIR ) DEBUG(8, ("d"));
246 if (dosattr & aARCH ) DEBUG(8, ("a"));
247
248 DEBUG(8,("\n"));
249
250 return True;
251}
252
253/****************************************************************************
254 Set DOS attributes in an EA.
255****************************************************************************/
256
257static bool set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
258{
259 fstring attrstr;
260 files_struct *fsp = NULL;
261 bool ret = False;
262
263 if (!lp_store_dos_attributes(SNUM(conn))) {
264 return False;
265 }
266
267 snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
268 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
269 if((errno != EPERM) && (errno != EACCES)) {
270 if (errno == ENOSYS
271#if defined(ENOTSUP)
272 || errno == ENOTSUP) {
273#else
274 ) {
275#endif
276 set_store_dos_attributes(SNUM(conn), False);
277 }
278 return False;
279 }
280
281 /* We want DOS semantics, ie allow non owner with write permission to change the
282 bits on a file. Just like file_ntimes below.
283 */
284
285 /* Check if we have write access. */
286 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
287 return False;
288
289 /*
290 * We need to open the file with write access whilst
291 * still in our current user context. This ensures we
292 * are not violating security in doing the setxattr.
293 */
294
295 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,path,sbuf,&fsp)))
296 return ret;
297 become_root();
298 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
299 ret = True;
300 }
301 unbecome_root();
302 close_file_fchmod(fsp);
303 return ret;
304 }
305 DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
306 return True;
307}
308
309/****************************************************************************
310 Change a unix mode to a dos mode for an ms dfs link.
311****************************************************************************/
312
313uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
314{
315 uint32 result = 0;
316
317 DEBUG(8,("dos_mode_msdfs: %s\n", path));
318
319 if (!VALID_STAT(*sbuf)) {
320 return 0;
321 }
322
323 /* First do any modifications that depend on the path name. */
324 /* hide files with a name starting with a . */
325 if (lp_hide_dot_files(SNUM(conn))) {
326 const char *p = strrchr_m(path,'/');
327 if (p) {
328 p++;
329 } else {
330 p = path;
331 }
332
333 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
334 result |= aHIDDEN;
335 }
336 }
337
338 result |= dos_mode_from_sbuf(conn, path, sbuf);
339
340 /* Optimization : Only call is_hidden_path if it's not already
341 hidden. */
342 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
343 result |= aHIDDEN;
344 }
345
346 DEBUG(8,("dos_mode_msdfs returning "));
347
348 if (result & aHIDDEN) DEBUG(8, ("h"));
349 if (result & aRONLY ) DEBUG(8, ("r"));
350 if (result & aSYSTEM) DEBUG(8, ("s"));
351 if (result & aDIR ) DEBUG(8, ("d"));
352 if (result & aARCH ) DEBUG(8, ("a"));
353 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
354
355 DEBUG(8,("\n"));
356
357 return(result);
358}
359
360/****************************************************************************
361 Change a unix mode to a dos mode.
362****************************************************************************/
363
364uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
365{
366 uint32 result = 0;
367 bool offline;
368
369 DEBUG(8,("dos_mode: %s\n", path));
370
371 if (!VALID_STAT(*sbuf)) {
372 return 0;
373 }
374
375 /* First do any modifications that depend on the path name. */
376 /* hide files with a name starting with a . */
377 if (lp_hide_dot_files(SNUM(conn))) {
378 const char *p = strrchr_m(path,'/');
379 if (p) {
380 p++;
381 } else {
382 p = path;
383 }
384
385 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
386 result |= aHIDDEN;
387 }
388 }
389
390 /* Get the DOS attributes from an EA by preference. */
391 if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
392 result |= set_sparse_flag(sbuf);
393 } else {
394 result |= dos_mode_from_sbuf(conn, path, sbuf);
395 }
396
397
398 offline = SMB_VFS_IS_OFFLINE(conn, path, sbuf);
399 if (S_ISREG(sbuf->st_mode) && offline) {
400 result |= FILE_ATTRIBUTE_OFFLINE;
401 }
402
403 /* Optimization : Only call is_hidden_path if it's not already
404 hidden. */
405 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
406 result |= aHIDDEN;
407 }
408
409 DEBUG(8,("dos_mode returning "));
410
411 if (result & aHIDDEN) DEBUG(8, ("h"));
412 if (result & aRONLY ) DEBUG(8, ("r"));
413 if (result & aSYSTEM) DEBUG(8, ("s"));
414 if (result & aDIR ) DEBUG(8, ("d"));
415 if (result & aARCH ) DEBUG(8, ("a"));
416 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
417
418 DEBUG(8,("\n"));
419
420 return(result);
421}
422
423/*******************************************************************
424 chmod a file - but preserve some bits.
425********************************************************************/
426
427int file_set_dosmode(connection_struct *conn, const char *fname,
428 uint32 dosmode, SMB_STRUCT_STAT *st,
429 const char *parent_dir,
430 bool newfile)
431{
432 SMB_STRUCT_STAT st1;
433 int mask=0;
434 mode_t tmp;
435 mode_t unixmode;
436 int ret = -1, lret = -1;
437 uint32_t old_mode;
438
439 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
440 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
441
442 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
443
444 if (st == NULL) {
445 SET_STAT_INVALID(st1);
446 st = &st1;
447 }
448
449 if (!VALID_STAT(*st)) {
450 if (SMB_VFS_STAT(conn,fname,st))
451 return(-1);
452 }
453
454 unixmode = st->st_mode;
455
456 get_acl_group_bits(conn, fname, &st->st_mode);
457
458 if (S_ISDIR(st->st_mode))
459 dosmode |= aDIR;
460 else
461 dosmode &= ~aDIR;
462
463 old_mode = dos_mode(conn,fname,st);
464
465 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
466 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
467 lret = SMB_VFS_SET_OFFLINE(conn, fname);
468 if (lret == -1) {
469 DEBUG(0, ("set_dos_mode: client has asked to set "
470 "FILE_ATTRIBUTE_OFFLINE to %s/%s but there was "
471 "an error while setting it or it is not supported.\n",
472 parent_dir, fname));
473 }
474 }
475 }
476
477 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
478 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
479
480 if (old_mode == dosmode) {
481 st->st_mode = unixmode;
482 return(0);
483 }
484
485 /* Store the DOS attributes in an EA by preference. */
486 if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
487 if (!newfile) {
488 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
489 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
490 }
491 st->st_mode = unixmode;
492 return 0;
493 }
494
495 unixmode = unix_mode(conn,dosmode,fname, parent_dir);
496
497 /* preserve the s bits */
498 mask |= (S_ISUID | S_ISGID);
499
500 /* preserve the t bit */
501#ifdef S_ISVTX
502 mask |= S_ISVTX;
503#endif
504
505 /* possibly preserve the x bits */
506 if (!MAP_ARCHIVE(conn))
507 mask |= S_IXUSR;
508 if (!MAP_SYSTEM(conn))
509 mask |= S_IXGRP;
510 if (!MAP_HIDDEN(conn))
511 mask |= S_IXOTH;
512
513 unixmode |= (st->st_mode & mask);
514
515 /* if we previously had any r bits set then leave them alone */
516 if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
517 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
518 unixmode |= tmp;
519 }
520
521 /* if we previously had any w bits set then leave them alone
522 whilst adding in the new w bits, if the new mode is not rdonly */
523 if (!IS_DOS_READONLY(dosmode)) {
524 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
525 }
526
527 ret = SMB_VFS_CHMOD(conn, fname, unixmode);
528 if (ret == 0) {
529 if(!newfile || (lret != -1)) {
530 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
531 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
532 }
533 st->st_mode = unixmode;
534 return 0;
535 }
536
537 if((errno != EPERM) && (errno != EACCES))
538 return -1;
539
540 if(!lp_dos_filemode(SNUM(conn)))
541 return -1;
542
543 /* We want DOS semantics, ie allow non owner with write permission to change the
544 bits on a file. Just like file_ntimes below.
545 */
546
547 /* Check if we have write access. */
548 if (CAN_WRITE(conn)) {
549 /*
550 * We need to open the file with write access whilst
551 * still in our current user context. This ensures we
552 * are not violating security in doing the fchmod.
553 * This file open does *not* break any oplocks we are
554 * holding. We need to review this.... may need to
555 * break batch oplocks open by others. JRA.
556 */
557 files_struct *fsp;
558 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp)))
559 return -1;
560 become_root();
561 ret = SMB_VFS_FCHMOD(fsp, unixmode);
562 unbecome_root();
563 close_file_fchmod(fsp);
564 if (!newfile) {
565 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
566 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
567 }
568 if (ret == 0) {
569 st->st_mode = unixmode;
570 }
571 }
572
573 return( ret );
574}
575
576/*******************************************************************
577 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
578 than POSIX.
579*******************************************************************/
580
581int file_ntimes(connection_struct *conn, const char *fname, const struct timespec ts[2])
582{
583 SMB_STRUCT_STAT sbuf;
584 int ret = -1;
585
586 errno = 0;
587 ZERO_STRUCT(sbuf);
588
589 DEBUG(6, ("file_ntime: actime: %s",
590 time_to_asc(convert_timespec_to_time_t(ts[0]))));
591 DEBUG(6, ("file_ntime: modtime: %s",
592 time_to_asc(convert_timespec_to_time_t(ts[1]))));
593
594 /* Don't update the time on read-only shares */
595 /* We need this as set_filetime (which can be called on
596 close and other paths) can end up calling this function
597 without the NEED_WRITE protection. Found by :
598 Leo Weppelman <leo@wau.mis.ah.nl>
599 */
600
601 if (!CAN_WRITE(conn)) {
602 return 0;
603 }
604
605 if(SMB_VFS_NTIMES(conn, fname, ts) == 0) {
606 return 0;
607 }
608
609 if((errno != EPERM) && (errno != EACCES)) {
610 return -1;
611 }
612
613 if(!lp_dos_filetimes(SNUM(conn))) {
614 return -1;
615 }
616
617 /* We have permission (given by the Samba admin) to
618 break POSIX semantics and allow a user to change
619 the time on a file they don't own but can write to
620 (as DOS does).
621 */
622
623 /* Check if we have write access. */
624 if (can_write_to_file(conn, fname, &sbuf)) {
625 /* We are allowed to become root and change the filetime. */
626 become_root();
627 ret = SMB_VFS_NTIMES(conn, fname, ts);
628 unbecome_root();
629 }
630
631 return ret;
632}
633
634/******************************************************************
635 Force a "sticky" write time on a pathname. This will always be
636 returned on all future write time queries and set on close.
637******************************************************************/
638
639bool set_sticky_write_time_path(connection_struct *conn, const char *fname,
640 struct file_id fileid, const struct timespec mtime)
641{
642 if (null_timespec(mtime)) {
643 return true;
644 }
645
646 if (!set_sticky_write_time(fileid, mtime)) {
647 return false;
648 }
649
650 return true;
651}
652
653/******************************************************************
654 Force a "sticky" write time on an fsp. This will always be
655 returned on all future write time queries and set on close.
656******************************************************************/
657
658bool set_sticky_write_time_fsp(struct files_struct *fsp, const struct timespec mtime)
659{
660 fsp->write_time_forced = true;
661 TALLOC_FREE(fsp->update_write_time_event);
662
663 return set_sticky_write_time_path(fsp->conn, fsp->fsp_name,
664 fsp->file_id, mtime);
665}
666
667/******************************************************************
668 Update a write time immediately, without the 2 second delay.
669******************************************************************/
670
671bool update_write_time(struct files_struct *fsp)
672{
673 if (!set_write_time(fsp->file_id, timespec_current())) {
674 return false;
675 }
676
677 notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
678 FILE_NOTIFY_CHANGE_LAST_WRITE, fsp->fsp_name);
679
680 return true;
681}
Note: See TracBrowser for help on using the repository browser.