source: branches/io/libtransmission/fdlimit.c @ 1200

Last change on this file since 1200 was 1200, checked in by titer, 15 years ago

Don't try to create the parent folder, and only create sub-folders for read-write access

  • Property svn:keywords set to Date Rev Author Id
File size: 11.1 KB
Line 
1/******************************************************************************
2 * $Id: fdlimit.c 1200 2006-12-13 21:32:54Z titer $
3 *
4 * Copyright (c) 2005-2006 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include "transmission.h"
26
27#define TR_MAX_OPEN_FILES 16 /* That is, real files, not sockets */
28#define TR_RESERVED_FDS   16 /* Number of sockets reserved for
29                                connections to trackers */
30
31typedef struct tr_openFile_s
32{
33    char       folder[MAX_PATH_LENGTH];
34    char       name[MAX_PATH_LENGTH];
35    int        file;
36    int        write;
37
38#define STATUS_INVALID 1
39#define STATUS_UNUSED  2
40#define STATUS_USED    4
41#define STATUS_CLOSING 8
42    int        status;
43
44    uint64_t   date;
45
46} tr_openFile_t;
47
48struct tr_fd_s
49{
50    tr_lock_t       lock;
51   
52    int             reserved;
53
54    int             normal;
55    int             normalMax;
56
57    tr_openFile_t   open[TR_MAX_OPEN_FILES];
58};
59
60/***********************************************************************
61 * Local prototypes
62 **********************************************************************/
63static int  ErrorFromErrno();
64static void CloseFile( tr_fd_t * f, int i );
65static int  CheckFolder( char * folder, char * name, int write );
66
67
68
69/***********************************************************************
70 * tr_fdInit
71 **********************************************************************/
72tr_fd_t * tr_fdInit()
73{
74    tr_fd_t * f;
75    int i, j, s[4096];
76
77    f = calloc( sizeof( tr_fd_t ), 1 );
78
79    /* Init lock */
80    tr_lockInit( &f->lock );
81
82    /* Detect the maximum number of open files or sockets */
83    for( i = 0; i < 4096; i++ )
84    {
85        if( ( s[i] = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
86        {
87            break;
88        }
89    }
90    for( j = 0; j < i; j++ )
91    {
92        tr_netClose( s[j] );
93    }
94
95    tr_dbg( "%d usable file descriptors", i );
96
97    f->reserved  = 0;
98    f->normal    = 0;
99
100    f->normalMax = i - TR_RESERVED_FDS - 10;
101        /* To be safe, in case the UI needs to write a preferences file
102           or something */
103
104    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
105    {
106        f->open[i].status = STATUS_INVALID;
107    }
108
109    return f;
110}
111
112/***********************************************************************
113 * tr_fdFileOpen
114 **********************************************************************/
115int tr_fdFileOpen( tr_fd_t * f, char * folder, char * name, int write )
116{
117    int i, winner, ret;
118    uint64_t date;
119    char * path;
120
121    tr_lockLock( &f->lock );
122
123    /* Is it already open? */
124    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
125    {
126        if( f->open[i].status & STATUS_INVALID ||
127            strcmp( folder, f->open[i].folder ) ||
128            strcmp( name, f->open[i].name ) )
129        {
130            continue;
131        }
132        if( f->open[i].status & STATUS_CLOSING )
133        {
134            /* File is being closed by another thread, wait until
135             * it's done before we reopen it */
136            tr_lockUnlock( &f->lock );
137            tr_wait( 10 );
138            tr_lockLock( &f->lock );
139            i = -1;
140            continue;
141        }
142        if( f->open[i].write < write )
143        {
144            /* File is open read-only and needs to be closed then
145             * re-opened read-write */
146            CloseFile( f, i );
147            continue;
148        }
149        winner = i;
150        goto done;
151    }
152
153    /* We'll need to open it, make sure that we can */
154    if( ( ret = CheckFolder( folder, name, write ) ) )
155    {
156        tr_err( "Can not open %s in %s (%d)", name, folder, ret );
157        tr_lockUnlock( &f->lock );
158        return ret;
159    }
160
161    /* Can we open one more file? */
162    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
163    {
164        if( f->open[i].status & STATUS_INVALID )
165        {
166            winner = i;
167            goto open;
168        }
169    }
170
171    for( ;; )
172    {
173        /* Close the oldest currently unused file */
174        date   = tr_date() + 1;
175        winner = -1;
176
177        for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
178        {
179            if( !( f->open[i].status & STATUS_UNUSED ) )
180            {
181                continue;
182            }
183            if( f->open[i].date < date )
184            {
185                winner = i;
186                date   = f->open[i].date;
187            }
188        }
189
190        if( winner >= 0 )
191        {
192            CloseFile( f, winner );
193            goto open;
194        }
195
196        /* All used! Wait a bit and try again */
197        tr_lockUnlock( &f->lock );
198        tr_wait( 10 );
199        tr_lockLock( &f->lock );
200    }
201
202open:
203    tr_dbg( "Opening %s in %s", name, folder );
204    asprintf( &path, "%s/%s", folder, name );
205    f->open[winner].file = open( path, write ? O_RDWR : O_RDONLY, 0 );
206    free( path );
207    if( f->open[winner].file < 0 )
208    {
209        ret = ErrorFromErrno();
210        tr_lockUnlock( &f->lock );
211        tr_err( "Could not open %s in %s (%d)", name, folder, ret );
212        return ret;
213    }
214    snprintf( f->open[winner].folder, MAX_PATH_LENGTH, "%s", folder );
215    snprintf( f->open[winner].name, MAX_PATH_LENGTH, "%s", name );
216    f->open[winner].write = write;
217
218done:
219    f->open[winner].status = STATUS_USED;
220    f->open[winner].date   = tr_date();
221    tr_lockUnlock( &f->lock );
222   
223    return f->open[winner].file;
224}
225
226/***********************************************************************
227 * tr_fdFileRelease
228 **********************************************************************/
229void tr_fdFileRelease( tr_fd_t * f, int file )
230{
231    int i;
232    tr_lockLock( &f->lock );
233
234    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
235    {
236        if( f->open[i].file == file )
237        {
238            f->open[i].status = STATUS_UNUSED;
239            break;
240        }
241    }
242   
243    tr_lockUnlock( &f->lock );
244}
245
246/***********************************************************************
247 * tr_fdFileClose
248 **********************************************************************/
249void tr_fdFileClose( tr_fd_t * f, char * folder, char * name )
250{
251    int i;
252
253    tr_lockLock( &f->lock );
254
255    /* Is it already open? */
256    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
257    {
258        if( f->open[i].status & STATUS_INVALID )
259        {
260            continue;
261        }
262        if( !strcmp( folder, f->open[i].folder ) &&
263            !strcmp( name, f->open[i].name ) )
264        {
265            CloseFile( f, i );
266        }
267    }
268
269    tr_lockUnlock( &f->lock );
270}
271
272/***********************************************************************
273 * tr_fdSocketWillCreate
274 **********************************************************************/
275int tr_fdSocketWillCreate( tr_fd_t * f, int reserved )
276{
277    int ret;
278
279    tr_lockLock( &f->lock );
280
281    if( reserved )
282    {
283        if( f->reserved < TR_RESERVED_FDS )
284        {
285            ret = 0;
286            (f->reserved)++;
287        }
288        else
289        {
290            ret = 1;
291        }
292    }
293    else
294    {
295        if( f->normal < f->normalMax )
296        {
297            ret = 0;
298            (f->normal)++;
299        }
300        else
301        {
302            ret = 1;
303        }
304    }
305
306    tr_lockUnlock( &f->lock );
307
308    return ret;
309}
310
311/***********************************************************************
312 * tr_fdSocketClosed
313 **********************************************************************/
314void tr_fdSocketClosed( tr_fd_t * f, int reserved )
315{
316    tr_lockLock( &f->lock );
317
318    if( reserved )
319    {
320        (f->reserved)--;
321    }
322    else
323    {
324        (f->normal)--;
325    }
326
327    tr_lockUnlock( &f->lock );
328}
329
330/***********************************************************************
331 * tr_fdClose
332 **********************************************************************/
333void tr_fdClose( tr_fd_t * f )
334{
335    tr_lockClose( &f->lock );
336    free( f );
337}
338
339
340/***********************************************************************
341 * Local functions
342 **********************************************************************/
343
344/***********************************************************************
345 * ErrorFromErrno
346 **********************************************************************/
347static int ErrorFromErrno()
348{
349    if( errno == EACCES || errno == EROFS )
350        return TR_ERROR_IO_PERMISSIONS;
351    return TR_ERROR_IO_OTHER;
352}
353
354/***********************************************************************
355 * CloseFile
356 ***********************************************************************
357 * We first mark it as closing then release the lock while doing so,
358 * because close() may take same time and we don't want to block other
359 * threads.
360 **********************************************************************/
361static void CloseFile( tr_fd_t * f, int i )
362{
363    if( !( f->open[i].status & STATUS_UNUSED ) )
364    {
365        tr_err( "CloseFile: status is %d, should be %d",
366                f->open[i].status, STATUS_UNUSED );
367    }
368    tr_dbg( "Closing %s in %s", f->open[i].name, f->open[i].folder );
369    f->open[i].status = STATUS_CLOSING;
370    tr_lockUnlock( &f->lock );
371    close( f->open[i].file );
372    tr_lockLock( &f->lock );
373    f->open[i].status = STATUS_INVALID;
374}
375
376/***********************************************************************
377 * CheckFolder
378 ***********************************************************************
379 *
380 **********************************************************************/
381static int CheckFolder( char * folder, char * name, int write )
382{
383    struct stat sb;
384    char * path, * p, * s;
385    int ret = 0;
386
387    if( stat( folder, &sb ) || !S_ISDIR( sb.st_mode ) )
388    {
389        return TR_ERROR_IO_PARENT;
390    }
391
392    asprintf( &path, "%s/%s", folder, name );
393
394    p = path + strlen( folder ) + 1;;
395    while( ( s = strchr( p, '/' ) ) )
396    {
397        *s = '\0';
398        if( stat( folder, &sb ) )
399        {
400            /* Sub-folder does not exist */
401            if( !write )
402            {
403                ret = TR_ERROR_IO_OTHER;
404                break;
405            }
406            if( mkdir( path, 0777 ) )
407            {
408                ret = ErrorFromErrno();
409                break;
410            }
411        }
412        else
413        {
414            if( !S_ISDIR( sb.st_mode ) )
415            {
416                ret = TR_ERROR_IO_OTHER;
417                break;
418            }
419        }
420        *s = '/';
421    }
422    free( path );
423
424    return ret;
425}
Note: See TracBrowser for help on using the repository browser.