~sergei.glushchenko/percona-xtrabackup/xb-pprint

« back to all changes in this revision

Viewing changes to src/libarchive/libarchive/archive_write_disk_set_standard_lookup.c

merge parallel compression branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-
 
2
 * Copyright (c) 2003-2007 Tim Kientzle
 
3
 * All rights reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions
 
7
 * are met:
 
8
 * 1. Redistributions of source code must retain the above copyright
 
9
 *    notice, this list of conditions and the following disclaimer.
 
10
 * 2. Redistributions in binary form must reproduce the above copyright
 
11
 *    notice, this list of conditions and the following disclaimer in the
 
12
 *    documentation and/or other materials provided with the distribution.
 
13
 *
 
14
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 
15
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
16
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 
17
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 
18
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
19
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
20
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
21
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
22
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
23
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
24
 */
 
25
 
 
26
#include "archive_platform.h"
 
27
__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_disk_set_standard_lookup.c 201083 2009-12-28 02:09:57Z kientzle $");
 
28
 
 
29
#ifdef HAVE_SYS_TYPES_H
 
30
#include <sys/types.h>
 
31
#endif
 
32
#ifdef HAVE_ERRNO_H
 
33
#include <errno.h>
 
34
#endif
 
35
#ifdef HAVE_GRP_H
 
36
#include <grp.h>
 
37
#endif
 
38
#ifdef HAVE_PWD_H
 
39
#include <pwd.h>
 
40
#endif
 
41
#ifdef HAVE_STDLIB_H
 
42
#include <stdlib.h>
 
43
#endif
 
44
#ifdef HAVE_STRING_H
 
45
#include <string.h>
 
46
#endif
 
47
 
 
48
#include "archive.h"
 
49
#include "archive_private.h"
 
50
#include "archive_read_private.h"
 
51
#include "archive_write_disk_private.h"
 
52
 
 
53
struct bucket {
 
54
        char    *name;
 
55
        int      hash;
 
56
        id_t     id;
 
57
};
 
58
 
 
59
static const size_t cache_size = 127;
 
60
static unsigned int     hash(const char *);
 
61
static gid_t    lookup_gid(void *, const char *uname, gid_t);
 
62
static uid_t    lookup_uid(void *, const char *uname, uid_t);
 
63
static void     cleanup(void *);
 
64
 
 
65
/*
 
66
 * Installs functions that use getpwnam()/getgrnam()---along with
 
67
 * a simple cache to accelerate such lookups---into the archive_write_disk
 
68
 * object.  This is in a separate file because getpwnam()/getgrnam()
 
69
 * can pull in a LOT of library code (including NIS/LDAP functions, which
 
70
 * pull in DNS resolveers, etc).  This can easily top 500kB, which makes
 
71
 * it inappropriate for some space-constrained applications.
 
72
 *
 
73
 * Applications that are size-sensitive may want to just use the
 
74
 * real default functions (defined in archive_write_disk.c) that just
 
75
 * use the uid/gid without the lookup.  Or define your own custom functions
 
76
 * if you prefer.
 
77
 *
 
78
 * TODO: Replace these hash tables with simpler move-to-front LRU
 
79
 * lists with a bounded size (128 items?).  The hash is a bit faster,
 
80
 * but has a bad pathology in which it thrashes a single bucket.  Even
 
81
 * walking a list of 128 items is a lot faster than calling
 
82
 * getpwnam()!
 
83
 */
 
84
int
 
85
archive_write_disk_set_standard_lookup(struct archive *a)
 
86
{
 
87
        struct bucket *ucache = malloc(cache_size * sizeof(struct bucket));
 
88
        struct bucket *gcache = malloc(cache_size * sizeof(struct bucket));
 
89
        memset(ucache, 0, cache_size * sizeof(struct bucket));
 
90
        memset(gcache, 0, cache_size * sizeof(struct bucket));
 
91
        archive_write_disk_set_group_lookup(a, gcache, lookup_gid, cleanup);
 
92
        archive_write_disk_set_user_lookup(a, ucache, lookup_uid, cleanup);
 
93
        return (ARCHIVE_OK);
 
94
}
 
95
 
 
96
static gid_t
 
97
lookup_gid(void *private_data, const char *gname, gid_t gid)
 
