mapping.c 20.5 KB
Newer Older
1 2 3 4
/*
 * Server-side file mapping management
 *
 * Copyright (C) 1999 Alexandre Julliard
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 20
 */

21
#include "config.h"
22
#include "wine/port.h"
23

24
#include <assert.h>
25
#include <stdarg.h>
26 27
#include <stdio.h>
#include <stdlib.h>
28
#include <sys/stat.h>
29 30
#include <unistd.h>

31 32
#include "ntstatus.h"
#define WIN32_NO_STATUS
33
#include "windef.h"
34
#include "winternl.h"
35

36
#include "file.h"
37 38
#include "handle.h"
#include "thread.h"
39
#include "request.h"
40
#include "security.h"
41

42 43 44 45 46 47 48 49 50 51 52 53
/* list of memory ranges, used to store committed info */
struct ranges
{
    unsigned int count;
    unsigned int max;
    struct range
    {
        file_pos_t  start;
        file_pos_t  end;
    } ranges[1];
};

54 55
struct mapping
{
56
    struct object   obj;             /* object header */
57
    mem_size_t      size;            /* mapping size */
58 59 60
    int             protect;         /* protection flags */
    struct file    *file;            /* file mapped */
    int             header_size;     /* size of headers (for PE image mapping) */
61
    client_ptr_t    base;            /* default base addr (for PE image mapping) */
62
    struct ranges  *committed;       /* list of committed ranges in this mapping */
63
    struct file    *shared_file;     /* temp file for shared PE mapping */
64
    struct list     shared_entry;    /* entry in global shared PE mappings list */
65 66 67
};

static void mapping_dump( struct object *obj, int verbose );
68
static struct object_type *mapping_get_type( struct object *obj );
69
static struct fd *mapping_get_fd( struct object *obj );
70
static unsigned int mapping_map_access( struct object *obj, unsigned int access );
71 72 73 74
static void mapping_destroy( struct object *obj );

static const struct object_ops mapping_ops =
{
75 76
    sizeof(struct mapping),      /* size */
    mapping_dump,                /* dump */
77
    mapping_get_type,            /* get_type */
78 79 80 81
    no_add_queue,                /* add_queue */
    NULL,                        /* remove_queue */
    NULL,                        /* signaled */
    NULL,                        /* satisfied */
82
    no_signal,                   /* signal */
83
    mapping_get_fd,              /* get_fd */
84
    mapping_map_access,          /* map_access */
85 86
    default_get_sd,              /* get_sd */
    default_set_sd,              /* set_sd */
87
    no_lookup_name,              /* lookup_name */
88
    no_open_file,                /* open_file */
89
    fd_close_handle,             /* close_handle */
90
    mapping_destroy              /* destroy */
91 92
};

93
static struct list shared_list = LIST_INIT(shared_list);
94

95 96 97 98 99
#ifdef __i386__

/* These are always the same on an i386, and it will be faster this way */
# define page_mask  0xfff
# define page_shift 12
100
# define init_page_size() do { /* nothing */ } while(0)
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125

#else  /* __i386__ */

static int page_shift, page_mask;

static void init_page_size(void)
{
    int page_size;
# ifdef HAVE_GETPAGESIZE
    page_size = getpagesize();
# else
#  ifdef __svr4__
    page_size = sysconf(_SC_PAGESIZE);
#  else
#   error Cannot get the page size on this platform
#  endif
# endif
    page_mask = page_size - 1;
    /* Make sure we have a power of 2 */
    assert( !(page_size & page_mask) );
    page_shift = 0;
    while ((1 << page_shift) != page_size) page_shift++;
}
#endif  /* __i386__ */

126
#define ROUND_SIZE(size)  (((size) + page_mask) & ~page_mask)
127 128


