Esempio n. 1
0
handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce) {
#ifdef HAVE_FAM_H
	fam_dir_entry *fam_dir = NULL;
	int dir_ndx = -1;
	splay_tree *dir_node = NULL;
#endif
	stat_cache_entry *sce = NULL;
	stat_cache *sc;
	struct stat st;
	size_t k;
	int fd;
	struct stat lst;
#ifdef DEBUG_STAT_CACHE
	size_t i;
#endif

	int file_ndx;
	splay_tree *file_node = NULL;

	*ret_sce = NULL;

	/*
	 * check if the directory for this file has changed
	 */
	sc = srv->stat_cache;

	buffer_copy_string_buffer(sc->hash_key, name);
	buffer_append_long(sc->hash_key, con->conf.follow_symlink);

	file_ndx = hashme(sc->hash_key);
	sc->files = splaytree_splay(sc->files, file_ndx);

#ifdef DEBUG_STAT_CACHE
	for (i = 0; i < ctrl.used; i++) {
		if (ctrl.ptr[i] == file_ndx) break;
	}
#endif

	if (sc->files && (sc->files->key == file_ndx)) {
#ifdef DEBUG_STAT_CACHE
		/* it was in the cache */
		assert(i < ctrl.used);
#endif

		/* we have seen this file already and
		 * don't stat() it again in the same second */

		file_node = sc->files;

		sce = file_node->data;

		/* check if the name is the same, we might have a collision */

		if (buffer_is_equal(name, sce->name)) {
			if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) {
				if (sce->stat_ts == srv->cur_ts) {
					*ret_sce = sce;
					return HANDLER_GO_ON;
				}
			}
		} else {
			/* oops, a collision,
			 *
			 * file_node is used by the FAM check below to see if we know this file
			 * and if we can save a stat().
			 *
			 * BUT, the sce is not reset here as the entry into the cache is ok, we
			 * it is just not pointing to our requested file.
			 *
			 *  */

			file_node = NULL;
		}
	} else {
#ifdef DEBUG_STAT_CACHE
		if (i != ctrl.used) {
			log_error_write(srv, __FILE__, __LINE__, "xSB",
				file_ndx, "was already inserted but not found in cache, ", name);
		}
		assert(i == ctrl.used);
#endif
	}

#ifdef HAVE_FAM_H
	/* dir-check */
	if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) {
		if (0 != buffer_copy_dirname(sc->dir_name, name)) {
			log_error_write(srv, __FILE__, __LINE__, "sb",
				"no '/' found in filename:", name);
			return HANDLER_ERROR;
		}

		buffer_copy_string_buffer(sc->hash_key, sc->dir_name);
		buffer_append_long(sc->hash_key, con->conf.follow_symlink);

		dir_ndx = hashme(sc->hash_key);

		sc->dirs = splaytree_splay(sc->dirs, dir_ndx);

		if (sc->dirs && (sc->dirs->key == dir_ndx)) {
			dir_node = sc->dirs;
		}

		if (dir_node && file_node) {
			/* we found a file */

			sce = file_node->data;
			fam_dir = dir_node->data;

			if (fam_dir->version == sce->dir_version) {
				/* the stat()-cache entry is still ok */
				*ret_sce = sce;
				return HANDLER_GO_ON;
			}
		}
	}
#endif

	/*
	 * *lol*
	 * - open() + fstat() on a named-pipe results in a (intended) hang.
	 * - stat() if regular file + open() to see if we can read from it is better
	 *
	 * */
	 int r;
	
#ifdef HAVE_LIBSMBCLIENT
	if(con->mode == DIRECT) 
		r =  stat(name->ptr, &st);
	else if(con->mode == SMB_NTLM||con->mode == SMB_BASIC)
		r = smbc_wrapper_stat(con, name->ptr, &st);
#else	
	r =  stat(name->ptr, &st);
