Example #1
0
/*
 * smb_receive
 * fs points to the correct segment
 */
static int
smb_receive(struct smb_sb_info *server)
{
	struct socket *socket = server_sock(server);
	unsigned char * packet = server->packet;
	int len, result;
	unsigned char peek_buf[4];

	result = smb_get_length(socket, peek_buf);
	if (result < 0)
		goto out;
	len = result;
	/*
	 * Some servers do not respect our max_xmit and send
	 * larger packets.  Try to allocate a new packet,
	 * but don't free the old one unless we succeed.
	 */
	if (len + 4 > server->packet_size)
	{
		int new_len = smb_round_length(len + 4);

		result = -ENOMEM;
		packet = smb_vmalloc(new_len);
		if (packet == NULL)
			goto out;
		smb_vfree(server->packet);
		server->packet = packet;
		server->packet_size = new_len;
	}
	memcpy(packet, peek_buf, 4);
	result = smb_receive_raw(socket, packet + 4, len);
	if (result < 0)
	{
#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_receive: receive error: %d\n", result);
#endif
		goto out;
	}
	server->rcls = *(packet + smb_rcls);
	server->err  = WVAL(packet, smb_err);

#ifdef SMBFS_DEBUG_VERBOSE
if (server->rcls != 0)
printk("smb_receive: rcls=%d, err=%d\n", server->rcls, server->err);
#endif
out:
	return result;
}
Example #2
0
static int
smb_readdir(struct inode *dir, struct file *filp,
	    void *dirent, filldir_t filldir)
{
	int result, i = 0;
	struct smb_dirent *entry = NULL;
	struct smb_server *server = SMB_SERVER(dir);

	DPRINTK("smb_readdir: filp->f_pos = %d\n", (int) filp->f_pos);
	DDPRINTK("smb_readdir: dir->i_ino = %ld, c_ino = %ld\n",
		 dir->i_ino, c_ino);

	if ((dir == NULL) || !S_ISDIR(dir->i_mode))
	{
		printk("smb_readdir: dir is NULL or not a directory\n");
		return -EBADF;
	}
	if (c_entry == NULL)
	{
		i = sizeof(struct smb_dirent) * SMB_READDIR_CACHE_SIZE;
		c_entry = (struct smb_dirent *) smb_vmalloc(i);
		if (c_entry == NULL)
		{
			printk("smb_readdir: no MEMORY for cache\n");
			return -ENOMEM;
		}
	}
	if (filp->f_pos == 0)
	{
		c_ino = 0;
		c_dev = 0;
		c_seen_eof = 0;

		if (filldir(dirent, ".", 1, filp->f_pos,
			    smb_info_ino(SMB_INOP(dir))) < 0)
		{
			return 0;
		}
		filp->f_pos += 1;
	}
	if (filp->f_pos == 1)
	{
		if (filldir(dirent, "..", 2, filp->f_pos,
			    smb_info_ino(SMB_INOP(dir)->dir)) < 0)
		{
			return 0;
		}
		filp->f_pos += 1;
	}
	entry = smb_search_in_cache(dir, filp->f_pos);

	if (entry == NULL)
	{
		if (c_seen_eof)
		{
			/* End of directory */
			return 0;
		}
		result = smb_refill_dir_cache(server, dir, filp->f_pos);
		if (result <= 0)
		{
			return result;
		}
		entry = c_entry;
	}
	while (entry < &(c_entry[c_size]))
	{
		/* We found it.  For getwd(), we have to return the
		   correct inode in d_ino if the inode is currently in
		   use. Otherwise the inode number does not
		   matter. (You can argue a lot about this..) */

		struct smb_inode_info *ino_info
		= smb_find_dir_inode(dir, entry->name, entry->len);

		ino_t ino = entry->f_ino;

		if (ino_info != NULL)
		{
			ino = smb_info_ino(ino_info);
		}
		DDPRINTK("smb_readdir: entry->name = %s\n", entry->name);
		DDPRINTK("smb_readdir: entry->f_pos = %ld\n", entry->f_pos);

		if (filldir(dirent, entry->name, strlen(entry->name),
			    entry->f_pos, ino) < 0)
		{
			break;
		}
		if ((dir->i_dev != c_dev) || (dir->i_ino != c_ino)
		    || (entry->f_pos != filp->f_pos))
		{
			/* Someone has destroyed the cache while we slept
			   in filldir */
			break;
		}
		filp->f_pos += 1;
		entry += 1;
	}
	return 0;
}
Example #3
0
/*
 * This routine checks first for "fast track" processing, as most
 * packets won't need to be copied. Otherwise, it allocates a new
 * packet to hold the incoming data.
 *
 * Note that the final server packet must be the larger of the two;
 * server packets aren't allowed to shrink.
 */