129 130 131 132 133
/* find the shared PE mapping for a given mapping */
static struct file *get_shared_file( struct mapping *mapping )
{
    struct mapping *ptr;

134
    LIST_FOR_EACH_ENTRY( ptr, &shared_list, struct mapping, shared_entry )
135 136 137 138 139
        if (is_same_file( ptr->file, mapping->file ))
            return (struct file *)grab_object( ptr->shared_file );
    return NULL;
}

140 141 142
/* return the size of the memory mapping and file range of a given section */
static inline void get_section_sizes( const IMAGE_SECTION_HEADER *sec, size_t *map_size,
                                      off_t *file_start, size_t *file_size )
143
{
144
    static const unsigned int sector_align = 0x1ff;
145

146 147 148 149 150 151
    if (!sec->Misc.VirtualSize) *map_size = ROUND_SIZE( sec->SizeOfRawData );
    else *map_size = ROUND_SIZE( sec->Misc.VirtualSize );

    *file_start = sec->PointerToRawData & ~sector_align;
    *file_size = (sec->SizeOfRawData + (sec->PointerToRawData & sector_align) + sector_align) & ~sector_align;
    if (*file_size > *map_size) *file_size = *map_size;
152 153
}

154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
/* add a range to the committed list */
static void add_committed_range( struct mapping *mapping, file_pos_t start, file_pos_t end )
{
    unsigned int i, j;
    struct range *ranges;

    if (!mapping->committed) return;  /* everything committed already */

    for (i = 0, ranges = mapping->committed->ranges; i < mapping->committed->count; i++)
    {
        if (ranges[i].start > end) break;
        if (ranges[i].end < start) continue;
        if (ranges[i].start > start) ranges[i].start = start;   /* extend downwards */
        if (ranges[i].end < end)  /* extend upwards and maybe merge with next */
        {
            for (j = i + 1; j < mapping->committed->count; j++)
            {
                if (ranges[j].start > end) break;
                if (ranges[j].end > end) end = ranges[j].end;
            }
            if (j > i + 1)
            {
                memmove( &ranges[i + 1], &ranges[j], (mapping->committed->count - j) * sizeof(*ranges) );
                mapping->committed->count -= j - (i + 1);
            }
            ranges[i].end = end;
        }
        return;
    }

    /* now add a new range */

    if (mapping->committed->count == mapping->committed->max)
    {
        unsigned int new_size = mapping->committed->max * 2;
        struct ranges *new_ptr = realloc( mapping->committed, offsetof( struct ranges, ranges[new_size] ));
        if (!new_ptr) return;
        new_ptr->max = new_size;
        ranges = new_ptr->ranges;
        mapping->committed = new_ptr;
    }
    memmove( &ranges[i + 1], &ranges[i], (mapping->committed->count - i) * sizeof(*ranges) );
    ranges[i].start = start;
    ranges[i].end = end;
    mapping->committed->count++;
}

/* find the range containing start and return whether it's committed */
202
static int find_committed_range( struct mapping *mapping, file_pos_t start, mem_size_t *size )
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
{
    unsigned int i;
    struct range *ranges;

    if (!mapping->committed)  /* everything is committed */
    {
        *size = mapping->size - start;
        return 1;
    }
    for (i = 0, ranges = mapping->committed->ranges; i < mapping->committed->count; i++)
    {
        if (ranges[i].start > start)
        {
            *size = ranges[i].start - start;
            return 0;
        }
        if (ranges[i].end > start)
        {
            *size = ranges[i].end - start;
            return 1;
        }
    }
    *size = mapping->size - start;
    return 0;
}

229 230
/* allocate and fill the temp file for a shared PE image mapping */
static int build_shared_mapping( struct mapping *mapping, int fd,
231
                                 IMAGE_SECTION_HEADER *sec, unsigned int nb_sec )
