/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Lan Manager (SMB/CIFS) share interface implementation. This interface * returns Win32 error codes, usually network error values (lmerr.h). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SMB_SHARE_HTAB_SZ 1024 #define SMB_SHARE_PUBLISH 0 #define SMB_SHARE_UNPUBLISH 1 static HT_HANDLE *smb_shr_handle = NULL; static rwlock_t smb_shr_lock; static pthread_t smb_shr_cache_populatethr; static uint32_t smb_shr_cache_create(void); static void *smb_shr_cache_populate(void *); static int smb_shr_del_shmgr(smb_share_t *); static int smb_shr_set_shmgr(smb_share_t *); static uint32_t smb_shr_set_refcnt(char *, int); static void smb_shr_set_oemname(smb_share_t *); typedef struct smb_shr_adinfo { TAILQ_ENTRY(smb_shr_adinfo) next; char name[MAXNAMELEN]; char container[MAXPATHLEN]; char flag; } smb_shr_adinfo_t; typedef struct smb_shr_adqueue { int nentries; TAILQ_HEAD(adqueue, smb_shr_adinfo) adlist; } smb_shr_adqueue_t; static smb_shr_adqueue_t ad_queue; static int publish_on = 0; static pthread_t smb_shr_publish_thr; static mutex_t smb_shr_publish_mtx = PTHREAD_MUTEX_INITIALIZER; static cond_t smb_shr_publish_cv = DEFAULTCV; static void *smb_shr_publisher(void *); static void smb_shr_stop_publish(void); static void smb_shr_publish(smb_share_t *, char, int); /* * Start loading lmshare information from sharemanager * and create the cache. */ int smb_shr_start(void) { int rc; rc = pthread_create(&smb_shr_publish_thr, NULL, smb_shr_publisher, 0); if (rc != 0) { syslog(LOG_ERR, "Failed to start publisher thread, " "share publishing is disabled"); } rc = pthread_create(&smb_shr_cache_populatethr, NULL, smb_shr_cache_populate, 0); if (rc != 0) { syslog(LOG_ERR, "Failed to start share loading, " "existing shares will not be available"); } return (rc); } void smb_shr_stop(void) { smb_shr_stop_publish(); } /* * lmshare_load_shares * * Helper function for smb_shr_cache_populate. It attempts to load the shares * contained in the group. */ static void lmshare_load_shares(sa_group_t group) { sa_share_t share; sa_resource_t resource; smb_share_t si; char *path, *rname; /* Don't bother if "smb" isn't set on the group */ if (sa_get_optionset(group, SMB_PROTOCOL_NAME) == NULL) return; for (share = sa_get_share(group, NULL); share != NULL; share = sa_get_next_share(share)) { path = sa_get_share_attr(share, "path"); if (path == NULL) { continue; } for (resource = sa_get_share_resource(share, NULL); resource != NULL; resource = sa_get_next_resource(resource)) { rname = sa_get_resource_attr(resource, "name"); if (rname == NULL) { syslog(LOG_ERR, "Invalid share " "resource for path: %s", path); continue; } smb_build_lmshare_info(rname, path, resource, &si); sa_free_attr_string(rname); if (smb_shr_add(&si, 0) != NERR_Success) { syslog(LOG_ERR, "Failed to load " "share %s", si.shr_name); } } /* We are done with all shares for same path */ sa_free_attr_string(path); } } /* * smb_shr_cache_populate * * Load shares from sharemanager. The args argument is currently not * used. The function walks through all the groups in libshare and * calls lmshare_load_shares for each group found. It also looks for * sub-groups and calls lmshare_load_shares for each sub-group found. */ /*ARGSUSED*/ static void * smb_shr_cache_populate(void *args) { sa_handle_t handle; sa_group_t group, subgroup; char *gstate; if (smb_shr_cache_create() != NERR_Success) { syslog(LOG_ERR, "Failed to create share hash table"); return (NULL); } handle = sa_init(SA_INIT_SHARE_API); if (handle == NULL) { syslog(LOG_ERR, "Failed to load share " "information: no libshare handle"); return (NULL); } for (group = sa_get_group(handle, NULL); group != NULL; group = sa_get_next_group(group)) { gstate = sa_get_group_attr(group, "state"); if (gstate == NULL) continue; if (strcasecmp(gstate, "disabled") == 0) { /* Skip disabled or unknown state group */ sa_free_attr_string(gstate); continue; } sa_free_attr_string(gstate); /* * Attempt to load the shares. lmshare_load_shares * will check to see if the protocol is enabled or * not. We then want to check for any sub-groups on * this group. This is needed in the ZFS case where * the top level ZFS group won't have "smb" protocol * enabled but the sub-groups will. */ lmshare_load_shares(group); for (subgroup = sa_get_sub_group(group); subgroup != NULL; subgroup = sa_get_next_group(subgroup)) { lmshare_load_shares(subgroup); } } sa_fini(handle); return (NULL); } /* * lmshare_callback * * Call back to free share structures stored * in shares' hash table. */ static void lmshare_callback(HT_ITEM *item) { if (item && item->hi_data) free(item->hi_data); } /* * smb_shr_cache_create * * Create the share hash table. */ static uint32_t smb_shr_cache_create(void) { if (smb_shr_handle == NULL) { (void) rwlock_init(&smb_shr_lock, USYNC_THREAD, 0); (void) rw_wrlock(&smb_shr_lock); smb_shr_handle = ht_create_table(SMB_SHARE_HTAB_SZ, MAXNAMELEN, 0); if (smb_shr_handle == NULL) { syslog(LOG_ERR, "smb_shr_cache_create:" " unable to create share table"); (void) rw_unlock(&smb_shr_lock); return (NERR_InternalError); } (void) ht_register_callback(smb_shr_handle, lmshare_callback); (void) rw_unlock(&smb_shr_lock); } return (NERR_Success); } /* * smb_shr_add_adminshare * * add the admin share for the volume when the share database * for that volume is going to be loaded. */ uint32_t smb_shr_add_adminshare(char *volname, unsigned char drive) { smb_share_t si; uint32_t rc; if (drive == 0) return (NERR_InvalidDevice); bzero(&si, sizeof (smb_share_t)); (void) strcpy(si.shr_path, volname); si.shr_flags = SMB_SHRF_TRANS | SMB_SHRF_ADMIN; (void) snprintf(si.shr_name, sizeof (si.shr_name), "%c$", drive); rc = smb_shr_add(&si, 0); return (rc); } /* * smb_shr_count * * Return the total number of shares, which should be the same value * that would be returned from a share enum request. */ int smb_shr_count(void) { int n_shares; n_shares = ht_get_total_items(smb_shr_handle); /* If we don't store IPC$ in hash table we should do this */ n_shares++; return (n_shares); } /* * smb_shr_iterinit * * Initialize an iterator for traversing hash table. * 'mode' is used for filtering shares when iterating. */ void smb_shr_iterinit(smb_shriter_t *shi, uint32_t mode) { bzero(shi, sizeof (smb_shriter_t)); shi->si_mode = mode; } /* * smb_shr_iterate * * Iterate on the shares in the hash table. The iterator must be initialized * before the first iteration. On subsequent calls, the iterator must be * passed unchanged. * * Returns NULL on failure or when all shares are visited, otherwise * returns information of visited share. * * Note that there are some special shares, i.e. IPC$, that must also * be processed. */ smb_share_t * smb_shr_iterate(smb_shriter_t *shi) { HT_ITEM *item; smb_share_t *si; if (smb_shr_handle == NULL || shi == NULL) return (NULL); if (shi->si_counter == 0) { /* * IPC$ is always first. */ (void) strcpy(shi->si_share.shr_name, "IPC$"); smb_shr_set_oemname(&shi->si_share); shi->si_share.shr_flags = SMB_SHRF_TRANS; shi->si_share.shr_type = (int)(STYPE_IPC | STYPE_SPECIAL); shi->si_counter = 1; return (&(shi->si_share)); } if (shi->si_counter == 1) { if ((item = ht_findfirst( smb_shr_handle, &shi->si_hashiter)) == NULL) { return (NULL); } si = (smb_share_t *)(item->hi_data); ++shi->si_counter; if (si->shr_flags & shi->si_mode) { bcopy(si, &(shi->si_share), sizeof (smb_share_t)); return (&(shi->si_share)); } } while ((item = ht_findnext(&shi->si_hashiter)) != NULL) { si = (smb_share_t *)(item->hi_data); ++shi->si_counter; if (si->shr_flags & shi->si_mode) { bcopy(si, &(shi->si_share), sizeof (smb_share_t)); return (&(shi->si_share)); } } return (NULL); } /* * smb_shr_add * * Add a share. This is a wrapper round smb_shr_set that checks * whether or not the share already exists. If the share exists, an * error is returned. * * Don't check smb_shr_is_dir here: it causes rootfs to recurse. */ uint32_t smb_shr_add(smb_share_t *si, int doshm) { uint32_t status = NERR_Success; if (si == 0 || smb_shr_is_valid(si->shr_name) == 0) return (NERR_InvalidDevice); (void) utf8_strlwr(si->shr_name); if (smb_shr_exists(si->shr_name)) { /* * Only autohome shares can be added multiple times */ if ((si->shr_flags & SMB_SHRF_AUTOHOME) == 0) return (NERR_DuplicateShare); } if (si->shr_refcnt == 0) { status = smb_shr_set(si, doshm); smb_shr_publish(si, SMB_SHARE_PUBLISH, 1); } if ((si->shr_flags & SMB_SHRF_AUTOHOME) && (status == NERR_Success)) { si->shr_refcnt++; status = smb_shr_set_refcnt(si->shr_name, si->shr_refcnt); } if (status) return (status); return (smb_dwncall_share(SMB_SHROP_ADD, si->shr_path, si->shr_name)); } /* * smb_shr_del * * Remove a share. Ensure that all SMB trees associated with this share * are disconnected. If the share does not exist, an error is returned. */ uint32_t smb_shr_del(char *share_name, int doshm) { smb_share_t *si; HT_ITEM *item; uint32_t status; char path[MAXPATHLEN]; if (share_name) (void) utf8_strlwr(share_name); if (smb_shr_is_valid(share_name) == 0 || smb_shr_exists(share_name) == 0) { return (NERR_NetNameNotFound); } (void) rw_wrlock(&smb_shr_lock); item = ht_find_item(smb_shr_handle, share_name); if (item == NULL) { (void) rw_unlock(&smb_shr_lock); return (NERR_ItemNotFound); } si = (smb_share_t *)item->hi_data; if (si == NULL) { (void) rw_unlock(&smb_shr_lock); return (NERR_InternalError); } if ((si->shr_flags & SMB_SHRF_AUTOHOME) == SMB_SHRF_AUTOHOME) { si->shr_refcnt--; if (si->shr_refcnt > 0) { status = smb_shr_set_refcnt(si->shr_name, si->shr_refcnt); (void) rw_unlock(&smb_shr_lock); return (status); } } if (doshm && (smb_shr_del_shmgr(si) != 0)) { (void) rw_unlock(&smb_shr_lock); return (NERR_InternalError); } smb_shr_publish(si, SMB_SHARE_UNPUBLISH, 1); /* * Copy the path before the entry is removed from the hash table */ (void) strlcpy(path, si->shr_path, MAXPATHLEN); /* Delete from hash table */ (void) ht_remove_item(smb_shr_handle, share_name); (void) rw_unlock(&smb_shr_lock); return (smb_dwncall_share(SMB_SHROP_DELETE, path, share_name)); } /* * smb_shr_set_refcnt * * sets the autohome shr_refcnt for a share */ static uint32_t smb_shr_set_refcnt(char *share_name, int refcnt) { smb_share_t *si; HT_ITEM *item; if (share_name) { (void) utf8_strlwr(share_name); } (void) rw_wrlock(&smb_shr_lock); item = ht_find_item(smb_shr_handle, share_name); if (item == NULL) { (void) rw_unlock(&smb_shr_lock); return (NERR_ItemNotFound); } si = (smb_share_t *)item->hi_data; if (si == NULL) { (void) rw_unlock(&smb_shr_lock); return (NERR_InternalError); } si->shr_refcnt = refcnt; (void) rw_unlock(&smb_shr_lock); return (NERR_Success); } /* * smb_shr_ren * * Rename a share. Check that the current name exists and the new name * doesn't exist. The rename is performed by deleting the current share * definition and creating a new share with the new name. */ uint32_t smb_shr_ren(char *from_name, char *to_name, int doshm) { smb_share_t si; uint32_t nerr; if (smb_shr_is_valid(from_name) == 0 || smb_shr_is_valid(to_name) == 0) return (NERR_InvalidDevice); (void) utf8_strlwr(from_name); (void) utf8_strlwr(to_name); if (smb_shr_exists(from_name) == 0) return (NERR_NetNameNotFound); if (smb_shr_exists(to_name)) return (NERR_DuplicateShare); if ((nerr = smb_shr_get(from_name, &si)) != NERR_Success) return (nerr); if ((nerr = smb_shr_del(from_name, doshm)) != NERR_Success) return (nerr); (void) strlcpy(si.shr_name, to_name, MAXNAMELEN); return (smb_shr_add(&si, 1)); } /* * smb_shr_exists * * Returns 1 if the share exists. Otherwise returns 0. */ int smb_shr_exists(char *share_name) { if (share_name == 0 || *share_name == 0) return (0); if (ht_find_item(smb_shr_handle, share_name) == NULL) return (0); else return (1); } /* * smb_shr_is_special * * Simple check to determine if share name represents a special share, * i.e. the last character of the name is a '$'. Returns STYPE_SPECIAL * if the name is special. Otherwise returns 0. */ int smb_shr_is_special(char *share_name) { int len; if (share_name == 0) return (0); if ((len = strlen(share_name)) == 0) return (0); if (share_name[len - 1] == '$') return (STYPE_SPECIAL); else return (0); } /* * smb_shr_is_restricted * * Check whether or not there is a restriction on a share. Restricted * shares are generally STYPE_SPECIAL, for example, IPC$. All the * administration share names are restricted: C$, D$ etc. Returns 1 * if the share is restricted. Otherwise 0 is returned to indicate * that there are no restrictions. */ int smb_shr_is_restricted(char *share_name) { static char *restricted[] = { "IPC$" }; int i; for (i = 0; i < sizeof (restricted)/sizeof (restricted[0]); i++) { if (strcasecmp(restricted[i], share_name) == 0) return (1); } if (smb_shr_is_admin(share_name)) return (1); return (0); } /* * smb_shr_is_admin * * Check whether or not access to the share should be restricted to * administrators. This is a bit of a hack because what we're doing * is checking for the default admin shares: C$, D$ etc.. There are * other shares that have restrictions: see smb_shr_is_restricted(). * * Returns 1 if the shares is an admin share. Otherwise 0 is returned * to indicate that there are no restrictions. */ int smb_shr_is_admin(char *share_name) { if (share_name == 0) return (0); if (strlen(share_name) == 2 && mts_isalpha(share_name[0]) && share_name[1] == '$') { return (1); } return (0); } /* * smb_shr_is_valid * * Check if any invalid char is present in share name. According to * MSDN article #236388: "Err Msg: The Share Name Contains Invalid * Characters", the list of invalid character is: * * " / \ [ ] : | < > + ; , ? * = * * Also rejects if control characters are embedded. * * If the sharename is valid, return (1). Otherwise return (0). */ int smb_shr_is_valid(char *share_name) { char *invalid = "\"/\\[]:|<>+;,?*="; char *cp; if (share_name == 0) return (0); if (strpbrk(share_name, invalid)) return (0); for (cp = share_name; *cp != '\0'; cp++) if (iscntrl(*cp)) return (0); return (1); } /* * smb_shr_is_dir * * Check to determine if a share object represents a directory. * * Returns 1 if the path leads to a directory. Otherwise returns 0. */ int smb_shr_is_dir(char *path) { struct stat stat_info; if (stat(path, &stat_info) == 0) if (S_ISDIR(stat_info.st_mode)) return (1); return (0); } /* * smb_shr_get * * Load the information for the specified share into the supplied share * info structure. If the shared directory does not begin with a /, one * will be inserted as a prefix. */ uint32_t smb_shr_get(char *share_name, smb_share_t *si) { int i, endidx; int dirlen; HT_ITEM *item; (void) rw_rdlock(&smb_shr_lock); (void) utf8_strlwr(share_name); if ((item = ht_find_item(smb_shr_handle, share_name)) == NULL) { bzero(si, sizeof (smb_share_t)); (void) rw_unlock(&smb_shr_lock); return (NERR_NetNameNotFound); } (void) memcpy(si, item->hi_data, sizeof (smb_share_t)); (void) rw_unlock(&smb_shr_lock); if (si->shr_path[0] == '\0') return (NERR_NetNameNotFound); if (si->shr_path[0] != '/') { dirlen = strlen(si->shr_path) + 1; endidx = (dirlen < MAXPATHLEN-1) ? dirlen : MAXPATHLEN - 2; for (i = endidx; i >= 0; i--) si->shr_path[i+1] = si->shr_path[i]; si->shr_path[MAXPATHLEN-1] = '\0'; si->shr_path[0] = '/'; } return (NERR_Success); } /* * Remove share from sharemanager repository. */ static int smb_shr_del_shmgr(smb_share_t *si) { sa_handle_t handle; sa_share_t share; sa_resource_t resource; handle = sa_init(SA_INIT_SHARE_API); if (handle == NULL) { syslog(LOG_ERR, "Failed to get handle to " "share lib"); return (1); } share = sa_find_share(handle, si->shr_path); if (share == NULL) { syslog(LOG_ERR, "Failed to get share to delete"); sa_fini(handle); return (1); } resource = sa_get_share_resource(share, si->shr_name); if (resource == NULL) { syslog(LOG_ERR, "Failed to get share resource to delete"); sa_fini(handle); return (1); } if (sa_remove_resource(resource) != SA_OK) { syslog(LOG_ERR, "Failed to remove resource"); sa_fini(handle); return (1); } sa_fini(handle); return (0); } static int smb_shr_set_shmgr(smb_share_t *si) { sa_handle_t handle; sa_share_t share; sa_group_t group; sa_resource_t resource; int share_created = 0; int err; /* Add share to sharemanager */ handle = sa_init(SA_INIT_SHARE_API); if (handle == NULL) { syslog(LOG_ERR, "Failed to get handle to share lib"); return (1); } share = sa_find_share(handle, si->shr_path); if (share == NULL) { group = smb_get_smb_share_group(handle); if (group == NULL) { sa_fini(handle); return (1); } share = sa_add_share(group, si->shr_path, 0, &err); if (share == NULL) { sa_fini(handle); return (1); } share_created = 1; } resource = sa_get_share_resource(share, si->shr_name); if (resource == NULL) { resource = sa_add_resource(share, si->shr_name, SA_SHARE_PERMANENT, &err); if (resource == NULL) { goto failure; } } if (sa_set_resource_attr(resource, "description", si->shr_cmnt) != SA_OK) { syslog(LOG_ERR, "Falied to set resource " "description in sharemgr"); goto failure; } if (sa_set_resource_attr(resource, SMB_SHROPT_AD_CONTAINER, si->shr_container) != SA_OK) { syslog(LOG_ERR, "Falied to set ad-container in sharemgr"); goto failure; } sa_fini(handle); return (0); failure: if (share_created && (share != NULL)) { if (sa_remove_share(share) != SA_OK) { syslog(LOG_ERR, "Failed to cleanup share"); } } if (resource != NULL) { if (sa_remove_resource(resource) != SA_OK) { syslog(LOG_ERR, "Failed to cleanup share resource"); } } sa_fini(handle); return (1); } /* * smb_shr_cache_delshare * * Delete the given share only from hash table */ static uint32_t smb_shr_cache_delshare(char *share_name) { if (share_name == 0) return (NERR_NetNameNotFound); (void) utf8_strlwr(share_name); if (smb_shr_is_valid(share_name) == 0 || smb_shr_exists(share_name) == 0) { return (NERR_NetNameNotFound); } (void) rw_wrlock(&smb_shr_lock); (void) ht_remove_item(smb_shr_handle, share_name); (void) rw_unlock(&smb_shr_lock); return (NERR_Success); } /* * smb_shr_set * * Adds the specified share into the system hash table * and also store its info in the corresponding disk * structure if it is not a temporary (SMB_SHRF_TRANS) share. * when the first share is going to be added, create shares * hash table if it is not already created. * If the share already exists, it will be replaced. If the * new share directory name does not begin with a /, one will be * inserted as a prefix. */ uint32_t smb_shr_set(smb_share_t *si, int doshm) { int i, endidx; int dirlen; smb_share_t *add_si; int res = NERR_Success; smb_share_t old_si; if (si->shr_path[0] != '/') { dirlen = strlen(si->shr_path) + 1; endidx = (dirlen < MAXPATHLEN - 1) ? dirlen : MAXPATHLEN - 2; for (i = endidx; i >= 0; i--) si->shr_path[i+1] = si->shr_path[i]; si->shr_path[MAXPATHLEN-1] = '\0'; si->shr_path[0] = '/'; } /* XXX Do we need to translate the directory here? to real path */ if (smb_shr_is_dir(si->shr_path) == 0) return (NERR_UnknownDevDir); /* * We should allocate memory for new entry because we * don't know anything about the passed pointer i.e. * it maybe destroyed by caller of this function while * we only store a pointer to the data in hash table. * Hash table doesn't do any allocation for the data that * is being added. */ add_si = malloc(sizeof (smb_share_t)); if (add_si == NULL) { syslog(LOG_ERR, "LmshareSetinfo: resource shortage"); return (NERR_NoRoom); } (void) memcpy(add_si, si, sizeof (smb_share_t)); /* * If we can't find it, use the new one to get things in sync, * but if there is an existing one, that is the one to * unpublish. */ if (smb_shr_get(si->shr_name, &old_si) != NERR_Success) (void) memcpy(&old_si, si, sizeof (smb_share_t)); if (doshm) { res = smb_shr_del(si->shr_name, doshm); if (res != NERR_Success) { free(add_si); syslog(LOG_ERR, "LmshareSetinfo: delete failed", res); return (res); } } else { /* Unpublish old share from AD */ if ((add_si->shr_flags & SMB_SHRF_PERM) == SMB_SHRF_PERM) smb_shr_publish(&old_si, SMB_SHARE_UNPUBLISH, 1); (void) smb_shr_cache_delshare(si->shr_name); } smb_shr_set_oemname(add_si); /* if it's not transient it should be permanent */ if ((add_si->shr_flags & SMB_SHRF_TRANS) == 0) add_si->shr_flags |= SMB_SHRF_PERM; add_si->shr_type = STYPE_DISKTREE; add_si->shr_type |= smb_shr_is_special(add_si->shr_name); (void) rw_wrlock(&smb_shr_lock); if (ht_add_item(smb_shr_handle, add_si->shr_name, add_si) == NULL) { syslog(LOG_ERR, "smb_shr_set[%s]: error in adding share", add_si->shr_name); (void) rw_unlock(&smb_shr_lock); free(add_si); return (NERR_InternalError); } (void) rw_unlock(&smb_shr_lock); if ((add_si->shr_flags & SMB_SHRF_PERM) == SMB_SHRF_PERM) { if (doshm && (smb_shr_set_shmgr(add_si) != 0)) { syslog(LOG_ERR, "Update share %s in sharemgr failed", add_si->shr_name); return (NERR_InternalError); } smb_shr_publish(add_si, SMB_SHARE_PUBLISH, 1); } return (res); } void smb_shr_list(int offset, smb_shrlist_t *list) { smb_shriter_t iterator; smb_share_t *si; int list_idx = 0; int i = 0; bzero(list, sizeof (smb_shrlist_t)); smb_shr_iterinit(&iterator, SMB_SHRF_ALL); (void) smb_shr_iterate(&iterator); /* To skip IPC$ */ while ((si = smb_shr_iterate(&iterator)) != NULL) { if (smb_shr_is_special(si->shr_name)) { /* * Don't return restricted shares. */ if (smb_shr_is_restricted(si->shr_name)) continue; } if (i++ < offset) continue; (void) memcpy(&list->smbshr[list_idx], si, sizeof (smb_share_t)); if (++list_idx == LMSHARES_PER_REQUEST) break; } list->no = list_idx; } /* * Put the share on publish queue. */ static void smb_shr_publish(smb_share_t *si, char flag, int poke) { smb_shr_adinfo_t *item = NULL; if (publish_on == 0) return; if ((si == NULL) || (si->shr_container[0] == '\0')) return; (void) mutex_lock(&smb_shr_publish_mtx); item = (smb_shr_adinfo_t *)malloc(sizeof (smb_shr_adinfo_t)); if (item == NULL) { syslog(LOG_ERR, "Failed to allocate share publish item"); (void) mutex_unlock(&smb_shr_publish_mtx); return; } item->flag = flag; (void) strlcpy(item->name, si->shr_name, sizeof (item->name)); (void) strlcpy(item->container, si->shr_container, sizeof (item->container)); /*LINTED - E_CONSTANT_CONDITION*/ TAILQ_INSERT_TAIL(&ad_queue.adlist, item, next); ad_queue.nentries++; if (poke) (void) cond_signal(&smb_shr_publish_cv); (void) mutex_unlock(&smb_shr_publish_mtx); } void smb_shr_stop_publish() { (void) mutex_lock(&smb_shr_publish_mtx); publish_on = 0; (void) cond_signal(&smb_shr_publish_cv); (void) mutex_unlock(&smb_shr_publish_mtx); } /* * This functions waits to be signaled and once running * will publish/unpublish any items on list. * smb_shr_stop_publish when called will exit this thread. */ /*ARGSUSED*/ static void * smb_shr_publisher(void *arg) { smb_ads_handle_t *ah; smb_shr_adinfo_t *item; char hostname[MAXHOSTNAMELEN]; char name[MAXNAMELEN]; char container[MAXPATHLEN]; char flag; /*LINTED - E_CONSTANT_CONDITION*/ TAILQ_INIT(&ad_queue.adlist); ad_queue.nentries = 0; publish_on = 1; hostname[0] = '\0'; for (;;) { (void) cond_wait(&smb_shr_publish_cv, &smb_shr_publish_mtx); if (hostname[0] == '\0') { if (smb_gethostname(hostname, MAXHOSTNAMELEN, 0) != 0) continue; } if (publish_on == 0) { syslog(LOG_DEBUG, "lmshare: publisher exit"); if (ad_queue.nentries == 0) { (void) mutex_unlock(&smb_shr_publish_mtx); break; } for (item = TAILQ_FIRST(&ad_queue.adlist); item; item = TAILQ_FIRST(&ad_queue.adlist)) { /*LINTED - E_CONSTANT_CONDITION*/ TAILQ_REMOVE(&ad_queue.adlist, item, next); (void) free(item); } ad_queue.nentries = 0; (void) mutex_unlock(&smb_shr_publish_mtx); break; } if (ad_queue.nentries == 0) continue; ah = smb_ads_open(); if (ah == NULL) { /* We mostly have no AD config so just clear the list */ for (item = TAILQ_FIRST(&ad_queue.adlist); item; item = TAILQ_FIRST(&ad_queue.adlist)) { /*LINTED - E_CONSTANT_CONDITION*/ TAILQ_REMOVE(&ad_queue.adlist, item, next); (void) free(item); } ad_queue.nentries = 0; continue; } TAILQ_FOREACH(item, &ad_queue.adlist, next) { (void) strlcpy(name, item->name, sizeof (name)); (void) strlcpy(container, item->container, sizeof (container)); flag = item->flag; if (flag == SMB_SHARE_UNPUBLISH) (void) smb_ads_remove_share(ah, name, NULL, container, hostname); else (void) smb_ads_publish_share(ah, name, NULL, container, hostname); } for (item = TAILQ_FIRST(&ad_queue.adlist); item; item = TAILQ_FIRST(&ad_queue.adlist)) { /*LINTED - E_CONSTANT_CONDITION*/ TAILQ_REMOVE(&ad_queue.adlist, item, next); (void) free(item); } ad_queue.nentries = 0; if (ah != NULL) { smb_ads_close(ah); ah = NULL; } } syslog(LOG_DEBUG, "lmshare: Stopping publisher"); return (NULL); } /* * smb_shr_get_realpath * * Derive the real path of a share from the path provided by a * Windows client application during the share addition. * * For instance, the real path of C:\ is /cvol and the * real path of F:\home is /vol1/home. * * clipath - path provided by the Windows client is in the * format of :\ * realpath - path that will be stored as the directory field of * the smb_share_t structure of the share. * maxlen - maximum length fo the realpath buffer * * Return LAN Manager network error code. */ /*ARGSUSED*/ uint32_t smb_shr_get_realpath(const char *clipath, char *realpath, int maxlen) { /* XXX do this translation */ return (NERR_Success); } /* * smb_shr_set_oemname * * Generates the OEM name of the given share. If it's * shorter than 13 chars it'll be saved in si->shr_oemname. * Otherwise si->shr_oemname will be empty and SMB_SHRF_LONGNAME * will be set in si->shr_flags. */ static void smb_shr_set_oemname(smb_share_t *si) { unsigned int cpid = oem_get_smb_cpid(); mts_wchar_t *unibuf; char *oem_name; int length; length = strlen(si->shr_name) + 1; oem_name = malloc(length); unibuf = malloc(length * sizeof (mts_wchar_t)); if ((oem_name == NULL) || (unibuf == NULL)) { free(oem_name); free(unibuf); return; } (void) mts_mbstowcs(unibuf, si->shr_name, length); if (unicodestooems(oem_name, unibuf, length, cpid) == 0) (void) strcpy(oem_name, si->shr_name); free(unibuf); if (strlen(oem_name) + 1 > SMB_SHARE_OEMNAME_MAX) { si->shr_flags |= SMB_SHRF_LONGNAME; *si->shr_oemname = '\0'; } else { si->shr_flags &= ~SMB_SHRF_LONGNAME; (void) strlcpy(si->shr_oemname, oem_name, SMB_SHARE_OEMNAME_MAX); } free(oem_name); }