static int
smb_receive_trans2(struct smb_sb_info *server,
		   int *ldata, unsigned char **data,
		   int *lparm, unsigned char **parm)
{
	unsigned char *inbuf, *base, *rcv_buf = NULL;
	unsigned int parm_disp, parm_offset, parm_count, parm_tot, parm_len = 0;
	unsigned int data_disp, data_offset, data_count, data_tot, data_len = 0;
	unsigned int total_p = 0, total_d = 0, buf_len = 0;
	int result;

	while (1)
	{
		result = smb_receive(server);
		if (result < 0)
			goto out;
		inbuf = server->packet;
		if (server->rcls != 0)
		{
			*parm = *data = inbuf;
			*ldata = *lparm = 0;
			goto out;
		}
		/*
		 * Extract the control data from the packet.
		 */
		data_tot    = WVAL(inbuf, smb_tdrcnt);
		parm_tot    = WVAL(inbuf, smb_tprcnt);
		parm_disp   = WVAL(inbuf, smb_prdisp);
		parm_offset = WVAL(inbuf, smb_proff);
		parm_count  = WVAL(inbuf, smb_prcnt);
		data_disp   = WVAL(inbuf, smb_drdisp);
		data_offset = WVAL(inbuf, smb_droff);
		data_count  = WVAL(inbuf, smb_drcnt);
		base = smb_base(inbuf);

		/*
		 * Assume success and increment lengths.
		 */
		parm_len += parm_count;
		data_len += data_count;

		if (!rcv_buf)
		{
			/*
			 * Check for fast track processing ... just this packet.
			 */
			if (parm_count == parm_tot && data_count == data_tot)
			{
				VERBOSE("fast track, parm=%u %u %u, data=%u %u %u\n",
					parm_disp, parm_offset, parm_count, 
					data_disp, data_offset, data_count);

				*parm  = base + parm_offset;
				*data  = base + data_offset;
				goto success;
			}

			if (parm_tot > TRANS2_MAX_TRANSFER ||
	  		    data_tot > TRANS2_MAX_TRANSFER)
				goto out_too_long;

			/*
			 * Save the total parameter and data length.
			 */
			total_d = data_tot;
			total_p = parm_tot;

			buf_len = total_d + total_p;
			if (server->packet_size > buf_len)
				buf_len = server->packet_size;
			buf_len = smb_round_length(buf_len);

			rcv_buf = smb_vmalloc(buf_len);
			if (!rcv_buf)
				goto out_no_mem;
			*parm = rcv_buf;
			*data = rcv_buf + total_p;
		}
		else if (data_tot > total_d || parm_tot > total_p)
			goto out_data_grew;

		if (parm_disp + parm_count > total_p)
			goto out_bad_parm;
		if (data_disp + data_count > total_d)
			goto out_bad_data;
		memcpy(*parm + parm_disp, base + parm_offset, parm_count);
		memcpy(*data + data_disp, base + data_offset, data_count);

		PARANOIA("copied, parm=%u of %u, data=%u of %u\n",
			 parm_len, parm_tot, data_len, data_tot);

		/*
		 * Check whether we've received all of the data. Note that
		 * we use the packet totals -- total lengths might shrink!
		 */
		if (data_len >= data_tot && parm_len >= parm_tot)
			break;
	}

	/*
	 * Install the new packet.  Note that it's possible, though
	 * unlikely, that the new packet could be smaller than the
	 * old one, in which case we just copy the data.
	 */
	inbuf = server->packet;
	if (buf_len >= server->packet_size)
	{
		server->packet_size = buf_len;
		server->packet = rcv_buf;
		rcv_buf = inbuf;
	} else {
		PARANOIA("copying data, old size=%d, new size=%u\n",
			 server->packet_size, buf_len);
		memcpy(inbuf, rcv_buf, parm_len + data_len);
	}

success:
	*ldata = data_len;
	*lparm = parm_len;
out:
	if (rcv_buf)
		smb_vfree(rcv_buf);
	return result;

out_no_mem:
	printk(KERN_ERR "smb_receive_trans2: couldn't allocate data area\n");
	result = -ENOMEM;
	goto out;
out_too_long:
	printk(KERN_ERR "smb_receive_trans2: data/param too long, data=%d, parm=%d\n",
	       data_tot, parm_tot);
	goto out_error;
out_data_grew:
	printk(KERN_ERR "smb_receive_trans2: data/params grew!\n");
	goto out_error;
out_bad_parm:
	printk(KERN_ERR "smb_receive_trans2: invalid parms, disp=%d, cnt=%d, tot=%d\n",
	       parm_disp, parm_count, parm_tot);
	goto out_error;
out_bad_data:
	printk(KERN_ERR "smb_receive_trans2: invalid data, disp=%d, cnt=%d, tot=%d\n",
	       data_disp, data_count, data_tot);
out_error:
	result = -EIO;
	goto out;
}