232
{
233
    unsigned int i;
234
    mem_size_t total_size;
235
    size_t file_size, map_size, max_size;
236
    off_t shared_pos, read_pos, write_pos;
237 238
    char *buffer = NULL;
    int shared_fd;
239
    long toread;
240 241 242 243 244 245 246 247 248

    /* compute the total size of the shared mapping */

    total_size = max_size = 0;
    for (i = 0; i < nb_sec; i++)
    {
        if ((sec[i].Characteristics & IMAGE_SCN_MEM_SHARED) &&
            (sec[i].Characteristics & IMAGE_SCN_MEM_WRITE))
        {
249 250 251
            get_section_sizes( &sec[i], &map_size, &read_pos, &file_size );
            if (file_size > max_size) max_size = file_size;
            total_size += map_size;
252 253
        }
    }
254
    if (!total_size) return 1;  /* nothing to do */
255

256 257
    if ((mapping->shared_file = get_shared_file( mapping ))) return 1;

258 259
    /* create a temp file for the mapping */

260
    if (!(mapping->shared_file = create_temp_file( FILE_GENERIC_READ|FILE_GENERIC_WRITE ))) return 0;
261
    if (!grow_file( mapping->shared_file, total_size )) goto error;
262
    if ((shared_fd = get_file_unix_fd( mapping->shared_file )) == -1) goto error;
263 264 265 266 267

    if (!(buffer = malloc( max_size ))) goto error;

    /* copy the shared sections data into the temp file */

268 269
    shared_pos = 0;
    for (i = 0; i < nb_sec; i++)
270 271 272
    {
        if (!(sec[i].Characteristics & IMAGE_SCN_MEM_SHARED)) continue;
        if (!(sec[i].Characteristics & IMAGE_SCN_MEM_WRITE)) continue;
273
        get_section_sizes( &sec[i], &map_size, &read_pos, &file_size );
274
        write_pos = shared_pos;
275 276 277
        shared_pos += map_size;
        if (!sec[i].PointerToRawData || !file_size) continue;
        toread = file_size;
278 279
        while (toread)
        {
280
            long res = pread( fd, buffer + file_size - toread, toread, read_pos );
281 282 283 284 285
            if (!res && toread < 0x200)  /* partial sector at EOF is not an error */
            {
                file_size -= toread;
                break;
            }
286 287
            if (res <= 0) goto error;
            toread -= res;
288
            read_pos += res;
289
        }
290
        if (pwrite( shared_fd, buffer, file_size, write_pos ) != file_size) goto error;
291 292 293 294 295
    }
    free( buffer );
    return 1;

 error:
296 297
    release_object( mapping->shared_file );
    mapping->shared_file = NULL;
298
    free( buffer );
299 300 301 302 303 304 305 306 307
    return 0;
}

