2 Unix SMB/CIFS implementation.
3 service (connection) opening and closing
4 Copyright (C) Andrew Tridgell 1992-1998
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 extern struct timeval smb_last_time;
24 extern int case_default;
25 extern BOOL case_preserve;
26 extern BOOL short_case_preserve;
27 extern BOOL case_mangle;
28 extern BOOL case_sensitive;
29 extern BOOL use_mangled_map;
30 extern userdom_struct current_user_info;
33 /****************************************************************************
34 Load parameters specific to a connection/service.
35 ****************************************************************************/
37 BOOL set_current_service(connection_struct *conn,BOOL do_chdir)
39 extern char magic_char;
40 static connection_struct *last_conn;
48 conn->lastused = smb_last_time.tv_sec;
53 vfs_ChDir(conn,conn->connectpath) != 0 &&
54 vfs_ChDir(conn,conn->origpath) != 0) {
55 DEBUG(0,("chdir (%s) failed\n",
60 if (conn == last_conn)
65 case_default = lp_defaultcase(snum);
66 case_preserve = lp_preservecase(snum);
67 short_case_preserve = lp_shortpreservecase(snum);
68 case_mangle = lp_casemangle(snum);
69 case_sensitive = lp_casesensitive(snum);
70 magic_char = lp_magicchar(snum);
71 use_mangled_map = (*lp_mangled_map(snum) ? True:False);
75 /****************************************************************************
76 Add a home service. Returns the new service number or -1 if fail.
77 ****************************************************************************/
79 int add_home_service(const char *service, const char *username, const char *homedir)
83 if (!service || !homedir)
86 if ((iHomeService = lp_servicenumber(HOMES_NAME)) < 0)
90 * If this is a winbindd provided username, remove
91 * the domain component before adding the service.
92 * Log a warning if the "path=" parameter does not
97 const char *p = strchr(service,*lp_winbind_separator());
99 /* We only want the 'user' part of the string */
105 if (!lp_add_home(service, iHomeService, username, homedir)) {
109 return lp_servicenumber(service);
115 * Find a service entry. service is always in dos codepage.
117 * @param service is modified (to canonical form??)
119 int find_service(fstring service)
123 all_string_sub(service,"\\","/",0);
125 iService = lp_servicenumber(service);
127 /* now handle the special case of a home directory */
130 char *phome_dir = get_user_home_dir(service);
135 * Try mapping the servicename, it may
136 * be a Windows to unix mapped user name.
138 if(map_username(service))
139 phome_dir = get_user_home_dir(service);
142 DEBUG(3,("checking for home directory %s gave %s\n",service,
143 phome_dir?phome_dir:"(NULL)"));
145 iService = add_home_service(service,service /* 'username' */, phome_dir);
148 /* If we still don't have a service, attempt to add it as a printer. */
153 if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) >= 0)
157 DEBUG(3,("checking whether %s is a valid printer name...\n", service));
158 pszTemp = lp_printcapname();
159 if ((pszTemp != NULL) && pcap_printername_ok(service, pszTemp))
161 DEBUG(3,("%s is a valid printer name\n", service));
162 DEBUG(3,("adding %s as a printer service\n", service));
163 lp_add_printer(service, iPrinterService);
164 iService = lp_servicenumber(service);
166 DEBUG(0,("failed to add %s as a printer service!\n", service));
169 DEBUG(3,("%s is not a valid printer name\n", service));
173 /* Check for default vfs service? Unsure whether to implement this */
178 /* just possibly it's a default service? */
181 char *pdefservice = lp_defaultservice();
182 if (pdefservice && *pdefservice &&
183 !strequal(pdefservice,service) &&
184 !strstr(service,".."))
187 * We need to do a local copy here as lp_defaultservice()
188 * returns one of the rotating lp_string buffers that
189 * could get overwritten by the recursive find_service() call
190 * below. Fix from Josef Hinteregger <joehtg@joehtg.co.at>.
193 pstrcpy(defservice, pdefservice);
194 iService = find_service(defservice);
197 all_string_sub(service, "_","/",0);
198 iService = lp_add_service(service, iService);
204 if (!VALID_SNUM(iService))
206 DEBUG(0,("Invalid snum %d for %s\n",iService, service));
211 DEBUG(3,("find_service() failed to find service %s\n", service));
217 /****************************************************************************
218 do some basic sainity checks on the share.
219 This function modifies dev, ecode.
220 ****************************************************************************/
221 static NTSTATUS share_sanity_checks(int snum, fstring dev)
224 if (!lp_snum_ok(snum) ||
225 !check_access(smbd_server_fd(),
226 lp_hostsallow(snum), lp_hostsdeny(snum))) {
227 return NT_STATUS_ACCESS_DENIED;
230 if (dev[0] == '?' || !dev[0]) {
231 if (lp_print_ok(snum)) {
232 fstrcpy(dev,"LPT1:");
233 } else if (strequal(lp_fstype(snum), "IPC")) {
242 if (lp_print_ok(snum)) {
243 if (!strequal(dev, "LPT1:")) {
244 return NT_STATUS_BAD_DEVICE_TYPE;
246 } else if (strequal(lp_fstype(snum), "IPC")) {
247 if (!strequal(dev, "IPC")) {
248 return NT_STATUS_BAD_DEVICE_TYPE;
250 } else if (!strequal(dev, "A:")) {
251 return NT_STATUS_BAD_DEVICE_TYPE;
254 /* Behave as a printer if we are supposed to */
255 if (lp_print_ok(snum) && (strcmp(dev, "A:") == 0)) {
256 fstrcpy(dev, "LPT1:");
263 /****************************************************************************
265 ****************************************************************************/
266 static void set_read_only(connection_struct *conn, gid_t *groups, size_t n_groups)
269 char *service = lp_servicename(conn->service);
270 conn->read_only = lp_readonly(conn->service);
272 if (!service) return;
274 str_list_copy(&list, lp_readlist(conn->service));
276 if (!str_list_substitute(list, "%S", service)) {
277 DEBUG(0, ("ERROR: read list substitution failed\n"));
279 if (user_in_list(conn->user, (const char **)list, groups, n_groups))
280 conn->read_only = True;
281 str_list_free(&list);
284 str_list_copy(&list, lp_writelist(conn->service));
286 if (!str_list_substitute(list, "%S", service)) {
287 DEBUG(0, ("ERROR: write list substitution failed\n"));
289 if (user_in_list(conn->user, (const char **)list, groups, n_groups))
290 conn->read_only = False;
291 str_list_free(&list);
296 /****************************************************************************
298 ****************************************************************************/
299 static void set_admin_user(connection_struct *conn, gid_t *groups, size_t n_groups)
301 /* admin user check */
303 /* JRA - original code denied admin user if the share was
304 marked read_only. Changed as I don't think this is needed,
305 but old code left in case there is a problem here.
307 if (user_in_list(conn->user,lp_admin_users(conn->service), groups, n_groups)
312 conn->admin_user = True;
313 conn->force_user = True; /* Admin users are effectivly 'forced' */
314 DEBUG(0,("%s logged in as admin user (root privileges)\n",conn->user));
316 conn->admin_user = False;
319 #if 0 /* This done later, for now */
320 /* admin users always run as uid=0 */
321 if (conn->admin_user) {
327 /****************************************************************************
328 Make a connection, given the snum to connect to, and the vuser of the
329 connecting user if appropriate.
330 ****************************************************************************/
332 static connection_struct *make_connection_snum(int snum, user_struct *vuser,
334 const char *pdev, NTSTATUS *status)
336 struct passwd *pass = NULL;
338 connection_struct *conn;
346 if (NT_STATUS_IS_ERR(*status = share_sanity_checks(snum, dev))) {
352 DEBUG(0,("Couldn't find free connection.\n"));
353 *status = NT_STATUS_INSUFFICIENT_RESOURCES;
357 if (lp_guest_only(snum)) {
358 const char *guestname = lp_guestaccount();
360 pass = getpwnam_alloc(guestname);
362 DEBUG(0,("authorise_login: Invalid guest account %s??\n",guestname));
364 *status = NT_STATUS_NO_SUCH_USER;
367 fstrcpy(user,pass->pw_name);
368 conn->force_user = True;
369 string_set(&conn->user,pass->pw_name);
371 DEBUG(3,("Guest only user %s\n",user));
374 if (!lp_guest_ok(snum)) {
375 DEBUG(2, ("guest user (from session setup) not permitted to access this share (%s)\n", lp_servicename(snum)));
377 *status = NT_STATUS_ACCESS_DENIED;
381 if (!user_ok(vuser->user.unix_name, snum, vuser->groups, vuser->n_groups)) {
382 DEBUG(2, ("user '%s' (from session setup) not permitted to access this share (%s)\n", vuser->user.unix_name, lp_servicename(snum)));
384 *status = NT_STATUS_ACCESS_DENIED;
388 conn->vuid = vuser->vuid;
389 conn->uid = vuser->uid;
390 conn->gid = vuser->gid;
391 string_set(&conn->user,vuser->user.unix_name);
392 fstrcpy(user,vuser->user.unix_name);
393 guest = vuser->guest;
394 } else if (lp_security() == SEC_SHARE) {
395 /* add it as a possible user name if we
396 are in share mode security */
397 add_session_user(lp_servicename(snum));
398 /* shall we let them in? */
399 if (!authorise_login(snum,user,password,&guest)) {
400 DEBUG( 2, ( "Invalid username/password for [%s]\n",
401 lp_servicename(snum)) );
403 *status = NT_STATUS_WRONG_PASSWORD;
406 pass = Get_Pwnam(user);
407 conn->force_user = True;
408 conn->uid = pass->pw_uid;
409 conn->gid = pass->pw_gid;
410 string_set(&conn->user, pass->pw_name);
411 fstrcpy(user, pass->pw_name);
414 DEBUG(0, ("invalid VUID (vuser) but not in security=share\n"));
416 *status = NT_STATUS_ACCESS_DENIED;
420 add_session_user(user);
422 safe_strcpy(conn->client_address, client_addr(),
423 sizeof(conn->client_address)-1);
424 conn->num_files_open = 0;
425 conn->lastused = time(NULL);
426 conn->service = snum;
428 conn->printer = (strncmp(dev,"LPT",3) == 0);
429 conn->ipc = ((strncmp(dev,"IPC",3) == 0) || strequal(dev,"ADMIN$"));
431 conn->veto_list = NULL;
432 conn->hide_list = NULL;
433 conn->veto_oplock_list = NULL;
434 string_set(&conn->dirpath,"");
435 string_set(&conn->user,user);
436 conn->nt_user_token = NULL;
438 set_read_only(conn, vuser ? vuser->groups : NULL, vuser ? vuser->n_groups : 0);
440 set_admin_user(conn, vuser ? vuser->groups : NULL, vuser ? vuser->n_groups : 0);
443 * If force user is true, then store the
444 * given userid and also the groups
445 * of the user we're forcing.
448 if (*lp_force_user(snum)) {
449 struct passwd *pass2;
451 pstrcpy(fuser,lp_force_user(snum));
453 /* Allow %S to be used by force user. */
454 pstring_sub(fuser,"%S",lp_servicename(snum));
456 pass2 = (struct passwd *)Get_Pwnam(fuser);
458 conn->uid = pass2->pw_uid;
459 conn->gid = pass2->pw_gid;
460 string_set(&conn->user,pass2->pw_name);
461 fstrcpy(user,pass2->pw_name);
462 conn->force_user = True;
463 DEBUG(3,("Forced user %s\n",user));
465 DEBUG(1,("Couldn't find user %s\n",fuser));
467 *status = NT_STATUS_NO_SUCH_USER;
472 /* admin users always run as uid=0 */
473 if (conn->admin_user) {
479 * If force group is true, then override
480 * any groupid stored for the connecting user.
483 if (*lp_force_group(snum)) {
487 BOOL user_must_be_member = False;
489 pstrcpy(tmp_gname,lp_force_group(snum));
491 if (tmp_gname[0] == '+') {
492 user_must_be_member = True;
493 /* even now, tmp_gname is null terminated */
494 pstrcpy(gname,&tmp_gname[1]);
496 pstrcpy(gname,tmp_gname);
498 /* default service may be a group name */
499 pstring_sub(gname,"%S",lp_servicename(snum));
500 gid = nametogid(gname);
502 if (gid != (gid_t)-1) {
505 * If the user has been forced and the forced group starts
506 * with a '+', then we only set the group to be the forced
507 * group if the forced user is a member of that group.
508 * Otherwise, the meaning of the '+' would be ignored.
510 if (conn->force_user && user_must_be_member) {
511 if (user_in_group_list( user, gname, NULL, 0)) {
513 DEBUG(3,("Forced group %s for member %s\n",gname,user));
517 DEBUG(3,("Forced group %s\n",gname));
519 conn->force_group = True;
521 DEBUG(1,("Couldn't find group %s\n",gname));
523 *status = NT_STATUS_NO_SUCH_GROUP;
527 #endif /* HAVE_GETGRNAM */
531 pstrcpy(s,lp_pathname(snum));
532 standard_sub_conn(conn,s,sizeof(s));
533 string_set(&conn->connectpath,s);
534 DEBUG(3,("Connect path is '%s' for service [%s]\n",s, lp_servicename(snum)));
537 if (conn->force_user || conn->force_group) {
539 /* groups stuff added by ih */
543 /* Find all the groups this uid is in and
544 store them. Used by change_to_user() */
545 initialise_groups(conn->user, conn->uid, conn->gid);
546 get_current_groups(conn->gid, &conn->ngroups,&conn->groups);
548 conn->nt_user_token = create_nt_token(conn->uid, conn->gid,
549 conn->ngroups, conn->groups,
554 * New code to check if there's a share security descripter
555 * added from NT server manager. This is done after the
556 * smb.conf checks are done as we need a uid and token. JRA.
561 BOOL can_write = share_access_check(conn, snum, vuser, FILE_WRITE_DATA);
564 if (!share_access_check(conn, snum, vuser, FILE_READ_DATA)) {
565 /* No access, read or write. */
566 DEBUG(0,( "make_connection: connection to %s denied due to security descriptor.\n",
567 lp_servicename(snum)));
569 *status = NT_STATUS_ACCESS_DENIED;
572 conn->read_only = True;
576 /* Initialise VFS function pointers */
578 if (!smbd_vfs_init(conn)) {
579 DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(SNUM(conn))));
581 *status = NT_STATUS_BAD_NETWORK_NAME;
585 /* ROOT Activities: */
586 /* check number of connections */
587 if (!claim_connection(conn,
588 lp_servicename(SNUM(conn)),
589 lp_max_connections(SNUM(conn)),
591 DEBUG(1,("too many connections - rejected\n"));
593 *status = NT_STATUS_INSUFFICIENT_RESOURCES;
597 /* Preexecs are done here as they might make the dir we are to ChDir to below */
598 /* execute any "root preexec = " line */
599 if (*lp_rootpreexec(SNUM(conn))) {
602 pstrcpy(cmd,lp_rootpreexec(SNUM(conn)));
603 standard_sub_conn(conn,cmd,sizeof(cmd));
604 DEBUG(5,("cmd=%s\n",cmd));
605 ret = smbrun(cmd,NULL);
606 if (ret != 0 && lp_rootpreexec_close(SNUM(conn))) {
607 DEBUG(1,("root preexec gave %d - failing connection\n", ret));
608 yield_connection(conn, lp_servicename(SNUM(conn)));
610 *status = NT_STATUS_ACCESS_DENIED;
615 /* USER Activites: */
616 if (!change_to_user(conn, conn->vuid)) {
617 /* No point continuing if they fail the basic checks */
618 DEBUG(0,("Can't become connected user!\n"));
620 *status = NT_STATUS_LOGON_FAILURE;
624 /* Remember that a different vuid can connect later without these checks... */
626 /* Preexecs are done here as they might make the dir we are to ChDir to below */
627 /* execute any "preexec = " line */
628 if (*lp_preexec(SNUM(conn))) {
631 pstrcpy(cmd,lp_preexec(SNUM(conn)));
632 standard_sub_conn(conn,cmd,sizeof(cmd));
633 ret = smbrun(cmd,NULL);
634 if (ret != 0 && lp_preexec_close(SNUM(conn))) {
635 DEBUG(1,("preexec gave %d - failing connection\n", ret));
636 change_to_root_user();
637 yield_connection(conn, lp_servicename(SNUM(conn)));
639 *status = NT_STATUS_ACCESS_DENIED;
644 #if CHECK_PATH_ON_TCONX
645 /* win2000 does not check the permissions on the directory
646 during the tree connect, instead relying on permission
647 check during individual operations. To match this behaviour
648 I have disabled this chdir check (tridge) */
649 if (vfs_ChDir(conn,conn->connectpath) != 0) {
650 DEBUG(0,("%s (%s) Can't change directory to %s (%s)\n",
651 get_remote_machine_name(), conn->client_address,
652 conn->connectpath,strerror(errno)));
653 change_to_root_user();
654 yield_connection(conn, lp_servicename(SNUM(conn)));
656 *status = NT_STATUS_BAD_NETWORK_NAME;
660 /* the alternative is just to check the directory exists */
661 if (stat(conn->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) {
662 DEBUG(0,("'%s' is not a directory, when connecting to [%s]\n", conn->connectpath, lp_servicename(SNUM(conn))));
663 change_to_root_user();
664 yield_connection(conn, lp_servicename(SNUM(conn)));
666 *status = NT_STATUS_BAD_NETWORK_NAME;
671 string_set(&conn->origpath,conn->connectpath);
673 #if SOFTLINK_OPTIMISATION
674 /* resolve any soft links early if possible */
675 if (vfs_ChDir(conn,conn->connectpath) == 0) {
677 pstrcpy(s,conn->connectpath);
679 string_set(&conn->connectpath,s);
680 vfs_ChDir(conn,conn->connectpath);
685 * Print out the 'connected as' stuff here as we need
686 * to know the effective uid and gid we will be using
687 * (at least initially).
690 if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) {
691 dbgtext( "%s (%s) ", get_remote_machine_name(), conn->client_address );
692 dbgtext( "connect to service %s ", lp_servicename(SNUM(conn)) );
693 dbgtext( "initially as user %s ", user );
694 dbgtext( "(uid=%d, gid=%d) ", (int)geteuid(), (int)getegid() );
695 dbgtext( "(pid %d)\n", (int)sys_getpid() );
698 /* Add veto/hide lists */
699 if (!IS_IPC(conn) && !IS_PRINT(conn)) {
700 set_namearray( &conn->veto_list, lp_veto_files(SNUM(conn)));
701 set_namearray( &conn->hide_list, lp_hide_files(SNUM(conn)));
702 set_namearray( &conn->veto_oplock_list, lp_veto_oplocks(SNUM(conn)));
705 /* Invoke VFS make connection hook */
707 if (conn->vfs_ops.connect) {
708 if (conn->vfs_ops.connect(conn, lp_servicename(snum), user) < 0) {
709 DEBUG(0,("make_connection: VFS make connection failed!\n"));
710 change_to_root_user();
712 *status = NT_STATUS_UNSUCCESSFUL;
717 /* we've finished with the user stuff - go back to root */
718 change_to_root_user();
723 /***************************************************************************************
724 Simple wrapper function for make_connection() to include a call to
726 **************************************************************************************/
728 connection_struct *make_connection_with_chdir(const char *service_in, DATA_BLOB password,
729 const char *dev, uint16 vuid, NTSTATUS *status)
731 connection_struct *conn = NULL;
733 conn = make_connection(service_in, password, dev, vuid, status);
736 * make_connection() does not change the directory for us any more
737 * so we have to do it as a separate step --jerry
740 if ( conn && vfs_ChDir(conn,conn->connectpath) != 0 ) {
741 DEBUG(0,("move_driver_to_download_area: Can't change directory to %s for [print$] (%s)\n",
742 conn->connectpath,strerror(errno)));
743 yield_connection(conn, lp_servicename(SNUM(conn)));
745 *status = NT_STATUS_UNSUCCESSFUL;
752 /****************************************************************************
753 Make a connection to a service.
756 ****************************************************************************/
758 connection_struct *make_connection(const char *service_in, DATA_BLOB password,
759 const char *pdev, uint16 vuid, NTSTATUS *status)
762 user_struct *vuser = NULL;
769 /* This must ONLY BE CALLED AS ROOT. As it exits this function as root. */
770 if (!non_root_mode() && (euid = geteuid()) != 0) {
771 DEBUG(0,("make_connection: PANIC ERROR. Called as nonroot (%u)\n", (unsigned int)euid ));
772 smb_panic("make_connection: PANIC ERROR. Called as nonroot\n");
775 if(lp_security() != SEC_SHARE) {
776 vuser = get_valid_user_struct(vuid);
778 DEBUG(1,("make_connection: refusing to connect with no session setup\n"));
779 *status = NT_STATUS_ACCESS_DENIED;
784 /* Logic to try and connect to the correct [homes] share, preferably without too many
785 getpwnam() lookups. This is particulary nasty for winbind usernames, where the
786 share name isn't the same as unix username.
788 The snum of the homes share is stored on the vuser at session setup time.
791 if (strequal(service_in,HOMES_NAME)) {
792 if(lp_security() != SEC_SHARE) {
793 DATA_BLOB no_pw = data_blob(NULL, 0);
794 if (vuser->homes_snum == -1) {
795 DEBUG(2, ("[homes] share not available for this user because it was not found or created at session setup time\n"));
796 *status = NT_STATUS_BAD_NETWORK_NAME;
799 DEBUG(5, ("making a connection to [homes] service created at session setup time\n"));
800 return make_connection_snum(vuser->homes_snum,
804 /* Security = share. Try with current_user_info.smb_name
805 * as the username. */
806 if (*current_user_info.smb_name) {
807 fstring unix_username;
808 fstrcpy(unix_username,
809 current_user_info.smb_name);
810 map_username(unix_username);
811 snum = find_service(unix_username);
814 DEBUG(5, ("making a connection to 'homes' service %s based on security=share\n", service_in));
815 return make_connection_snum(snum, NULL,
820 } else if ((lp_security() != SEC_SHARE) && (vuser->homes_snum != -1)
821 && strequal(service_in, lp_servicename(vuser->homes_snum))) {
822 DATA_BLOB no_pw = data_blob(NULL, 0);
823 DEBUG(5, ("making a connection to 'homes' service [%s] created at session setup time\n", service_in));
824 return make_connection_snum(vuser->homes_snum,
829 fstrcpy(service, service_in);
833 snum = find_service(service);
836 if (strequal(service,"IPC$") || strequal(service,"ADMIN$")) {
837 DEBUG(3,("refusing IPC connection to %s\n", service));
838 *status = NT_STATUS_ACCESS_DENIED;
842 DEBUG(0,("%s (%s) couldn't find service %s\n",
843 get_remote_machine_name(), client_addr(), service));
844 *status = NT_STATUS_BAD_NETWORK_NAME;
848 /* Handle non-Dfs clients attempting connections to msdfs proxy */
849 if (lp_host_msdfs() && (*lp_msdfs_proxy(snum) != '\0')) {
850 DEBUG(3, ("refusing connection to dfs proxy '%s'\n", service));
851 *status = NT_STATUS_BAD_NETWORK_NAME;
855 DEBUG(5, ("making a connection to 'normal' service %s\n", service));
857 return make_connection_snum(snum, vuser,
862 /****************************************************************************
864 ****************************************************************************/
865 void close_cnum(connection_struct *conn, uint16 vuid)
867 DirCacheFlush(SNUM(conn));
869 change_to_root_user();
871 DEBUG(IS_IPC(conn)?3:1, ("%s (%s) closed connection to service %s\n",
872 get_remote_machine_name(),conn->client_address,
873 lp_servicename(SNUM(conn))));
875 if (conn->vfs_ops.disconnect != NULL) {
877 /* Call VFS disconnect hook */
879 conn->vfs_ops.disconnect(conn);
883 yield_connection(conn, lp_servicename(SNUM(conn)));
885 file_close_conn(conn);
886 dptr_closecnum(conn);
888 /* execute any "postexec = " line */
889 if (*lp_postexec(SNUM(conn)) &&
890 change_to_user(conn, vuid)) {
892 pstrcpy(cmd,lp_postexec(SNUM(conn)));
893 standard_sub_conn(conn,cmd,sizeof(cmd));
895 change_to_root_user();
898 change_to_root_user();
899 /* execute any "root postexec = " line */
900 if (*lp_rootpostexec(SNUM(conn))) {
902 pstrcpy(cmd,lp_rootpostexec(SNUM(conn)));
903 standard_sub_conn(conn,cmd,sizeof(cmd));
907 /* make sure we leave the directory available for unmount */
908 vfs_ChDir(conn, "/");