#endif

	if (r == -1) {
		return HANDLER_ERROR;
	}


	if (S_ISREG(st.st_mode)) {
		/* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */
		if (name->ptr[name->used-2] == '/') {
			errno = ENOTDIR;
			return HANDLER_ERROR;
		}

		/* try to open the file to check if we can read it */
#ifdef HAVE_LIBSMBCLIENT
		if(con->mode == DIRECT) {
			fd = open(name->ptr, O_RDONLY);
			if(fd == -1) {
				return HANDLER_ERROR;
			}
			close(fd);
		}
		else if(con->mode == SMB_NTLM) {
			smb_file_t *fp;
			fp = smbc_cli_ntcreate(con->smb_info->cli, name->ptr, 
				FILE_READ_DATA | FILE_WRITE_DATA, FILE_OPEN, 0);
			if(fp == NULL) {
				return HANDLER_ERROR;
			}
			smbc_cli_close(con->smb_info->cli, fp);
		}
		else {
			//Cdbg(DBE, "call smbc_open..%s", con->smb_info->url.path->ptr);			
			//Cdbg(DBE, "call smbc_open..%s", name->ptr);			

			//fd = smbc_open(con->smb_info->url.path->ptr, O_RDONLY, 0);
			fd = smbc_open(name->ptr, O_RDONLY, 0);
			
			if(fd == -1) {
				Cdbg(DBE, "fail to smbc_open");
				return HANDLER_ERROR;
			}
			//Cdbg(DBE, "call smbc_close");			
			smbc_close(fd);
		}
#else
		if (-1 == (fd = open(name->ptr, O_RDONLY))) {
			return HANDLER_ERROR;
		}
		close(fd);
#endif		
	}

	if (NULL == sce) {
#ifdef DEBUG_STAT_CACHE
		int osize = splaytree_size(sc->files);
#endif

		sce = stat_cache_entry_init();
		buffer_copy_string_buffer(sce->name, name);

		sc->files = splaytree_insert(sc->files, file_ndx, sce);
#ifdef DEBUG_STAT_CACHE
		if (ctrl.size == 0) {
			ctrl.size = 16;
			ctrl.used = 0;
			ctrl.ptr = malloc(ctrl.size * sizeof(*ctrl.ptr));
		} else if (ctrl.size == ctrl.used) {
			ctrl.size += 16;
			ctrl.ptr = realloc(ctrl.ptr, ctrl.size * sizeof(*ctrl.ptr));
		}

		ctrl.ptr[ctrl.used++] = file_ndx;

		assert(sc->files);
		assert(sc->files->data == sce);
		assert(osize + 1 == splaytree_size(sc->files));
#endif
	}

	sce->st = st;
	sce->stat_ts = srv->cur_ts;

	/* catch the obvious symlinks
	 *
	 * this is not a secure check as we still have a race-condition between
	 * the stat() and the open. We can only solve this by
	 * 1. open() the file
	 * 2. fstat() the fd
	 *
	 * and keeping the file open for the rest of the time. But this can
	 * only be done at network level.
	 *
	 * per default it is not a symlink
	 * */
#ifdef HAVE_LSTAT
	sce->is_symlink = 0;

	/* we want to only check for symlinks if we should block symlinks.
	 */
	if (!con->conf.follow_symlink) {
		if (stat_cache_lstat(srv, name, &lst)  == 0) {
#ifdef DEBUG_STAT_CACHE
				log_error_write(srv, __FILE__, __LINE__, "sb",
						"found symlink", name);
#endif
				sce->is_symlink = 1;
		}

		/*
		 * we assume "/" can not be symlink, so
		 * skip the symlink stuff if our path is /
		 **/
		else if ((name->used > 2)) {
			buffer *dname;
			char *s_cur;

			dname = buffer_init();
			buffer_copy_string_buffer(dname, name);

			while ((s_cur = strrchr(dname->ptr,'/'))) {
				*s_cur = '\0';
				dname->used = s_cur - dname->ptr + 1;
				if (dname->ptr == s_cur) {
#ifdef DEBUG_STAT_CACHE
					log_error_write(srv, __FILE__, __LINE__, "s", "reached /");
#endif
					break;
				}
#ifdef DEBUG_STAT_CACHE
				log_error_write(srv, __FILE__, __LINE__, "sbs",
						"checking if", dname, "is a symlink");
#endif
				if (stat_cache_lstat(srv, dname, &lst)  == 0) {
					sce->is_symlink = 1;
#ifdef DEBUG_STAT_CACHE
					log_error_write(srv, __FILE__, __LINE__, "sb",
							"found symlink", dname);
#endif
					break;
				};
			};
			buffer_free(dname);
		};
	};
