win32-symlink 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 25941f053e8f97104200a2a4ca2ca86f39f9ec8f
4
- data.tar.gz: 1957797a39473aa89fd5282bd98f26928a194be2
3
+ metadata.gz: cff5a6b053e50f47c45bdde6a29ba72088182522
4
+ data.tar.gz: 88a4b8561aba22278fdfedfd94a3938cdbcf24ee
5
5
  SHA512:
6
- metadata.gz: 72a668ca365915590fadbfe642592c14176ae33a70fb1c74a53dffff7083823c749d81e532b3b50018f3b7aa53eb0e090e6b408c39bbef4bd8458311d3a27000
7
- data.tar.gz: 188f5823f596f0fafc3da5d423e232a5a017edaa91a0810a2dd3541b7d6002131fcb7968094e2ba601f5016d1873516a3703f9ac50a42484c3667561d3cc187d
6
+ metadata.gz: 2dc15b209921a53f8f0024e275b2c7331a913be813699382b7ede0e16bbfe04f455ac7d3038e71914c5580b001bfdde591983ef9efbbb9a76e468c68813be724
7
+ data.tar.gz: 6b3bcec0e9283052bf4af873e52faf60f3deda9d1ab72ee58082401a9a92b42fb322754e174c47a05df1f42942b9f92aaa86751bdeb7c7d69258549b9d26d34d
data/Gemfile CHANGED
@@ -1,5 +1,6 @@
1
- # -*- ruby -*-
2
- source 'https://rubygems.org'
3
-
4
- # Specify your gem's dependencies in win32-symlink.gemspec
5
- gemspec
1
+ # -*- ruby -*-
2
+ source 'https://rubygems.org'
3
+
4
+ group :development do
5
+ gem "rake-compiler"
6
+ end
data/LICENSE CHANGED
@@ -1,22 +1,22 @@
1
- Copyright (c) 2013 boris
2
-
3
- MIT License
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
1
+ Copyright (c) 2013 boris
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
22
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,28 +1,40 @@
1
- # Win32::Symlink
2
-
3
- Since the standard ruby library lacks symlink functions on Windows, this
4
- little library attempts to fill the gap.
5
-
6
- ## Installation
7
-
8
- Add this line to your application's Gemfile:
9
-
10
- gem 'win32-symlink'
11
-
12
- And then execute:
13
-
14
- $ bundle
15
-
16
- Or install it yourself as:
17
-
18
- $ gem install win32-symlink
19
-
20
- ## Usage
21
-
22
- This library implements the following functons that mimic their ruby
23
- counterparts:
24
-
25
- readlink(link_name) -> file_name
26
- symlink?(link_name) -> true or false
27
- symlink(old_name, new_name) -> 0
28
-
1
+ # Win32::Symlink
2
+
3
+ Since the standard ruby library lacks symlink functions on Windows, this
4
+ little library attempts to fill the gap.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'win32-symlink'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install win32-symlink
19
+
20
+ ## Usage
21
+
22
+ This library implements the following functons that mimic their ruby
23
+ counterparts:
24
+
25
+ Win32::Symlink.readlink(link_name) -> file_name
26
+ Win32::Symlink.symlink?(link_name) -> true or false
27
+ Win32::Symlink.symlink(old_name, new_name) -> 0
28
+
29
+ ### 'Portable' Usage
30
+
31
+ Instead of referencing symlink functions in the Win32::Symlink
32
+ module it is possible to extend the Ruby File class and use the
33
+ functions as on other systems, which support symlinks directly.
34
+
35
+
36
+ require 'win32/filesymlink' if RUBY_PLATFORM.include?('mingw')
37
+
38
+ File.symlink('path to source', 'symlink')
39
+ File.symlink?('symlink')
40
+ File.readlink('symlink')
data/Rakefile CHANGED
@@ -1,21 +1,21 @@
1
- # -*- ruby -*-
2
- require 'bundler/gem_tasks'
3
- require 'rake/testtask'
4
-
5
- begin
6
- Bundler.setup(:default, :development)
7
- rescue Bundler::BundlerError => e
8
- $stderr.puts e.message
9
- $stderr.puts "Run `bundle install` to install missing gems"
10
- exit e.status_code
11
- end
12
-
13
- $gemspec = Bundler.load_gemspec('win32-symlink.gemspec')
14
-
15
- Rake::TestTask.new do |t|
16
- t.warning = true
17
- end
18
-
19
- load 'tasks/ext.rake'
20
-
21
- task :default => [:compile, :test]
1
+ # -*- ruby -*-
2
+ require 'bundler/gem_tasks'
3
+ require 'rake/testtask'
4
+
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+
13
+ $gemspec = Bundler.load_gemspec('win32-symlink.gemspec')
14
+
15
+ Rake::TestTask.new do |t|
16
+ t.warning = true
17
+ end
18
+
19
+ load 'tasks/ext.rake'
20
+
21
+ task :default => [:compile, :test]
data/ext/win32/extconf.rb CHANGED
@@ -1,12 +1,12 @@
1
- require 'mkmf'
2
-
3
- if CONFIG['CXXFLAGS'] == '$(cxxflags)'
4
- CONFIG['CXXFLAGS'] = CONFIG['cxxflags'].clone
5
- end
6
-
7
- # these are not relevant for C++
8
- CONFIG['warnflags'].sub! '-Wdeclaration-after-statement', ''
9
- CONFIG['warnflags'].sub! '-Wimplicit-function-declaration', ''
10
-
11
- try_cppflags $CPPFLAGS.sub(/_WIN32_WINNT=\S+/, '_WIN32_WINNT=0x600')
12
- create_makefile 'win32_symlink'
1
+ require 'mkmf'
2
+
3
+ if CONFIG['CXXFLAGS'] == '$(cxxflags)'
4
+ CONFIG['CXXFLAGS'] = CONFIG['cxxflags'].clone
5
+ end
6
+
7
+ # these are not relevant for C++
8
+ CONFIG['warnflags'].sub! '-Wdeclaration-after-statement', ''
9
+ CONFIG['warnflags'].sub! '-Wimplicit-function-declaration', ''
10
+
11
+ try_cppflags $CPPFLAGS.sub(/_WIN32_WINNT=\S+/, '_WIN32_WINNT=0x600')
12
+ create_makefile 'win32_symlink'
@@ -1,297 +1,297 @@
1
- #include "ruby.h"
2
-
3
- #define filecp_to_wstr(str, plen) cstr_to_wstr(filecp(), str, -1, plen)
4
-
5
- typedef BOOL (WINAPI* create_symbolic_linkW_t)(LPWSTR src, LPWSTR dest, DWORD flags);
6
- create_symbolic_linkW_t create_symbolic_linkW = NULL;
7
-
8
-
9
- typedef struct _REPARSE_DATA_BUFFER
10
- {
11
- ULONG ReparseTag;
12
- USHORT ReparseDataLength;
13
- USHORT Reserved;
14
- union
15
- {
16
- struct
17
- {
18
- USHORT SubstituteNameOffset;
19
- USHORT SubstituteNameLength;
20
- USHORT PrintNameOffset;
21
- USHORT PrintNameLength;
22
- ULONG Flags;
23
- WCHAR PathBuffer[1];
24
- } SymbolicLinkReparseBuffer;
25
- struct
26
- {
27
- USHORT SubstituteNameOffset;
28
- USHORT SubstituteNameLength;
29
- USHORT PrintNameOffset;
30
- USHORT PrintNameLength;
31
- WCHAR PathBuffer[1];
32
- } MountPointReparseBuffer;
33
- struct
34
- {
35
- UCHAR DataBuffer[1];
36
- } GenericReparseBuffer;
37
- } DUMMYUNIONNAME;
38
- } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
39
-
40
-
41
- static inline UINT
42
- filecp(void)
43
- {
44
- UINT cp = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
45
- return cp;
46
- }
47
-
48
- static wchar_t *
49
- cstr_to_wstr(UINT code_page, const char *str, int clen, int *wstr_len)
50
- {
51
- int len;
52
- wchar_t* wstr;
53
-
54
- len = MultiByteToWideChar(code_page, 0, str, clen, NULL, 0);
55
- wstr = (wchar_t *)xmalloc(len * sizeof(wchar_t));
56
-
57
- MultiByteToWideChar(code_page, 0, str, clen, wstr, len);
58
- if( wstr_len )
59
- *wstr_len = len;
60
- return wstr;
61
- }
62
-
63
- static VALUE
64
- wstr_to_str(UINT code_page, const wchar_t* wstr, int clen)
65
- {
66
- int len, term_len;
67
- VALUE v;
68
- term_len = clen < 0 ? 1 : 0;
69
- len = WideCharToMultiByte(code_page, 0, wstr, clen, NULL, 0, NULL, NULL) - term_len;
70
- v = rb_str_new(NULL, len);
71
- WideCharToMultiByte(code_page, 0, wstr, clen, RSTRING_PTR(v), len + term_len, NULL, NULL);
72
- return v;
73
- }
74
-
75
- static VALUE
76
- make_error_str(VALUE klass, const char* fmt, ...)
77
- {
78
- VALUE exc;
79
- VALUE str;
80
-
81
- va_list va;
82
- va_start(va, fmt);
83
- str = rb_vsprintf(fmt, va);
84
- va_end(va);
85
- exc = rb_exc_new3(klass, str);
86
- rb_str_free(str);
87
- return exc;
88
- }
89
-
90
- static VALUE
91
- make_api_error(const char* msg)
92
- {
93
- DWORD w32_err;
94
- int myerrno;
95
- w32_err = GetLastError();
96
- myerrno = rb_w32_map_errno(w32_err);
97
- return rb_syserr_new( myerrno == EINVAL ? w32_err : myerrno, msg);
98
- }
99
-
100
- static wchar_t*
101
- resolve_api_path(wchar_t* path)
102
- {
103
- if( 0 == wcsncmp(L"\\??\\UNC\\", path, 8) )
104
- {
105
- path += 6;
106
- *path = L'\\';
107
- }
108
- else if( 0 == wcsncmp(L"\\??\\", path, 4) )
109
- {
110
- path += 4;
111
- }
112
- return path;
113
- }
114
-
115
- static VALUE
116
- win32_readlink(const char* file)
117
- {
118
- VALUE error = Qnil;
119
- VALUE target = Qnil;
120
- HANDLE h = INVALID_HANDLE_VALUE;
121
- bool ok = true;
122
- REPARSE_DATA_BUFFER* buf = NULL;
123
- const size_t buf_size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
124
-
125
- buf = (REPARSE_DATA_BUFFER*)xmalloc(buf_size);
126
- if(ok)
127
- {
128
- const DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
129
- const DWORD access_mode = FILE_READ_EA;
130
- const DWORD attributes =
131
- FILE_FLAG_BACKUP_SEMANTICS
132
- | FILE_ATTRIBUTE_REPARSE_POINT
133
- | FILE_FLAG_OPEN_REPARSE_POINT;
134
-
135
- h = CreateFile( file, access_mode, share_mode,
136
- 0, OPEN_EXISTING, attributes, NULL);
137
-
138
- if(!(ok = (h != INVALID_HANDLE_VALUE)))
139
- {
140
- error = make_api_error(file);
141
- }
142
- }
143
-
144
- if(ok)
145
- {
146
- DWORD returned_size;
147
- ok = 0 != DeviceIoControl (h, FSCTL_GET_REPARSE_POINT,
148
- NULL, 0, buf, buf_size, &returned_size, NULL);
149
- if(!ok)
150
- {
151
- error = make_api_error(file);
152
- }
153
- }
154
- if(ok)
155
- {
156
- wchar_t* raw_target = NULL;
157
- long target_len = 0;
158
-
159
- switch (buf->ReparseTag)
160
- {
161
- case IO_REPARSE_TAG_MOUNT_POINT:
162
- {
163
- raw_target = &buf->MountPointReparseBuffer.PathBuffer[
164
- buf->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t)];
165
- target_len = buf->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
166
- break;
167
- }
168
- case IO_REPARSE_TAG_SYMLINK:
169
- {
170
- raw_target = &buf->SymbolicLinkReparseBuffer.PathBuffer[
171
- buf->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t)];
172
- target_len = buf->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
173
- break;
174
- }
175
- default:
176
- ok = false;
177
- error = make_error_str(rb_eIOError, "file %s is not a symlink", file);
178
- }
179
- if(ok)
180
- {
181
- wchar_t* resolved;
182
- resolved = resolve_api_path(raw_target);
183
- target_len -= resolved - raw_target;
184
- target = wstr_to_str(filecp(), resolved, target_len);
185
- }
186
- }
187
-
188
-
189
- if( h != INVALID_HANDLE_VALUE )
190
- {
191
- CloseHandle(h);
192
- }
193
- xfree(buf);
194
-
195
- if( !NIL_P(error) )
196
- {
197
- rb_exc_raise(error);
198
- }
199
- return target;
200
- }
201
-
202
- static VALUE
203
- rb_readlink(VALUE mod, VALUE file)
204
- {
205
- (void)mod;
206
- FilePathValue(file);
207
- file = rb_str_encode_ospath(file);
208
- return win32_readlink(RSTRING_PTR(file));
209
- }
210
-
211
- static VALUE
212
- rb_symlink_p(VALUE mod, VALUE file)
213
- {
214
- (void)mod;
215
- DWORD attrs;
216
- FilePathValue(file);
217
- file = rb_str_encode_ospath(file);
218
- attrs = GetFileAttributes(RSTRING_PTR(file));
219
- if( attrs == INVALID_FILE_ATTRIBUTES )
220
- {
221
- rb_exc_raise(make_api_error(RSTRING_PTR(file)));
222
- }
223
- return (attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
224
- }
225
-
226
- static bool
227
- is_directory(wchar_t* path)
228
- {
229
- HANDLE h = INVALID_HANDLE_VALUE;
230
- WIN32_FIND_DATAW fd;
231
- h = FindFirstFileW(path, &fd);
232
- if( h != INVALID_HANDLE_VALUE )
233
- {
234
- FindClose(h);
235
- return (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 ;
236
- }
237
- return false;
238
- }
239
-
240
- static VALUE
241
- rb_symlink(VALUE mod, VALUE target, VALUE symlink)
242
- {
243
- (void)mod;
244
-
245
- VALUE error = Qnil;
246
- wchar_t* wtarget = NULL;
247
- wchar_t* wsymlink = NULL;
248
- BOOL res;
249
- DWORD flags = 0;
250
-
251
- target = FilePathValue(target);
252
- symlink = FilePathValue(symlink);
253
- target = rb_str_encode_ospath(target);
254
- symlink = rb_str_encode_ospath(symlink);
255
-
256
- wtarget = filecp_to_wstr(RSTRING_PTR(target), NULL);
257
- wsymlink = filecp_to_wstr(RSTRING_PTR(symlink), NULL);
258
-
259
- if( is_directory(wsymlink) )
260
- {
261
- flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
262
- }
263
- res = create_symbolic_linkW(wsymlink, wtarget, flags);
264
- if( !res )
265
- {
266
- error = make_api_error(RSTRING_PTR(target));
267
- }
268
-
269
- xfree(wtarget);
270
- xfree(wsymlink);
271
-
272
- if( !NIL_P(error) )
273
- {
274
- rb_exc_raise(error);
275
- }
276
- return INT2FIX(0);
277
- }
278
-
279
- extern "C" void
280
- Init_win32_symlink()
281
- {
282
- VALUE mod_win32;
283
- VALUE mod;
284
-
285
- mod_win32 = rb_define_module("Win32");
286
- mod = rb_define_module_under(mod_win32, "Symlink");
287
-
288
- rb_define_module_function(mod, "readlink", RUBY_METHOD_FUNC(rb_readlink), 1);
289
- rb_define_module_function(mod, "symlink?", RUBY_METHOD_FUNC(rb_symlink_p), 1);
290
-
291
- create_symbolic_linkW = (create_symbolic_linkW_t)GetProcAddress(
292
- GetModuleHandle("kernel32.dll"), "CreateSymbolicLinkW");
293
- if( create_symbolic_linkW )
294
- {
295
- rb_define_module_function(mod, "symlink", RUBY_METHOD_FUNC(rb_symlink), 2);
296
- }
297
- }
1
+ #include "ruby.h"
2
+
3
+ #define filecp_to_wstr(str, plen) cstr_to_wstr(filecp(), str, -1, plen)
4
+
5
+ typedef BOOL (WINAPI* create_symbolic_linkW_t)(LPWSTR src, LPWSTR dest, DWORD flags);
6
+ create_symbolic_linkW_t create_symbolic_linkW = NULL;
7
+
8
+
9
+ typedef struct _REPARSE_DATA_BUFFER
10
+ {
11
+ ULONG ReparseTag;
12
+ USHORT ReparseDataLength;
13
+ USHORT Reserved;
14
+ union
15
+ {
16
+ struct
17
+ {
18
+ USHORT SubstituteNameOffset;
19
+ USHORT SubstituteNameLength;
20
+ USHORT PrintNameOffset;
21
+ USHORT PrintNameLength;
22
+ ULONG Flags;
23
+ WCHAR PathBuffer[1];
24
+ } SymbolicLinkReparseBuffer;
25
+ struct
26
+ {
27
+ USHORT SubstituteNameOffset;
28
+ USHORT SubstituteNameLength;
29
+ USHORT PrintNameOffset;
30
+ USHORT PrintNameLength;
31
+ WCHAR PathBuffer[1];
32
+ } MountPointReparseBuffer;
33
+ struct
34
+ {
35
+ UCHAR DataBuffer[1];
36
+ } GenericReparseBuffer;
37
+ } DUMMYUNIONNAME;
38
+ } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
39
+
40
+
41
+ static inline UINT
42
+ filecp(void)
43
+ {
44
+ UINT cp = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
45
+ return cp;
46
+ }
47
+
48
+ static wchar_t *
49
+ cstr_to_wstr(UINT code_page, const char *str, int clen, int *wstr_len)
50
+ {
51
+ int len;
52
+ wchar_t* wstr;
53
+
54
+ len = MultiByteToWideChar(code_page, 0, str, clen, NULL, 0);
55
+ wstr = (wchar_t *)xmalloc(len * sizeof(wchar_t));
56
+
57
+ MultiByteToWideChar(code_page, 0, str, clen, wstr, len);
58
+ if( wstr_len )
59
+ *wstr_len = len;
60
+ return wstr;
61
+ }
62
+
63
+ static VALUE
64
+ wstr_to_str(UINT code_page, const wchar_t* wstr, int clen)
65
+ {
66
+ int len, term_len;
67
+ VALUE v;
68
+ term_len = clen < 0 ? 1 : 0;
69
+ len = WideCharToMultiByte(code_page, 0, wstr, clen, NULL, 0, NULL, NULL) - term_len;
70
+ v = rb_str_new(NULL, len);
71
+ WideCharToMultiByte(code_page, 0, wstr, clen, RSTRING_PTR(v), len + term_len, NULL, NULL);
72
+ return v;
73
+ }
74
+
75
+ static VALUE
76
+ make_error_str(VALUE klass, const char* fmt, ...)
77
+ {
78
+ VALUE exc;
79
+ VALUE str;
80
+
81
+ va_list va;
82
+ va_start(va, fmt);
83
+ str = rb_vsprintf(fmt, va);
84
+ va_end(va);
85
+ exc = rb_exc_new3(klass, str);
86
+ rb_str_free(str);
87
+ return exc;
88
+ }
89
+
90
+ static VALUE
91
+ make_api_error(const char* msg)
92
+ {
93
+ DWORD w32_err;
94
+ int myerrno;
95
+ w32_err = GetLastError();
96
+ myerrno = rb_w32_map_errno(w32_err);
97
+ return rb_syserr_new( myerrno == EINVAL ? w32_err : myerrno, msg);
98
+ }
99
+
100
+ static wchar_t*
101
+ resolve_api_path(wchar_t* path)
102
+ {
103
+ if( 0 == wcsncmp(L"\\??\\UNC\\", path, 8) )
104
+ {
105
+ path += 6;
106
+ *path = L'\\';
107
+ }
108
+ else if( 0 == wcsncmp(L"\\??\\", path, 4) )
109
+ {
110
+ path += 4;
111
+ }
112
+ return path;
113
+ }
114
+
115
+ static VALUE
116
+ win32_readlink(const char* file)
117
+ {
118
+ VALUE error = Qnil;
119
+ VALUE target = Qnil;
120
+ HANDLE h = INVALID_HANDLE_VALUE;
121
+ bool ok = true;
122
+ REPARSE_DATA_BUFFER* buf = NULL;
123
+ const size_t buf_size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
124
+
125
+ buf = (REPARSE_DATA_BUFFER*)xmalloc(buf_size);
126
+ if(ok)
127
+ {
128
+ const DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
129
+ const DWORD access_mode = FILE_READ_EA;
130
+ const DWORD attributes =
131
+ FILE_FLAG_BACKUP_SEMANTICS
132
+ | FILE_ATTRIBUTE_REPARSE_POINT
133
+ | FILE_FLAG_OPEN_REPARSE_POINT;
134
+
135
+ h = CreateFile( file, access_mode, share_mode,
136
+ 0, OPEN_EXISTING, attributes, NULL);
137
+
138
+ if(!(ok = (h != INVALID_HANDLE_VALUE)))
139
+ {
140
+ error = make_api_error(file);
141
+ }
142
+ }
143
+
144
+ if(ok)
145
+ {
146
+ DWORD returned_size;
147
+ ok = 0 != DeviceIoControl (h, FSCTL_GET_REPARSE_POINT,
148
+ NULL, 0, buf, buf_size, &returned_size, NULL);
149
+ if(!ok)
150
+ {
151
+ error = make_api_error(file);
152
+ }
153
+ }
154
+ if(ok)
155
+ {
156
+ wchar_t* raw_target = NULL;
157
+ long target_len = 0;
158
+
159
+ switch (buf->ReparseTag)
160
+ {
161
+ case IO_REPARSE_TAG_MOUNT_POINT:
162
+ {
163
+ raw_target = &buf->MountPointReparseBuffer.PathBuffer[
164
+ buf->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t)];
165
+ target_len = buf->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
166
+ break;
167
+ }
168
+ case IO_REPARSE_TAG_SYMLINK:
169
+ {
170
+ raw_target = &buf->SymbolicLinkReparseBuffer.PathBuffer[
171
+ buf->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t)];
172
+ target_len = buf->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
173
+ break;
174
+ }
175
+ default:
176
+ ok = false;
177
+ error = make_error_str(rb_eIOError, "file %s is not a symlink", file);
178
+ }
179
+ if(ok)
180
+ {
181
+ wchar_t* resolved;
182
+ resolved = resolve_api_path(raw_target);
183
+ target_len -= resolved - raw_target;
184
+ target = wstr_to_str(filecp(), resolved, target_len);
185
+ }
186
+ }
187
+
188
+
189
+ if( h != INVALID_HANDLE_VALUE )
190
+ {
191
+ CloseHandle(h);
192
+ }
193
+ xfree(buf);
194
+
195
+ if( !NIL_P(error) )
196
+ {
197
+ rb_exc_raise(error);
198
+ }
199
+ return target;
200
+ }
201
+
202
+ static VALUE
203
+ rb_readlink(VALUE mod, VALUE file)
204
+ {
205
+ (void)mod;
206
+ FilePathValue(file);
207
+ file = rb_str_encode_ospath(file);
208
+ return win32_readlink(RSTRING_PTR(file));
209
+ }
210
+
211
+ static VALUE
212
+ rb_symlink_p(VALUE mod, VALUE file)
213
+ {
214
+ (void)mod;
215
+ DWORD attrs;
216
+ FilePathValue(file);
217
+ file = rb_str_encode_ospath(file);
218
+ attrs = GetFileAttributes(RSTRING_PTR(file));
219
+ if( attrs == INVALID_FILE_ATTRIBUTES )
220
+ {
221
+ rb_exc_raise(make_api_error(RSTRING_PTR(file)));
222
+ }
223
+ return (attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
224
+ }
225
+
226
+ static bool
227
+ is_directory(wchar_t* path)
228
+ {
229
+ HANDLE h = INVALID_HANDLE_VALUE;
230
+ WIN32_FIND_DATAW fd;
231
+ h = FindFirstFileW(path, &fd);
232
+ if( h != INVALID_HANDLE_VALUE )
233
+ {
234
+ FindClose(h);
235
+ return (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 ;
236
+ }
237
+ return false;
238
+ }
239
+
240
+ static VALUE
241
+ rb_symlink(VALUE mod, VALUE target, VALUE symlink)
242
+ {
243
+ (void)mod;
244
+
245
+ VALUE error = Qnil;
246
+ wchar_t* wtarget = NULL;
247
+ wchar_t* wsymlink = NULL;
248
+ BOOL res;
249
+ DWORD flags = 0;
250
+
251
+ target = FilePathValue(target);
252
+ symlink = FilePathValue(symlink);
253
+ target = rb_str_encode_ospath(target);
254
+ symlink = rb_str_encode_ospath(symlink);
255
+
256
+ wtarget = filecp_to_wstr(RSTRING_PTR(target), NULL);
257
+ wsymlink = filecp_to_wstr(RSTRING_PTR(symlink), NULL);
258
+
259
+ if( is_directory(wsymlink) )
260
+ {
261
+ flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
262
+ }
263
+ res = create_symbolic_linkW(wsymlink, wtarget, flags);
264
+ if( !res )
265
+ {
266
+ error = make_api_error(RSTRING_PTR(target));
267
+ }
268
+
269
+ xfree(wtarget);
270
+ xfree(wsymlink);
271
+
272
+ if( !NIL_P(error) )
273
+ {
274
+ rb_exc_raise(error);
275
+ }
276
+ return INT2FIX(0);
277
+ }
278
+
279
+ extern "C" void
280
+ Init_win32_symlink()
281
+ {
282
+ VALUE mod_win32;
283
+ VALUE mod;
284
+
285
+ mod_win32 = rb_define_module("Win32");
286
+ mod = rb_define_module_under(mod_win32, "Symlink");
287
+
288
+ rb_define_module_function(mod, "readlink", RUBY_METHOD_FUNC(rb_readlink), 1);
289
+ rb_define_module_function(mod, "symlink?", RUBY_METHOD_FUNC(rb_symlink_p), 1);
290
+
291
+ create_symbolic_linkW = (create_symbolic_linkW_t)GetProcAddress(
292
+ GetModuleHandle("kernel32.dll"), "CreateSymbolicLinkW");
293
+ if( create_symbolic_linkW )
294
+ {
295
+ rb_define_module_function(mod, "symlink", RUBY_METHOD_FUNC(rb_symlink), 2);
296
+ }
297
+ }
@@ -0,0 +1,19 @@
1
+ require_relative 'symlink'
2
+
3
+
4
+ # Extending the File class on windows system, so that its symlink function work as on other sytems.
5
+ class File
6
+
7
+ def File.symlink(file, symlink)
8
+ Win32::Symlink::symlink(file, symlink)
9
+ end
10
+
11
+ def File.symlink?(file)
12
+ Win32::Symlink::symlink?(file)
13
+ end
14
+
15
+ def File.readlink(file)
16
+ Win32::Symlink::readlink(file)
17
+ end
18
+
19
+ end
@@ -1,12 +1,12 @@
1
- module Win32
2
- module Symlink
3
- module Version
4
- MAJOR = 0
5
- MINOR = 1
6
- PATCH = 1
7
- BUILD = nil
8
- STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join '.'
9
- end
10
- VERSION = Version::STRING
11
- end
12
- end
1
+ module Win32
2
+ module Symlink
3
+ module Version
4
+ MAJOR = 0
5
+ MINOR = 1
6
+ PATCH = 2
7
+ BUILD = nil
8
+ STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join '.'
9
+ end
10
+ VERSION = Version::STRING
11
+ end
12
+ end
Binary file
data/lib/win32/symlink.rb CHANGED
@@ -1,2 +1,2 @@
1
- require_relative 'symlink/win32_symlink'
2
- require_relative 'symlink/version'
1
+ require_relative 'symlink/win32_symlink'
2
+ require_relative 'symlink/version'
data/tasks/ext.rake CHANGED
@@ -1,7 +1,7 @@
1
- # -*- ruby -*-
2
- require 'rake/extensiontask'
3
- Rake::ExtensionTask.new('win32_symlink', $gemspec) do |ext|
4
- ext.ext_dir = 'ext/win32'
5
- ext.lib_dir = 'lib/win32/symlink'
6
- end
7
-
1
+ # -*- ruby -*-
2
+ require 'rake/extensiontask'
3
+ Rake::ExtensionTask.new('win32_symlink', $gemspec) do |ext|
4
+ ext.ext_dir = 'ext/win32'
5
+ ext.lib_dir = 'lib/win32/symlink'
6
+ end
7
+
@@ -1,60 +1,60 @@
1
- $LOAD_PATH.unshift File.join(File.dirname(File.dirname(__FILE__)), 'ext')
2
-
3
- # puts $LOAD_PATH
4
-
5
- require 'test/unit'
6
- require 'win32/symlink'
7
- include Win32
8
-
9
- class Errno::E4390 < SystemCallError
10
- # The file is not a reparse point
11
- end
12
- class Errno::E1314 < SystemCallError
13
- # A required privilege is not set
14
- end
15
-
16
- class TC_Win32Symlink < Test::Unit::TestCase
17
-
18
- def setup
19
- @users_dir = File.join(ENV['SYSTEMDRIVE'], '/Users')
20
- end
21
-
22
- def test_bad_args
23
- assert_raises(Errno::ENOENT) {Symlink.symlink? ""}
24
- assert_raises(TypeError) { Symlink.symlink? nil }
25
- assert_raises(TypeError) { Symlink.symlink?(Object.new) }
26
- assert_raises(TypeError) { Symlink.symlink "", nil }
27
- assert_raises(TypeError) { Symlink.symlink nil, "" }
28
- end
29
-
30
- def test_system_links
31
- assert(Symlink.symlink?(File.join(@users_dir, "All Users")))
32
- assert(!Symlink.symlink?(ENV['WINDIR']))
33
- end
34
-
35
- def test_readlink
36
- assert_equal(Symlink.readlink(File.join(@users_dir, "All Users")), ENV['ALLUSERSPROFILE'])
37
- end
38
-
39
- def test_nonlink
40
- assert_raises(Errno::E4390) { Symlink.readlink(ENV['WINDIR']) }
41
- end
42
-
43
- def test_makelink
44
- Dir.chdir(ENV["TMP"]) do
45
- IO::write("symlink-test-file", "test")
46
- begin
47
- Symlink.symlink("symlink-test-file", "symlink-test-symlink")
48
- assert(Symlink.symlink? "symlink-test-symlink")
49
- rescue Errno::E1314 => e
50
- skip e.message
51
- end
52
- end
53
- end
54
-
55
- def teardown
56
- Dir.glob(File.join(ENV['TMP'], 'symlink-test*')).each {|f| File::unlink f}
57
- end
58
-
59
- end
60
-
1
+ $LOAD_PATH.unshift File.join(File.dirname(File.dirname(__FILE__)), 'ext')
2
+
3
+ # puts $LOAD_PATH
4
+
5
+ require 'test/unit'
6
+ require 'win32/symlink'
7
+ include Win32
8
+
9
+ class Errno::E4390 < SystemCallError
10
+ # The file is not a reparse point
11
+ end
12
+ class Errno::E1314 < SystemCallError
13
+ # A required privilege is not set
14
+ end
15
+
16
+ class TC_Win32Symlink < Test::Unit::TestCase
17
+
18
+ def setup
19
+ @users_dir = File.join(ENV['SYSTEMDRIVE'], '/Users')
20
+ end
21
+
22
+ def test_bad_args
23
+ assert_raises(Errno::ENOENT) {Symlink.symlink? ""}
24
+ assert_raises(TypeError) { Symlink.symlink? nil }
25
+ assert_raises(TypeError) { Symlink.symlink?(Object.new) }
26
+ assert_raises(TypeError) { Symlink.symlink "", nil }
27
+ assert_raises(TypeError) { Symlink.symlink nil, "" }
28
+ end
29
+
30
+ def test_system_links
31
+ assert(Symlink.symlink?(File.join(@users_dir, "All Users")))
32
+ assert(!Symlink.symlink?(ENV['WINDIR']))
33
+ end
34
+
35
+ def test_readlink
36
+ assert_equal(Symlink.readlink(File.join(@users_dir, "All Users")), ENV['ALLUSERSPROFILE'])
37
+ end
38
+
39
+ def test_nonlink
40
+ assert_raises(Errno::E4390) { Symlink.readlink(ENV['WINDIR']) }
41
+ end
42
+
43
+ def test_makelink
44
+ Dir.chdir(ENV["TMP"]) do
45
+ IO::write("symlink-test-file", "test")
46
+ begin
47
+ Symlink.symlink("symlink-test-file", "symlink-test-symlink")
48
+ assert(Symlink.symlink? "symlink-test-symlink")
49
+ rescue Errno::E1314 => e
50
+ skip e.message
51
+ end
52
+ end
53
+ end
54
+
55
+ def teardown
56
+ Dir.glob(File.join(ENV['TMP'], 'symlink-test*')).each {|f| File::unlink f}
57
+ end
58
+
59
+ end
60
+
@@ -1,35 +1,36 @@
1
- # -*- ruby -*-
2
- require File.expand_path('../lib/win32/symlink/version', __FILE__)
3
-
4
- Gem::Specification.new do |gem|
5
- gem.authors = ['Boris Kropivnitsky']
6
- gem.email = ['boris.kro@gmail.com']
7
- gem.description = <<DESC
8
- Symlink functions for Windows (Vista and above)
9
- DESC
10
- gem.summary = 'symlink functions for Windows'
11
- gem.homepage = 'https://github.com/boriskro/win32-symlink'
12
-
13
- gem.files = %w{
14
- Gemfile
15
- LICENSE
16
- README.md
17
- Rakefile
18
- ext/win32/extconf.rb
19
- ext/win32/win32-symlink.cpp
20
- lib/win32/symlink.rb
21
- lib/win32/symlink/version.rb
22
- lib/win32/symlink/win32_symlink.so
23
- tasks/ext.rake
24
- test/test_win32_symlink.rb
25
- win32-symlink.gemspec
26
- }
27
- gem.extensions = ['ext/win32/extconf.rb']
28
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
29
- gem.name = 'win32-symlink'
30
- gem.require_paths = ['lib']
31
- gem.version = Win32::Symlink::VERSION
32
- gem.licenses = ['MIT']
33
- gem.add_development_dependency 'bundler'
34
- gem.add_development_dependency 'rake-compiler'
35
- end
1
+ # -*- ruby -*-
2
+ require File.expand_path('../lib/win32/symlink/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ['Boris Kropivnitsky']
6
+ gem.email = ['boris.kro@gmail.com']
7
+ gem.description = <<DESC
8
+ Symlink functions for Windows (Vista and above)
9
+ DESC
10
+ gem.summary = 'symlink functions for Windows'
11
+ gem.homepage = 'https://github.com/boriskro/win32-symlink'
12
+
13
+ gem.files = %w{
14
+ Gemfile
15
+ LICENSE
16
+ README.md
17
+ Rakefile
18
+ ext/win32/extconf.rb
19
+ ext/win32/win32-symlink.cpp
20
+ lib/win32/symlink.rb
21
+ lib/win32/filesymlink.rb
22
+ lib/win32/symlink/version.rb
23
+ lib/win32/symlink/win32_symlink.so
24
+ tasks/ext.rake
25
+ test/test_win32_symlink.rb
26
+ win32-symlink.gemspec
27
+ }
28
+ gem.extensions = ['ext/win32/extconf.rb']
29
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
30
+ gem.name = 'win32-symlink'
31
+ gem.require_paths = ['lib']
32
+ gem.version = Win32::Symlink::VERSION
33
+ gem.licenses = ['MIT']
34
+ gem.add_development_dependency 'bundler'
35
+ gem.add_development_dependency 'rake-compiler'
36
+ end
metadata CHANGED
@@ -1,41 +1,41 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: win32-symlink
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boris Kropivnitsky
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-26 00:00:00.000000000 Z
11
+ date: 2014-11-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake-compiler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  description: |
@@ -53,6 +53,7 @@ files:
53
53
  - Rakefile
54
54
  - ext/win32/extconf.rb
55
55
  - ext/win32/win32-symlink.cpp
56
+ - lib/win32/filesymlink.rb
56
57
  - lib/win32/symlink.rb
57
58
  - lib/win32/symlink/version.rb
58
59
  - lib/win32/symlink/win32_symlink.so
@@ -69,17 +70,17 @@ require_paths:
69
70
  - lib
70
71
  required_ruby_version: !ruby/object:Gem::Requirement
71
72
  requirements:
72
- - - '>='
73
+ - - ">="
73
74
  - !ruby/object:Gem::Version
74
75
  version: '0'
75
76
  required_rubygems_version: !ruby/object:Gem::Requirement
76
77
  requirements:
77
- - - '>='
78
+ - - ">="
78
79
  - !ruby/object:Gem::Version
79
80
  version: '0'
80
81
  requirements: []
81
82
  rubyforge_project:
82
- rubygems_version: 2.1.11
83
+ rubygems_version: 2.4.4
83
84
  signing_key:
84
85
  specification_version: 4
85
86
  summary: symlink functions for Windows