/* retrieve the mapping parameters for an executable (PE) image */
static int get_image_params( struct mapping *mapping )
{
    IMAGE_DOS_HEADER dos;
    IMAGE_NT_HEADERS nt;
    IMAGE_SECTION_HEADER *sec = NULL;
308
    struct fd *fd;
309
    off_t pos;
310
    int unix_fd, size, toread;
311 312 313

    /* load the headers */

314
    if (!(fd = mapping_get_fd( &mapping->obj ))) return 0;
315
    if ((unix_fd = get_unix_fd( fd )) == -1) goto error;
316
    if (pread( unix_fd, &dos, sizeof(dos), 0 ) != sizeof(dos)) goto error;
317
    if (dos.e_magic != IMAGE_DOS_SIGNATURE) goto error;
318
    pos = dos.e_lfanew;
319

320 321 322
    if (pread( unix_fd, &nt.Signature, sizeof(nt.Signature), pos ) != sizeof(nt.Signature))
        goto error;
    pos += sizeof(nt.Signature);
323
    if (nt.Signature != IMAGE_NT_SIGNATURE) goto error;
324 325 326
    if (pread( unix_fd, &nt.FileHeader, sizeof(nt.FileHeader), pos ) != sizeof(nt.FileHeader))
        goto error;
    pos += sizeof(nt.FileHeader);
327 328
    /* zero out Optional header in the case it's not present or partial */
    memset(&nt.OptionalHeader, 0, sizeof(nt.OptionalHeader));
329 330
    toread = min( sizeof(nt.OptionalHeader), nt.FileHeader.SizeOfOptionalHeader );
    if (pread( unix_fd, &nt.OptionalHeader, toread, pos ) != toread) goto error;
331
    pos += nt.FileHeader.SizeOfOptionalHeader;
332 333 334 335 336

    /* load the section headers */

    size = sizeof(*sec) * nt.FileHeader.NumberOfSections;
    if (!(sec = malloc( size ))) goto error;
337
    if (pread( unix_fd, sec, size, pos ) != size) goto error;
338

339
    if (!build_shared_mapping( mapping, unix_fd, sec, nt.FileHeader.NumberOfSections )) goto error;
340

341
    if (mapping->shared_file) list_add_head( &shared_list, &mapping->shared_entry );
342

343
    mapping->size        = ROUND_SIZE( nt.OptionalHeader.SizeOfImage );
344
    mapping->base        = nt.OptionalHeader.ImageBase;
345
    mapping->header_size = max( pos + size, nt.OptionalHeader.SizeOfHeaders );
346 347 348
    mapping->protect     = VPROT_IMAGE;

    /* sanity check */
349
    if (pos + size > mapping->size) goto error;
350 351

    free( sec );
352
    release_object( fd );
353 354 355
    return 1;

 error:
356
    free( sec );
357
    release_object( fd );
358 359 360 361
    set_error( STATUS_INVALID_FILE_FOR_SECTION );
    return 0;
}

362
/* get the size of the unix file associated with the mapping */
363
static inline int get_file_size( struct file *file, mem_size_t *size )
364 365
{
    struct stat st;
366
    int unix_fd = get_file_unix_fd( file );
367

368
    if (unix_fd == -1 || fstat( unix_fd, &st ) == -1) return 0;
369
    *size = st.st_size;
370 371
    return 1;
}
372

373
static struct object *create_mapping( struct directory *root, const struct unicode_str *name,
374
                                      unsigned int attr, mem_size_t size, int protect,
375
                                      obj_handle_t handle, const struct security_descriptor *sd )
376 377
{
    struct mapping *mapping;
378 379 380
    int access = 0;

    if (!page_mask) init_page_size();
381

382
    if (!(mapping = create_named_object_dir( root, name, attr, &mapping_ops )))
383
        return NULL;
384
    if (get_error() == STATUS_OBJECT_NAME_EXISTS)
385 386
        return &mapping->obj;  /* Nothing else to do */

387 388 389 390
    if (sd) default_set_sd( &mapping->obj, sd, OWNER_SECURITY_INFORMATION|
                                               GROUP_SECURITY_INFORMATION|
                                               DACL_SECURITY_INFORMATION|
                                               SACL_SECURITY_INFORMATION );
391
    mapping->header_size = 0;
392
    mapping->base        = 0;
393
    mapping->file        = NULL;
394
    mapping->shared_file = NULL;
395
    mapping->committed   = NULL;
396

397 398
    if (protect & VPROT_READ) access |= FILE_READ_DATA;
    if (protect & VPROT_WRITE) access |= FILE_WRITE_DATA;
399

400
    if (handle)
401
    {
402 403 404 405 406
        if (!(protect & VPROT_COMMITTED))
        {
            set_error( STATUS_INVALID_PARAMETER );
            goto error;
        }
407
        if (!(mapping->file = get_file_obj( current->process, handle, access ))) goto error;
408 409 410 411 412
        if (protect & VPROT_IMAGE)
        {
            if (!get_image_params( mapping )) goto error;
            return &mapping->obj;
        }
413
        if (!size)
414
        {
415 416
            if (!get_file_size( mapping->file, &size )) goto error;
            if (!size)
417
            {
418
                set_error( STATUS_MAPPED_FILE_SIZE_ZERO );
419 420
                goto error;
            }
421 422 423
        }
        else
        {
424
            if (!grow_file( mapping->file, size )) goto error;
425 426
        }
    }
427 428
    else  /* Anonymous mapping (no associated file) */
    {
429
        if (!size || (protect & VPROT_IMAGE))
430
        {
431
            set_error( STATUS_INVALID_PARAMETER );
432 433
            goto error;
        }
434 435 436 437 438 439
        if (!(protect & VPROT_COMMITTED))
        {
            if (!(mapping->committed = mem_alloc( offsetof(struct ranges, ranges[8]) ))) goto error;
            mapping->committed->count = 0;
            mapping->committed->max   = 8;
        }
440
        if (!(mapping->file = create_temp_file( access ))) goto error;
441
        if (!grow_file( mapping->file, size )) goto error;
442
    }
443
    mapping->size    = (size + page_mask) & ~((mem_size_t)page_mask);
444
    mapping->protect = protect;
445
    return &mapping->obj;
446 447 448 449

 error:
    release_object( mapping );
    return NULL;
450 451 452 453 454 455
}