98
{
 
99
        int h;
 
100
        struct bucket *b;
 
101
        struct bucket *gcache = (struct bucket *)private_data;
 
102
 
 
103
        /* If no gname, just use the gid provided. */
 
104
        if (gname == NULL || *gname == '\0')
 
105
                return (gid);
 
106
 
 
107
        /* Try to find gname in the cache. */
 
108
        h = hash(gname);
 
109
        b = &gcache[h % cache_size ];
 
110
        if (b->name != NULL && b->hash == h && strcmp(gname, b->name) == 0)
 
111
                return ((gid_t)b->id);
 
112
 
 
113
        /* Free the cache slot for a new entry. */
 
114
        if (b->name != NULL)
 
115
                free(b->name);
 
116
        b->name = strdup(gname);
 
117
        /* Note: If strdup fails, that's okay; we just won't cache. */
 
118
        b->hash = h;
 
119
#if HAVE_GRP_H
 
120
#  if HAVE_GETGRNAM_R
 
121
        {
 
122
                char _buffer[128];
 
123
                size_t bufsize = 128;
 
124
                char *buffer = _buffer;
 
125
                struct group    grent, *result;
 
126
                int r;
 
127
 
 
128
                for (;;) {
 
129
                        result = &grent; /* Old getgrnam_r ignores last arg. */
 
130
                        r = getgrnam_r(gname, &grent, buffer, bufsize, &result);
 
131
                        if (r == 0)
 
132
                                break;
 
133
                        if (r != ERANGE)
 
134
                                break;
 
135
                        bufsize *= 2;
 
136
                        if (buffer != _buffer)
 
137
                                free(buffer);
 
138
                        buffer = malloc(bufsize);
 
139
                        if (buffer == NULL)
 
140
                                break;
 
141
                }
 
142
                if (result != NULL)
 
143
                        gid = result->gr_gid;
 
144
                if (buffer != _buffer)
 
145
                        free(buffer);
 
146
        }
 
147
#  else /* HAVE_GETGRNAM_R */
 
148
        {
 
149
                struct group *result;
 
150
 
 
151
                result = getgrnam(gname);
 
152
                if (result != NULL)
 
153
                        gid = result->gr_gid;
 
154
        }
 
155
#  endif /* HAVE_GETGRNAM_R */
 
156
#elif defined(_WIN32) && !defined(__CYGWIN__)
 
157
        /* TODO: do a gname->gid lookup for Windows. */
 
158
#else
 
159
        #error No way to perform gid lookups on this platform
 
160
#endif
 
161
        b->id = gid;
 
162
 
 
163
        return (gid);
 
164
}
 
165
 
 
166
static uid_t
 
167
lookup_uid(void *private_data, const char *uname, uid_t uid)
 
168
{
 
169
        int h;
 
170
        struct bucket *b;
 
171
        struct bucket *ucache = (struct bucket *)private_data;
 
172
 
 
173
        /* If no uname, just use the uid provided. */
 
174
        if (uname == NULL || *uname == '\0')
 
175
                return (uid);
 
176
 
 
177
        /* Try to find uname in the cache. */
 
178
        h = hash(uname);
 
179
        b = &ucache[h % cache_size ];
 
180
        if (b->name != NULL && b->hash == h && strcmp(uname, b->name) == 0)
 
181
                return ((uid_t)b->id);
 
182
 
 
183
        /* Free the cache slot for a new entry. */
 
184
        if (b->name != NULL)
 
185
                free(b->name);
 
186
        b->name = strdup(uname);
 
187
        /* Note: If strdup fails, that's okay; we just won't cache. */
 
188
        b->hash = h;
 
189
#if HAVE_PWD_H
 
190
#  if HAVE_GETPWNAM_R
 
191
        {
 
192
                char _buffer[128];
 
193
                size_t bufsize = 128;
 
194
                char *buffer = _buffer;
 
195
                struct passwd   pwent, *result;
 
196
                int r;
 
197
 
 
198
                for (;;) {
 
199
                        result = &pwent; /* Old getpwnam_r ignores last arg. */
 
200
                        r = getpwnam_r(uname, &pwent, buffer, bufsize, &result);
 
201
                        if (r == 0)
 
202
                                break;
 
203
                        if (r != ERANGE)
 
204
                                break;
 
205
                        bufsize *= 2;
 
206
                        if (buffer != _buffer)
 
207
                                free(buffer);
 
208
                        buffer = malloc(bufsize);
 
209
                        if (buffer == NULL)
 
210
                                break;
 
211
                }
 
212
                if (result != NULL)
 
213
                        uid = result->pw_uid;
 
214
                if (buffer != _buffer)
 
215
                        free(buffer);
 
216
        }
 
217
#  else /* HAVE_GETPWNAM_R */
 
218
        {
 
219
                struct passwd *result;
 
220
 
 
221
                result = getpwnam(uname);
 
222
                if (result != NULL)
 
223
                        uid = result->pw_uid;
 
224
        }
 
225
#endif  /* HAVE_GETPWNAM_R */
 
226
#elif defined(_WIN32) && !defined(__CYGWIN__)
 
227
        /* TODO: do a uname->uid lookup for Windows. */
 
228
#else
 
229
        #error No way to look up uids on this platform
 
230
#endif
 
231
        b->id = uid;
 
232
 
 
233
        return (uid);
 
234
}
 
235
 
 
236
static void
 
237
cleanup(void *private)
 
238
{
 
239
        size_t i;
 
240
        struct bucket *cache = (struct bucket *)private;
 
241
 
 
242
        for (i = 0; i < cache_size; i++)
 
243
                free(cache[i].name);
 
244
        free(cache);
 
245
}
 
246
 
 
247
 
 
248
static unsigned int
 
249
hash(const char *p)
 
250
{
 
251
        /* A 32-bit version of Peter Weinberger's (PJW) hash algorithm,
 
252
           as used by ELF for hashing function names. */
 
253
        unsigned g, h = 0;
 
254
        while (*p != '\0') {
 
255
                h = (h << 4) + *p++;
 
256
                if ((g = h & 0xF0000000) != 0) {
 
257
                        h ^= g >> 24;
 
258
                        h &= 0x0FFFFFFF;
 
259
                }
 
260
        }
 
261
        return h;
 
262
}