1/*
2 * Copyright (c) 2011 - 2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25  * Fun with FIDs
26 * (1) SMB2 uses two uint64_t of fid_persistent and fid_volatile
27 * (2) SMB1 uses a single uint16_t for the FID
28 * (3) User space functions use a single uint64_t for the FID
29 *
30 * Solution: All functions will pass around a single uint64_t FID.
31 * (1) SMB2 will map the uint64_t FID to the two uint64_t SMB2 FID.  This will
32 *     done at the very last layer when the SMB2 packets are being built and
33 *     in the code that parses out the reply packet.
34 * (2) SMB1 will just saves its uint16_t FID inside the uint64_t and will just
35 *     assign it or read it to/from a uint16_t temp value.
36 * (3) User space functions will remain unchanged and continue to use a
37 *     uint64_t regardless of whether they are using SMB1 or SMB2. This will
38 *     also make reconnect "invisible" to the user level since a reconnect will
39 *     just update the mapping table.
40 *
41 */
42
43//#include <sys/msfscc.h>
44#include <sys/smb_apple.h>
45#include <sys/param.h>
46#include <sys/kauth.h>
47
48#include <netsmb/smb.h>
49#include <netsmb/smb_2.h>
50#include <netsmb/smb_packets_2.h>
51#include <netsmb/smb_rq.h>
52#include <netsmb/smb_rq_2.h>
53#include <netsmb/smb_conn.h>
54#include <netsmb/smb_conn_2.h>
55#include <netsmb/smb_tran.h>
56#include <netsmb/smb_gss.h>
57#include <netsmb/smb_fid.h>
58#include <smbfs/smbfs_subr.h>
59#include <smbfs/smbfs_subr_2.h>
60#include <smbfs/smbfs_node.h>
61#include <netsmb/smb_converter.h>
62
63static void smb_fid_insert_new_node(struct smb_share *share, SMB_FID_NODE *node);
64static uint32_t smb_fid_one_at_a_time(uint8_t *key, uint32_t len);
65
66/* smb_fid_count_all() is used for Debugging */
67uint64_t
68smb_fid_count_all(struct smb_share *share)
69{
70    FID_HASH_TABLE_SLOT *slotPtr;
71    SMB_FID_NODE *node, *temp_node;
72    uint32_t table_index;
73    uint64_t count = 0;
74
75    if (share == NULL) {
76        SMBERROR("share is null\n");
77        return (0);
78    }
79
80    smb_fid_table_lock(share);
81
82    for (table_index = 0; table_index < SMB_FID_TABLE_SIZE; table_index += 1) {
83        slotPtr = &share->ss_fid_table[table_index];
84        if (slotPtr == NULL) {
85            continue;
86        }
87
88        LIST_FOREACH_SAFE(node, &slotPtr->fid_list, link, temp_node) {
89            count++;
90        }
91    }
92
93    smb_fid_table_unlock(share);
94
95    return count;
96}
97
98void
99smb_fid_delete_all(struct smb_share *share)
100{
101    FID_HASH_TABLE_SLOT *slotPtr;
102    SMB_FID_NODE *node, *temp_node;
103    uint32_t table_index;
104
105    if (share == NULL) {
106        SMBERROR("share is null\n");
107        return;
108    }
109
110    smb_fid_table_lock(share);
111
112    for (table_index = 0; table_index < SMB_FID_TABLE_SIZE; table_index += 1) {
113        slotPtr = &share->ss_fid_table[table_index];
114        if (slotPtr == NULL) {
115            continue;
116        }
117
118        LIST_FOREACH_SAFE(node, &slotPtr->fid_list, link, temp_node) {
119            LIST_REMOVE(node, link);
120            SMB_FREE(node, M_TEMP);
121        }
122    }
123
124    smb_fid_table_unlock(share);
125}
126
127int
128smb_fid_get_kernel_fid(struct smb_share *share, SMBFID fid, int remove_fid,
129                       SMB2FID *smb2_fid)
130{
131    FID_HASH_TABLE_SLOT *slotPtr;
132    SMB_FID_NODE *node;
133    uint32_t table_index, iter;
134    uint32_t found_it = 0;
135    int error = EINVAL;
136
137    /* cant put it into smp because that is NULL for DCERPC calls to srvsvc */
138
139    if (share == NULL) {
140        SMBERROR("share is null\n");
141        return EINVAL;
142    };
143
144    /* handle compound requests */
145    if (fid == 0xffffffffffffffff) {
146        smb2_fid->fid_persistent = 0xffffffffffffffff;
147        smb2_fid->fid_volatile = 0xffffffffffffffff;
148        return (0);
149    }
150
151    smb_fid_table_lock(share);
152
153    /* calculate the slot */
154    table_index = fid & SMB_FID_TABLE_MASK;
155    if (table_index >= SMB_FID_TABLE_SIZE) {
156        SMBERROR("Bad table_index: %u for fid %llx\n", table_index, fid);
157        goto exit;
158    };
159
160    slotPtr = &share->ss_fid_table[table_index];
161    if (slotPtr == NULL) {
162        SMBERROR("slotPtr is null for table_index %u, fid %llx\n",
163                 table_index, fid);
164        goto exit;
165    };
166
167    iter = 0;
168    LIST_FOREACH(node, &slotPtr->fid_list, link) {
169        if (node->fid == fid) {
170            *smb2_fid = node->smb2_fid;
171            found_it = 1;
172
173            if (remove_fid == 1) {
174                /*SMBERROR("remove smb2 fid %llx %llx -> fid %llx\n",
175                         node->smb2_fid.fid_persistent,
176                         node->smb2_fid.fid_volatile,
177                         fid);*/
178                LIST_REMOVE(node, link);
179				SMB_FREE(node, M_TEMP);
180            }
181            break;
182        }
183        iter++;
184    }
185
186    if (iter >= share->ss_fid_max_iter) {
187        share->ss_fid_max_iter = iter;
188    }
189
190    if (found_it == 1) {
191        /*SMBERROR("fid %llx -> smb2 fid %llx %llx\n",
192                 fid,
193                 smb2_fid->fid_persistent,
194                 smb2_fid->fid_volatile);*/
195        error = 0;
196    }
197    else {
198        SMBERROR("No smb2 fid found for fid %llx\n", fid);
199    }
200exit:
201    smb_fid_table_unlock(share);
202    return (error);
203}
204
205int
206smb_fid_get_user_fid(struct smb_share *share, SMB2FID smb2_fid, SMBFID *ret_fid)
207{
208    uint64_t fid, val1, val2;
209    SMB_FID_NODE *node;
210    int error = EINVAL;
211
212    if (share == NULL) {
213        SMBERROR("share is null\n");
214        return EINVAL;
215    };
216
217    smb_fid_table_lock(share);
218
219    val1 = smb_fid_one_at_a_time((uint8_t *)&smb2_fid.fid_persistent,
220                                 sizeof(smb2_fid.fid_persistent));
221    val2 = smb_fid_one_at_a_time((uint8_t *)&smb2_fid.fid_volatile,
222                                 sizeof(smb2_fid.fid_volatile));
223
224    fid = (val1 << 32) | val2;
225
226    SMB_MALLOC(node, SMB_FID_NODE *, sizeof(SMB_FID_NODE), M_TEMP, M_WAITOK);
227    if (node != NULL) {
228        node->fid = fid;
229        node->smb2_fid = smb2_fid;
230
231        // insert our new node into the hash table
232        smb_fid_insert_new_node(share, node);
233        /*SMBERROR("insert smb2 fid %llx %llx -> fid %llx\n",
234                 smb2_fid.fid_persistent,
235                 smb2_fid.fid_volatile,
236                 fid);*/
237        *ret_fid = fid;
238        error = 0;
239    }
240    else {
241        SMBERROR("malloc failed\n");
242        error = ENOMEM;
243    }
244
245    smb_fid_table_unlock(share);
246    return (error);
247}
248
249static void
250smb_fid_insert_new_node(struct smb_share *share, SMB_FID_NODE *node)
251{
252    FID_HASH_TABLE_SLOT *slotPtr;
253    uint32_t table_index;
254
255    // calculate the slot
256    table_index = node->fid & SMB_FID_TABLE_MASK;
257    DBG_ASSERT(table_index < SMB_FID_TABLE_SIZE);
258
259    slotPtr = &share->ss_fid_table[table_index];
260
261    if (LIST_EMPTY(&slotPtr->fid_list)) {
262    }
263    else {
264        share->ss_fid_collisions++;
265    }
266    LIST_INSERT_HEAD(&slotPtr->fid_list, node, link);
267    share->ss_fid_inserted++;
268}
269
270/* A quick little hash function
271 * Written by Bob Jenkins, and put in the public domain
272 * See http://burtleburtle.net/bob/hash/doobs.html
273 */
274static uint32_t
275smb_fid_one_at_a_time(uint8_t *key, uint32_t len)
276{
277    uint32_t   hash, i;
278
279    for (hash = 0, i = 0; i < len; ++i)
280    {
281        hash += key[i];
282        hash += (hash << 10);
283        hash ^= (hash >> 6);
284    }
285    hash += (hash << 3);
286    hash ^= (hash >> 11);
287    hash += (hash << 15);
288    return (hash);
289}
290
291
292