static void mapping_dump( struct object *obj, int verbose )
{
    struct mapping *mapping = (struct mapping *)obj;
    assert( obj->ops == &mapping_ops );
456
    fprintf( stderr, "Mapping size=%08x%08x prot=%08x file=%p header_size=%08x base=%08lx "
457
             "shared_file=%p ",
458 459
             (unsigned int)(mapping->size >> 32), (unsigned int)mapping->size,
             mapping->protect, mapping->file, mapping->header_size,
460
             (unsigned long)mapping->base, mapping->shared_file );
461 462
    dump_object_name( &mapping->obj );
    fputc( '\n', stderr );
463 464
}

465 466 467 468 469 470 471
static struct object_type *mapping_get_type( struct object *obj )
{
    static const WCHAR name[] = {'S','e','c','t','i','o','n'};
    static const struct unicode_str str = { name, sizeof(name) };
    return get_object_type( &str );
}

472 473 474 475 476 477
static struct fd *mapping_get_fd( struct object *obj )
{
    struct mapping *mapping = (struct mapping *)obj;
    return get_obj_fd( (struct object *)mapping->file );
}

478 479 480 481 482 483 484 485 486
static unsigned int mapping_map_access( struct object *obj, unsigned int access )
{
    if (access & GENERIC_READ)    access |= STANDARD_RIGHTS_READ | SECTION_QUERY | SECTION_MAP_READ;
    if (access & GENERIC_WRITE)   access |= STANDARD_RIGHTS_WRITE | SECTION_MAP_WRITE;
    if (access & GENERIC_EXECUTE) access |= STANDARD_RIGHTS_EXECUTE | SECTION_MAP_EXECUTE;
    if (access & GENERIC_ALL)     access |= SECTION_ALL_ACCESS;
    return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
}

487 488 489 490 491
static void mapping_destroy( struct object *obj )
{
    struct mapping *mapping = (struct mapping *)obj;
    assert( obj->ops == &mapping_ops );
    if (mapping->file) release_object( mapping->file );
492 493 494
    if (mapping->shared_file)
    {
        release_object( mapping->shared_file );
495
        list_remove( &mapping->shared_entry );
496
    }
497
    free( mapping->committed );
498
}
499

500 501 502 503 504 505
int get_page_size(void)
{
    if (!page_mask) init_page_size();
    return page_mask + 1;
}