#endif

	if (S_ISREG(st.st_mode)) {
		/* determine mimetype */
		buffer_reset(sce->content_type);
#ifdef HAVE_XATTR
		if (con->conf.use_xattr) {
			stat_cache_attr_get(sce->content_type, name->ptr);
		}
#endif
		/* xattr did not set a content-type. ask the config */
		if (buffer_is_empty(sce->content_type)) {
			for (k = 0; k < con->conf.mimetypes->used; k++) {
				data_string *ds = (data_string *)con->conf.mimetypes->data[k];
				buffer *type = ds->key;

				if (type->used == 0) continue;

				/* check if the right side is the same */
				if (type->used > name->used) continue;

				if (0 == strncasecmp(name->ptr + name->used - type->used, type->ptr, type->used - 1)) {
					buffer_copy_string_buffer(sce->content_type, ds->value);
					break;
				}
			}
		}
		etag_create(sce->etag, &(sce->st), con->etag_flags);
	} else if (S_ISDIR(st.st_mode)) {
		etag_create(sce->etag, &(sce->st), con->etag_flags);
	}

#ifdef HAVE_FAM_H
	if (sc->fam &&
	    (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM)) {
		/* is this directory already registered ? */
		if (!dir_node) {
			fam_dir = fam_dir_entry_init();
			fam_dir->fc = sc->fam;

			buffer_copy_string_buffer(fam_dir->name, sc->dir_name);

			fam_dir->version = 1;

			fam_dir->req = calloc(1, sizeof(FAMRequest));

			if (0 != FAMMonitorDirectory(sc->fam, fam_dir->name->ptr,
						     fam_dir->req, fam_dir)) {

				log_error_write(srv, __FILE__, __LINE__, "sbsbs",
						"monitoring dir failed:",
						fam_dir->name, 
						"file:", name,
						FamErrlist[FAMErrno]);

				fam_dir_entry_free(fam_dir);
			} else {
				int osize = 0;

			       	if (sc->dirs) {
					osize = sc->dirs->size;
				}

				sc->dirs = splaytree_insert(sc->dirs, dir_ndx, fam_dir);
				assert(sc->dirs);
				assert(sc->dirs->data == fam_dir);
				assert(osize == (sc->dirs->size - 1));
			}
		} else {
			fam_dir = dir_node->data;
		}

		/* bind the fam_fc to the stat() cache entry */

		if (fam_dir) {
			sce->dir_version = fam_dir->version;
			sce->dir_ndx     = dir_ndx;
		}
	}
#endif

	*ret_sce = sce;

	return HANDLER_GO_ON;
}
Esempio n. 2
0
int network_open_file_chunk(server *srv, connection *con, chunkqueue *cq) {
	chunk* const c = cq->first;
	off_t file_size, offset, toSend;

	force_assert(NULL != c);
	force_assert(FILE_CHUNK == c->type || SMB_CHUNK == c->type);
	force_assert(c->offset >= 0 && c->offset <= c->file.length);

	//Cdbg(1,"0 c->file.start=%lld, c->offset=%lld, c->file.length=%lld", c->file.start, c->offset, c->file.length);
	offset = c->file.start + c->offset;
	toSend = c->file.length - c->offset;

	if (-1 == c->file.fd) {
		stat_cache_entry *sce = NULL;

		if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) {
			log_error_write(srv, __FILE__, __LINE__, "ssb", "stat-cache failed:", strerror(errno), c->file.name);
			return -1;
		}
		
		if( c->type == SMB_CHUNK ){
			if (-1 == (c->file.fd = smbc_wrapper_open(con,c->file.name->ptr, O_RDONLY, 0755))) {
				log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));
				return -1;
			}
		}
		else{
			if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY|O_NOCTTY))) {
				log_error_write(srv, __FILE__, __LINE__, "ssb", "open failed:", strerror(errno), c->file.name);
				return -1;
			}

			fd_close_on_exec(c->file.fd);
		}

		file_size = sce->st.st_size;
	} 
	else {	
		struct stat st;

		if( c->type == SMB_CHUNK ){
			if (-1 == smbc_wrapper_stat(con, c->file.name->ptr, &st)) {
				log_error_write(srv, __FILE__, __LINE__, "ss", "smbc_wrapper_stat failed:", strerror(errno));
				return -1;
			}
		}
		else{
			if (-1 == fstat(c->file.fd, &st)) {
				log_error_write(srv, __FILE__, __LINE__, "ss", "fstat failed:", strerror(errno));
				return -1;
			}
		}
		
		file_size = st.st_size;
	}
	//Cdbg(1,"1 file_size=%d, toSend=%d, offset=%d", file_size, toSend, offset);
	
	if (offset > file_size || toSend > file_size || offset > file_size - toSend) {
		//Cdbg(1,"2 file_size=%d", file_size);
		log_error_write(srv, __FILE__, __LINE__, "sb", "file was shrinked:", c->file.name);
		return -1;
	}

	return 0;
}