1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
3 
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #include <config.h>
18 
19 #include "data/file-handle-def.h"
20 
21 #include <assert.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include "data/dataset.h"
27 #include "data/variable.h"
28 #include "libpspp/cast.h"
29 #include "libpspp/compiler.h"
30 #include "libpspp/hash-functions.h"
31 #include "libpspp/hmap.h"
32 #include "libpspp/i18n.h"
33 #include "libpspp/message.h"
34 #include "libpspp/str.h"
35 
36 #include <sys/stat.h>
37 
38 #include "gl/dirname.h"
39 #include "gl/xalloc.h"
40 
41 #include "gettext.h"
42 #define _(msgid) gettext (msgid)
43 
44 #if defined _WIN32 || defined __WIN32__
45 #define WIN32_LEAN_AND_MEAN  /* avoid including junk */
46 #include <windows.h>
47 #endif
48 
49 /* File handle. */
50 struct file_handle
51   {
52     struct hmap_node name_node; /* Element in named_handles hmap. */
53     size_t ref_cnt;             /* Number of references. */
54     char *id;                   /* Identifier token, NULL if none. */
55     char *name;                 /* User-friendly identifying name. */
56     enum fh_referent referent;  /* What the file handle refers to. */
57 
58     /* FH_REF_FILE only. */
59     char *file_name;		/* File name as provided by user. */
60     char *file_name_encoding;	/* The character encoding of file_name,
61 				   This is NOT the encoding of the file contents! */
62     enum fh_mode mode;  	/* File mode. */
63     enum fh_line_ends line_ends; /* Line ends for text files. */
64 
65     /* FH_REF_FILE and FH_REF_INLINE only. */
66     size_t record_width;        /* Length of fixed-format records. */
67     size_t tab_width;           /* Tab width, 0=do not expand tabs. */
68     char *encoding;             /* Charset for contents. */
69 
70     /* FH_REF_DATASET only. */
71     struct dataset *ds;         /* Dataset. */
72   };
73 
74 /* All "struct file_handle"s with nonnull 'id' member. */
75 static struct hmap named_handles = HMAP_INITIALIZER (named_handles);
76 
77 /* Default file handle for DATA LIST, REREAD, REPEATING DATA
78    commands. */
79 static struct file_handle *default_handle;
80 
81 /* The "file" that reads from BEGIN DATA...END DATA. */
82 static struct file_handle *inline_file;
83 
84 static struct file_handle *create_handle (const char *id,
85                                           char *name, enum fh_referent,
86                                           const char *encoding);
87 static void free_handle (struct file_handle *);
88 static void unname_handle (struct file_handle *);
89 
90 /* Hash table of all active locks. */
91 static struct hmap locks = HMAP_INITIALIZER (locks);
92 
93 static struct file_identity *fh_get_identity (const struct file_handle *);
94 static void fh_free_identity (struct file_identity *);
95 static int fh_compare_file_identities (const struct file_identity *,
96                                 const struct file_identity *);
97 static unsigned int fh_hash_identity (const struct file_identity *);
98 
99 /* File handle initialization routine. */
100 void
fh_init(void)101 fh_init (void)
102 {
103   inline_file = create_handle ("INLINE", xstrdup ("INLINE"), FH_REF_INLINE,
104                                "Auto");
105   inline_file->record_width = 80;
106   inline_file->tab_width = 8;
107 }
108 
109 /* Removes all named file handles from the global list. */
110 void
fh_done(void)111 fh_done (void)
112 {
113   struct file_handle *handle, *next;
114 
115   HMAP_FOR_EACH_SAFE (handle, next,
116                       struct file_handle, name_node, &named_handles)
117     unname_handle (handle);
118 
119   free_handle (inline_file);
120 }
121 
122 /* Free HANDLE and remove it from the global list. */
123 static void
free_handle(struct file_handle * handle)124 free_handle (struct file_handle *handle)
125 {
126   if (handle == NULL)
127     return;
128 
129   /* Remove handle from global list. */
130   if (handle->id != NULL)
131     hmap_delete (&named_handles, &handle->name_node);
132 
133   /* Free data. */
134   free (handle->id);
135   free (handle->name);
136   free (handle->file_name);
137   free (handle->file_name_encoding);
138   free (handle->encoding);
139   free (handle);
140 }
141 
142 /* Make HANDLE unnamed, so that it can no longer be referenced by
143    name.  The caller must hold a reference to HANDLE, which is
144    not affected by this function. */
145 static void
unname_handle(struct file_handle * handle)146 unname_handle (struct file_handle *handle)
147 {
148   assert (handle->id != NULL);
149   free (handle->id);
150   handle->id = NULL;
151   hmap_delete (&named_handles, &handle->name_node);
152 
153   /* Drop the reference held by the named_handles table. */
154   fh_unref (handle);
155 }
156 
157 /* Increments HANDLE's reference count and returns HANDLE. */
158 struct file_handle *
fh_ref(struct file_handle * handle)159 fh_ref (struct file_handle *handle)
160 {
161   if (handle == fh_inline_file ())
162     return handle;
163   assert (handle->ref_cnt > 0);
164   handle->ref_cnt++;
165   return handle;
166 }
167 
168 /* Decrements HANDLE's reference count.
169    If the reference count drops to 0, HANDLE is destroyed. */
170 void
fh_unref(struct file_handle * handle)171 fh_unref (struct file_handle *handle)
172 {
173   if (handle != NULL)
174     {
175       if (handle == fh_inline_file ())
176         return;
177       assert (handle->ref_cnt > 0);
178       if (--handle->ref_cnt == 0)
179         free_handle (handle);
180     }
181 }
182 
183 /* Make HANDLE unnamed, so that it can no longer be referenced by
184    name.  The caller must hold a reference to HANDLE, which is
185    not affected by this function.
186 
187    This function ignores a null pointer as input.  It has no
188    effect on the inline handle, which is always named INLINE.*/
189 void
fh_unname(struct file_handle * handle)190 fh_unname (struct file_handle *handle)
191 {
192   assert (handle->ref_cnt > 1);
193   if (handle != fh_inline_file () && handle->id != NULL)
194     unname_handle (handle);
195 }
196 
197 /* Returns the handle with the given ID, or a null pointer if
198    there is none. */
199 struct file_handle *
fh_from_id(const char * id)200 fh_from_id (const char *id)
201 {
202   struct file_handle *handle;
203 
204   HMAP_FOR_EACH_WITH_HASH (handle, struct file_handle, name_node,
205                            utf8_hash_case_string (id, 0), &named_handles)
206     if (!utf8_strcasecmp (id, handle->id))
207       {
208 	return fh_ref (handle);
209       }
210 
211   return NULL;
212 }
213 
214 /* Creates a new handle with identifier ID (which may be null)
215    and name HANDLE_NAME that refers to REFERENT.  Links the new
216    handle into the global list.  Returns the new handle.
217 
218    The new handle is not fully initialized.  The caller is
219    responsible for completing its initialization. */
220 static struct file_handle *
create_handle(const char * id,char * handle_name,enum fh_referent referent,const char * encoding)221 create_handle (const char *id, char *handle_name, enum fh_referent referent,
222                const char *encoding)
223 {
224   struct file_handle *handle = xzalloc (sizeof *handle);
225 
226   handle->ref_cnt = 1;
227   handle->id = id != NULL ? xstrdup (id) : NULL;
228   handle->name = handle_name;
229   handle->referent = referent;
230   handle->encoding = xstrdup (encoding);
231 
232   if (id != NULL)
233     {
234       hmap_insert (&named_handles, &handle->name_node,
235                    utf8_hash_case_string (handle->id, 0));
236     }
237 
238   return handle;
239 }
240 
241 /* Returns the unique handle of referent type FH_REF_INLINE,
242    which refers to the "inline file" that represents character
243    data in the command file between BEGIN DATA and END DATA. */
244 struct file_handle *
fh_inline_file(void)245 fh_inline_file (void)
246 {
247   return inline_file;
248 }
249 
250 /* Creates and returns a new file handle with the given ID, which may be null.
251    If it is non-null, it must be a UTF-8 encoded string that is unique among
252    existing file identifiers.  The new handle is associated with file FILE_NAME
253    and the given PROPERTIES. */
254 struct file_handle *
fh_create_file(const char * id,const char * file_name,const char * file_name_encoding,const struct fh_properties * properties)255 fh_create_file (const char *id, const char *file_name, const char *file_name_encoding,
256                 const struct fh_properties *properties)
257 {
258   char *handle_name;
259   struct file_handle *handle;
260 
261   handle_name = id != NULL ? xstrdup (id) : xasprintf ("`%s'", file_name);
262   handle = create_handle (id, handle_name, FH_REF_FILE, properties->encoding);
263   handle->file_name = xstrdup (file_name);
264   handle->file_name_encoding = file_name_encoding ? xstrdup (file_name_encoding) : NULL;
265   handle->mode = properties->mode;
266   handle->line_ends = properties->line_ends;
267   handle->record_width = properties->record_width;
268   handle->tab_width = properties->tab_width;
269   return handle;
270 }
271 
272 /* Creates a new file handle with the given ID, which must be
273    unique among existing file identifiers.  The new handle is
274    associated with a dataset file (initially empty). */
275 struct file_handle *
fh_create_dataset(struct dataset * ds)276 fh_create_dataset (struct dataset *ds)
277 {
278   const char *name;
279   struct file_handle *handle;
280 
281   name = dataset_name (ds);
282   if (name[0] == '\0')
283     name = _("active dataset");
284 
285   handle = create_handle (NULL, xstrdup (name), FH_REF_DATASET, C_ENCODING);
286   handle->ds = ds;
287   return handle;
288 }
289 
290 /* Returns a set of default properties for a file handle. */
291 const struct fh_properties *
fh_default_properties(void)292 fh_default_properties (void)
293 {
294 #if defined _WIN32 || defined __WIN32__
295 #define DEFAULT_LINE_ENDS FH_END_CRLF
296 #else
297 #define DEFAULT_LINE_ENDS FH_END_LF
298 #endif
299 
300   static const struct fh_properties default_properties
301     = {FH_MODE_TEXT, DEFAULT_LINE_ENDS, 1024, 4, (char *) "Auto"};
302   return &default_properties;
303 }
304 
305 /* Returns the identifier that may be used in syntax to name the
306    given HANDLE, which takes the form of a PSPP identifier.  If
307    HANDLE has no identifier, returns a null pointer.
308 
309    Return value is owned by the file handle.*/
310 const char *
fh_get_id(const struct file_handle * handle)311 fh_get_id (const struct file_handle *handle)
312 {
313   return handle->id;
314 }
315 
316 /* Returns a user-friendly string to identify the given HANDLE.
317    If HANDLE was created by referring to a file name, returns the
318    file name, enclosed in double quotes.  Return value is owned
319    by the file handle.
320 
321    Useful for printing error messages about use of file handles.  */
322 const char *
fh_get_name(const struct file_handle * handle)323 fh_get_name (const struct file_handle *handle)
324 {
325   return handle->name;
326 }
327 
328 /* Returns the type of object that HANDLE refers to. */
329 enum fh_referent
fh_get_referent(const struct file_handle * handle)330 fh_get_referent (const struct file_handle *handle)
331 {
332   return handle->referent;
333 }
334 
335 /* Returns the name of the file associated with HANDLE. */
336 const char *
fh_get_file_name(const struct file_handle * handle)337 fh_get_file_name (const struct file_handle *handle)
338 {
339   assert (handle->referent == FH_REF_FILE);
340   return handle->file_name;
341 }
342 
343 
344 /* Returns the character encoding of the name of the file associated with HANDLE. */
345 const char *
fh_get_file_name_encoding(const struct file_handle * handle)346 fh_get_file_name_encoding (const struct file_handle *handle)
347 {
348   assert (handle->referent == FH_REF_FILE);
349   return handle->file_name_encoding;
350 }
351 
352 
353 /* Returns the mode of HANDLE. */
354 enum fh_mode
fh_get_mode(const struct file_handle * handle)355 fh_get_mode (const struct file_handle *handle)
356 {
357   assert (handle->referent == FH_REF_FILE);
358   return handle->mode;
359 }
360 
361 /* Returns the line ends of HANDLE, which must be a handle associated with a
362    file. */
363 enum fh_line_ends
fh_get_line_ends(const struct file_handle * handle)364 fh_get_line_ends (const struct file_handle *handle)
365 {
366   assert (handle->referent == FH_REF_FILE);
367   return handle->line_ends;
368 }
369 
370 /* Returns the width of a logical record on HANDLE. */
371 size_t
fh_get_record_width(const struct file_handle * handle)372 fh_get_record_width (const struct file_handle *handle)
373 {
374   assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
375   return handle->record_width;
376 }
377 
378 /* Returns the number of characters per tab stop for HANDLE, or
379    zero if tabs are not to be expanded.  Applicable only to
380    FH_MODE_TEXT files. */
381 size_t
fh_get_tab_width(const struct file_handle * handle)382 fh_get_tab_width (const struct file_handle *handle)
383 {
384   assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
385   return handle->tab_width;
386 }
387 
388 /* Returns the encoding of characters read from HANDLE. */
389 const char *
fh_get_encoding(const struct file_handle * handle)390 fh_get_encoding (const struct file_handle *handle)
391 {
392   return handle->encoding;
393 }
394 
395 /* Returns the dataset handle associated with HANDLE.
396    Applicable to only FH_REF_DATASET files. */
397 struct dataset *
fh_get_dataset(const struct file_handle * handle)398 fh_get_dataset (const struct file_handle *handle)
399 {
400   assert (handle->referent == FH_REF_DATASET);
401   return handle->ds;
402 }
403 
404 /* Returns the current default handle. */
405 struct file_handle *
fh_get_default_handle(void)406 fh_get_default_handle (void)
407 {
408   return default_handle ? default_handle : fh_inline_file ();
409 }
410 
411 /* Sets NEW_DEFAULT_HANDLE as the default handle. */
412 void
fh_set_default_handle(struct file_handle * new_default_handle)413 fh_set_default_handle (struct file_handle *new_default_handle)
414 {
415   assert (new_default_handle == NULL
416           || (new_default_handle->referent & (FH_REF_INLINE | FH_REF_FILE)));
417   if (default_handle != NULL && default_handle != inline_file)
418     fh_unref (default_handle);
419   default_handle = new_default_handle;
420   if (default_handle != NULL)
421     default_handle = fh_ref (default_handle);
422 }
423 
424 /* Information about a file handle's readers or writers. */
425 struct fh_lock
426   {
427     struct hmap_node node;      /* hmap_node member. */
428 
429     /* Hash key. */
430     enum fh_referent referent;  /* Type of underlying file. */
431     union
432       {
433         struct file_identity *file; /* FH_REF_FILE only. */
434         unsigned int unique_id;    /* FH_REF_DATASET only. */
435       }
436     u;
437     enum fh_access access;      /* Type of file access. */
438 
439     /* Number of openers. */
440     size_t open_cnt;
441 
442     /* Applicable only when open_cnt > 0. */
443     bool exclusive;             /* No other openers allowed? */
444     const char *type;           /* Human-readable type of file. */
445     void *aux;                  /* Owner's auxiliary data. */
446   };
447 
448 
449 static void make_key (struct fh_lock *, const struct file_handle *,
450                       enum fh_access);
451 static void free_key (struct fh_lock *);
452 static int compare_fh_locks (const struct fh_lock *a, const struct fh_lock *b);
453 static unsigned int hash_fh_lock (const struct fh_lock *lock);
454 
455 /* Tries to lock handle H for the given kind of ACCESS and TYPE
456    of file.  Returns a pointer to a struct fh_lock if successful,
457    otherwise a null pointer.
458 
459    H's referent type must be one of the bits in MASK.  The caller
460    must verify this ahead of time; we simply assert it here.
461 
462    TYPE is the sort of file, e.g. "system file".  Only one type
463    of access is allowed on a given file at a time for reading,
464    and similarly for writing.  If successful, a reference to TYPE
465    is retained, so it should probably be a string literal.
466 
467    TYPE should be marked with N_() in the caller: that is, the
468    caller should not translate it with gettext, but fh_lock will
469    do so.
470 
471    ACCESS specifies whether the lock is for reading or writing.
472    EXCLUSIVE is true to require exclusive access, false to allow
473    sharing with other accessors.  Exclusive read access precludes
474    other readers, but not writers; exclusive write access
475    precludes other writers, but not readers.  A sharable read or
476    write lock precludes reader or writers, respectively, of a
477    different TYPE.
478 
479    A lock may be associated with auxiliary data.  See
480    fh_lock_get_aux and fh_lock_set_aux for more details. */
481 struct fh_lock *
fh_lock(struct file_handle * h,enum fh_referent mask UNUSED,const char * type,enum fh_access access,bool exclusive)482 fh_lock (struct file_handle *h, enum fh_referent mask UNUSED,
483          const char *type, enum fh_access access, bool exclusive)
484 {
485   struct fh_lock *key = NULL;
486   size_t hash ;
487   struct fh_lock *lock = NULL;
488   bool found_lock = false;
489 
490   assert ((fh_get_referent (h) & mask) != 0);
491   assert (access == FH_ACC_READ || access == FH_ACC_WRITE);
492 
493   key = xmalloc (sizeof *key);
494 
495   make_key (key, h, access);
496 
497   key->open_cnt = 1;
498   key->exclusive = exclusive;
499   key->type = type;
500   key->aux = NULL;
501 
502   hash = hash_fh_lock (key);
503 
504   HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks)
505     {
506       if (0 == compare_fh_locks (lock, key))
507 	{
508 	  found_lock = true;
509 	  break;
510 	}
511     }
512 
513   if (found_lock)
514     {
515       if (strcmp (lock->type, type))
516         {
517           if (access == FH_ACC_READ)
518             msg (SE, _("Can't read from %s as a %s because it is "
519                        "already being read as a %s."),
520                  fh_get_name (h), gettext (type), gettext (lock->type));
521           else
522             msg (SE, _("Can't write to %s as a %s because it is "
523                        "already being written as a %s."),
524                  fh_get_name (h), gettext (type), gettext (lock->type));
525           return NULL;
526         }
527       else if (exclusive || lock->exclusive)
528         {
529           msg (SE, _("Can't re-open %s as a %s."),
530                fh_get_name (h), gettext (type));
531           return NULL;
532         }
533       lock->open_cnt++;
534 
535       free_key (key);
536       free (key);
537 
538       return lock;
539     }
540 
541   hmap_insert (&locks, &key->node, hash);
542   found_lock = false;
543   HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks)
544     {
545       if (0 == compare_fh_locks (lock, key))
546 	{
547 	  found_lock = true;
548 	  break;
549 	}
550     }
551 
552   assert (found_lock);
553 
554   return key;
555 }
556 
557 /* Releases LOCK that was acquired with fh_lock.
558    Returns true if LOCK is still locked, because other clients
559    also had it locked.
560 
561    Returns false if LOCK has now been destroyed.  In this case
562    the caller must ensure that any auxiliary data associated with
563    LOCK is destroyed, to avoid a memory leak.  The caller must
564    obtain a pointer to the auxiliary data, e.g. via
565    fh_lock_get_aux *before* calling fh_unlock (because it yields
566    undefined behavior to call fh_lock_get_aux on a destroyed
567    lock).  */
568 bool
fh_unlock(struct fh_lock * lock)569 fh_unlock (struct fh_lock *lock)
570 {
571   if (lock != NULL)
572     {
573       assert (lock->open_cnt > 0);
574       if (--lock->open_cnt == 0)
575         {
576 	  hmap_delete (&locks, &lock->node);
577           free_key (lock);
578           free (lock);
579           return false;
580         }
581     }
582   return true;
583 }
584 
585 /* Returns auxiliary data for LOCK.
586 
587    Auxiliary data is shared by every client that holds LOCK (for
588    an exclusive lock, this is a single client).  To avoid leaks,
589    auxiliary data must be released before LOCK is destroyed. */
590 void *
fh_lock_get_aux(const struct fh_lock * lock)591 fh_lock_get_aux (const struct fh_lock *lock)
592 {
593   return lock->aux;
594 }
595 
596 /* Sets the auxiliary data for LOCK to AUX. */
597 void
fh_lock_set_aux(struct fh_lock * lock,void * aux)598 fh_lock_set_aux (struct fh_lock *lock, void *aux)
599 {
600   lock->aux = aux;
601 }
602 
603 /* Returns true if HANDLE is locked for the given type of ACCESS,
604    false otherwise. */
605 bool
fh_is_locked(const struct file_handle * handle,enum fh_access access)606 fh_is_locked (const struct file_handle *handle, enum fh_access access)
607 {
608   struct fh_lock key;
609   const struct fh_lock *k = NULL;
610   bool is_locked = false;
611   size_t hash ;
612 
613   make_key (&key, handle, access);
614 
615   hash = hash_fh_lock (&key);
616 
617 
618   HMAP_FOR_EACH_WITH_HASH (k, struct fh_lock, node, hash, &locks)
619     {
620       if (0 == compare_fh_locks (k, &key))
621 	{
622 	  is_locked = true;
623 	  break;
624 	}
625     }
626 
627   free_key (&key);
628 
629   return is_locked;
630 }
631 
632 /* Initializes the key fields in LOCK for looking up or inserting
633    handle H for the given kind of ACCESS. */
634 static void
make_key(struct fh_lock * lock,const struct file_handle * h,enum fh_access access)635 make_key (struct fh_lock *lock, const struct file_handle *h,
636           enum fh_access access)
637 {
638   lock->referent = fh_get_referent (h);
639   lock->access = access;
640   if (lock->referent == FH_REF_FILE)
641     lock->u.file = fh_get_identity (h);
642   else if (lock->referent == FH_REF_DATASET)
643     lock->u.unique_id = dataset_seqno (fh_get_dataset (h));
644 }
645 
646 /* Frees the key fields in LOCK. */
647 static void
free_key(struct fh_lock * lock)648 free_key (struct fh_lock *lock)
649 {
650   if (lock->referent == FH_REF_FILE)
651     fh_free_identity (lock->u.file);
652 }
653 
654 /* Compares the key fields in struct fh_lock objects A and B and
655    returns a strcmp()-type result. */
656 static int
compare_fh_locks(const struct fh_lock * a,const struct fh_lock * b)657 compare_fh_locks (const struct fh_lock *a, const struct fh_lock *b)
658 {
659   if (a->referent != b->referent)
660     return a->referent < b->referent ? -1 : 1;
661   else if (a->access != b->access)
662     return a->access < b->access ? -1 : 1;
663   else if (a->referent == FH_REF_FILE)
664     return fh_compare_file_identities (a->u.file, b->u.file);
665   else if (a->referent == FH_REF_DATASET)
666     return (a->u.unique_id < b->u.unique_id ? -1
667             : a->u.unique_id > b->u.unique_id);
668   else
669     return 0;
670 }
671 
672 /* Returns a hash value for LOCK. */
673 static unsigned int
hash_fh_lock(const struct fh_lock * lock)674 hash_fh_lock (const struct fh_lock *lock)
675 {
676   unsigned int basis;
677   if (lock->referent == FH_REF_FILE)
678     basis = fh_hash_identity (lock->u.file);
679   else if (lock->referent == FH_REF_DATASET)
680     basis = lock->u.unique_id;
681   else
682     basis = 0;
683   return hash_int ((lock->referent << 3) | lock->access, basis);
684 }
685 
686 
687 
688 
689 
690 
691 /* A file's identity:
692 
693    - For a file that exists, this is its device and inode.
694 
695    - For a file that does not exist, but which has a directory
696      name that exists, this is the device and inode of the
697      directory, plus the file's base name.
698 
699    - For a file that does not exist and has a nonexistent
700      directory, this is the file name.
701 
702    Windows doesn't have inode numbers, so we just use the name
703    there. */
704 struct file_identity
705 {
706   unsigned long long device;               /* Device number. */
707   unsigned long long inode;                /* Inode number. */
708   char *name;                 /* File name, where needed, otherwise NULL. */
709 };
710 
711 /* Returns a pointer to a dynamically allocated structure whose
712    value can be used to tell whether two files are actually the
713    same file.  The caller is responsible for freeing the structure with
714    fh_free_identity() when finished. */
715 static struct file_identity *
fh_get_identity(const struct file_handle * fh)716 fh_get_identity (const struct file_handle *fh)
717 {
718   struct file_identity *identity = xmalloc (sizeof *identity);
719 
720   const char *file_name = fh_get_file_name (fh);
721 
722 #if !(defined _WIN32 || defined __WIN32__)
723   struct stat s;
724   if (lstat (file_name, &s) == 0)
725     {
726       identity->device = s.st_dev;
727       identity->inode = s.st_ino;
728       identity->name = NULL;
729     }
730   else
731     {
732       char *dir = dir_name (file_name);
733       if (last_component (file_name) != NULL && stat (dir, &s) == 0)
734         {
735           identity->device = s.st_dev;
736           identity->inode = s.st_ino;
737           identity->name = base_name (file_name);
738         }
739       else
740         {
741           identity->device = 0;
742           identity->inode = 0;
743           identity->name = xstrdup (file_name);
744         }
745       free (dir);
746     }
747 #else /* Windows */
748   bool ok = false;
749   HANDLE h = CreateFile (file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
750   if (h != INVALID_HANDLE_VALUE)
751   {
752     BY_HANDLE_FILE_INFORMATION fi;
753     ok = GetFileInformationByHandle (h, &fi);
754     if (ok)
755       {
756 	identity->device = fi.dwVolumeSerialNumber;
757 	identity->inode = fi.nFileIndexHigh;
758 	identity->inode <<= (sizeof fi.nFileIndexLow) * CHAR_BIT;
759 	identity->inode |= fi.nFileIndexLow;
760 	identity->name = 0;
761       }
762     CloseHandle (h);
763   }
764 
765   if (!ok)
766     {
767       identity->device = 0;
768       identity->inode = 0;
769 
770       size_t bufsize;
771       size_t pathlen = 255;
772       char *cname = NULL;
773       do
774       {
775 	bufsize = pathlen;
776 	cname = xrealloc (cname, bufsize);
777 	pathlen = GetFullPathName (file_name, bufsize, cname, NULL);
778       }
779       while (pathlen > bufsize);
780       identity->name = xstrdup (cname);
781       free (cname);
782       str_lowercase (identity->name);
783     }
784 #endif /* Windows */
785 
786   return identity;
787 }
788 
789 /* Frees IDENTITY obtained from fh_get_identity(). */
790 void
fh_free_identity(struct file_identity * identity)791 fh_free_identity (struct file_identity *identity)
792 {
793   if (identity != NULL)
794     {
795       free (identity->name);
796       free (identity);
797     }
798 }
799 
800 /* Compares A and B, returning a strcmp()-type result. */
801 int
fh_compare_file_identities(const struct file_identity * a,const struct file_identity * b)802 fh_compare_file_identities (const struct file_identity *a,
803                             const struct file_identity *b)
804 {
805   if (a->device != b->device)
806     return a->device < b->device ? -1 : 1;
807   else if (a->inode != b->inode)
808     return a->inode < b->inode ? -1 : 1;
809   else if (a->name != NULL)
810     return b->name != NULL ? strcmp (a->name, b->name) : 1;
811   else
812     return b->name != NULL ? -1 : 0;
813 }
814 
815 /* Returns a hash value for IDENTITY. */
816 unsigned int
fh_hash_identity(const struct file_identity * identity)817 fh_hash_identity (const struct file_identity *identity)
818 {
819   unsigned int hash = hash_int (identity->device, identity->inode);
820   if (identity->name != NULL)
821     hash = hash_string (identity->name, hash);
822   return hash;
823 }
824