506 507 508 509
/* create a file mapping */
DECL_HANDLER(create_mapping)
{
    struct object *obj;
510
    struct unicode_str name;
511
    struct directory *root = NULL;
512 513
    const struct object_attributes *objattr = get_req_data();
    const struct security_descriptor *sd;
514

515
    reply->handle = 0;
516 517 518 519 520

    if (!objattr_is_valid( objattr, get_req_data_size() ))
        return;

    sd = objattr->sd_len ? (const struct security_descriptor *)(objattr + 1) : NULL;
521
    objattr_get_name( objattr, &name );
522 523

    if (objattr->rootdir && !(root = get_directory_obj( current->process, objattr->rootdir, 0 )))
524 525
        return;

526
    if ((obj = create_mapping( root, &name, req->attributes, req->size, req->protect, req->file_handle, sd )))
527
    {
528 529 530 531
        if (get_error() == STATUS_OBJECT_NAME_EXISTS)
            reply->handle = alloc_handle( current->process, obj, req->access, req->attributes );
        else
            reply->handle = alloc_handle_no_access_check( current->process, obj, req->access, req->attributes );
532 533
        release_object( obj );
    }
534 535

    if (root) release_object( root );
536 537 538 539 540
}

/* open a handle to a mapping */
DECL_HANDLER(open_mapping)
{
541
    struct unicode_str name;
542
    struct directory *root = NULL;
543
    struct mapping *mapping;
544 545

    get_req_unicode_str( &name );
546 547 548
    if (req->rootdir && !(root = get_directory_obj( current->process, req->rootdir, 0 )))
        return;

549 550
    if ((mapping = open_object_dir( root, &name, req->attributes, &mapping_ops )))
    {
551
        reply->handle = alloc_handle( current->process, &mapping->obj, req->access, req->attributes );
552 553
        release_object( mapping );
    }
554 555

    if (root) release_object( root );
556 557 558 559 560
}

/* get a mapping information */
DECL_HANDLER(get_mapping_info)
{
561
    struct mapping *mapping;
562
    struct fd *fd;
563 564

    if ((mapping = (struct mapping *)get_handle_obj( current->process, req->handle,
565
                                                     req->access, &mapping_ops )))
566
    {
567
        reply->size        = mapping->size;
568 569 570 571
        reply->protect     = mapping->protect;
        reply->header_size = mapping->header_size;
        reply->base        = mapping->base;
        reply->shared_file = 0;
572 573 574 575 576 577
        if ((fd = get_obj_fd( &mapping->obj )))
        {
            if (!is_fd_removable(fd))
                reply->mapping = alloc_handle( current->process, mapping, 0, 0 );
            release_object( fd );
        }
578
        if (mapping->shared_file)
579 580 581 582 583 584 585
        {
            if (!(reply->shared_file = alloc_handle( current->process, mapping->shared_file,
                                                     GENERIC_READ|GENERIC_WRITE, 0 )))
            {
                if (reply->mapping) close_handle( current->process, reply->mapping );
            }
        }
586 587
        release_object( mapping );
    }
588
}
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624

/* get a range of committed pages in a file mapping */
DECL_HANDLER(get_mapping_committed_range)
{
    struct mapping *mapping;

    if ((mapping = (struct mapping *)get_handle_obj( current->process, req->handle, 0, &mapping_ops )))
    {
        if (!(req->offset & page_mask) && req->offset < mapping->size)
            reply->committed = find_committed_range( mapping, req->offset, &reply->size );
        else
            set_error( STATUS_INVALID_PARAMETER );

        release_object( mapping );
    }
}

/* add a range to the committed pages in a file mapping */
DECL_HANDLER(add_mapping_committed_range)
{
    struct mapping *mapping;

    if ((mapping = (struct mapping *)get_handle_obj( current->process, req->handle, 0, &mapping_ops )))
    {
        if (!(req->size & page_mask) &&
            !(req->offset & page_mask) &&
            req->offset < mapping->size &&
            req->size > 0 &&
            req->size <= mapping->size - req->offset)
            add_committed_range( mapping, req->offset, req->offset + req->size );
        else
            set_error( STATUS_INVALID_PARAMETER );

        release_object( mapping );
    }
}