@@ -361,16 +361,19 @@ class fs_req_wrap {
361
361
#define ASYNC_CALL (func, req, encoding, ...) \
362
362
ASYNC_DEST_CALL (func, req, nullptr , encoding, __VA_ARGS__) \
363
363
364
- #define SYNC_DEST_CALL (func, path, dest, ...) \
365
- fs_req_wrap req_wrap; \
364
+ #define SYNC_CALL_NO_THROW (req_wrap, func, dest, ...) \
366
365
env->PrintSyncTrace (); \
367
366
int err = uv_fs_ ## func(env->event_loop (), \
368
- &req_wrap.req, \
367
+ &( req_wrap) .req, \
369
368
__VA_ARGS__, \
370
- nullptr); \
371
- if (err < 0 ) { \
372
- return env->ThrowUVException (err, #func, nullptr , path, dest); \
373
- } \
369
+ nullptr);
370
+
371
+ #define SYNC_DEST_CALL (func, path, dest, ...) \
372
+ fs_req_wrap req_wrap; \
373
+ SYNC_CALL_NO_THROW (req_wrap, func, dest, __VA_ARGS__) \
374
+ if (SYNC_RESULT < 0 ) { \
375
+ return env->ThrowUVException (SYNC_RESULT, #func, nullptr , path, dest); \
376
+ }
374
377
375
378
#define SYNC_CALL (func, path, ...) \
376
379
SYNC_DEST_CALL (func, path, nullptr , __VA_ARGS__) \
@@ -882,6 +885,77 @@ static void MKDir(const FunctionCallbackInfo<Value>& args) {
882
885
}
883
886
}
884
887
888
+
889
+ static size_t CountSlashes (const char * str, size_t len) {
890
+ size_t cntr = 0 ;
891
+ for (size_t i = 0 ; i < len; i++) {
892
+ if (str[i] == ' /' ) cntr++;
893
+ }
894
+ return cntr;
895
+ }
896
+
897
+
898
+ static int ResolveRealPathSync (Environment* env,
899
+ std::string* ret_str,
900
+ const char * path,
901
+ size_t call_depth) {
902
+ fs_req_wrap req_wrap;
903
+ SYNC_CALL_NO_THROW (req_wrap, realpath , path, path);
904
+
905
+ call_depth++;
906
+ if (SYNC_RESULT != UV_ELOOP) {
907
+ if (SYNC_RESULT) return SYNC_RESULT;
908
+ *ret_str = std::string (static_cast <const char *>(SYNC_REQ.ptr ));
909
+ return SYNC_RESULT;
910
+ // TODO(trevnorris): Instead of simply not allowing too many recursive
911
+ // calls, would it instead be a viable solution to attempt detection of
912
+ // recursive symlinks? Thus preventing false negatives.
913
+ } else if (SYNC_RESULT == UV_ELOOP && call_depth > 100 ) {
914
+ return UV_ELOOP;
915
+ }
916
+
917
+ #ifdef _WIN32
918
+ const char separator = ' \\ ' ;
919
+ #else
920
+ const char separator = ' /' ;
921
+ #endif
922
+ std::string str_path (path);
923
+ size_t offset = 0 ;
924
+ size_t current = 0 ;
925
+ size_t slash_count = 0 ;
926
+
927
+ // Can assume '/' because uv_fs_realpath() cannot return UV_ELOOP on win.
928
+ while ((offset = str_path.find (separator, offset + 1 )) != std::string::npos) {
929
+ // OSX libc bails with ELOOP when encountering more than MAXSYMLINKS,
930
+ // which is hard coded to in the kernel header to 32.
931
+ if (++slash_count < 32 ) {
932
+ continue ;
933
+ }
934
+
935
+ std::string partial = *ret_str + str_path.substr (current, offset - current);
936
+ int err2 = ResolveRealPathSync (env, ret_str, partial.c_str (), call_depth);
937
+ // No need to handle an error that was returned by a recursive call.
938
+ if (err2) {
939
+ *ret_str = std::string ();
940
+ return err2;
941
+ }
942
+
943
+ current = offset;
944
+ slash_count = CountSlashes (ret_str->c_str (), ret_str->length ());
945
+ }
946
+
947
+ if (offset == std::string::npos) {
948
+ offset = str_path.length ();
949
+ }
950
+ if (current >= offset) {
951
+ return 0 ;
952
+ }
953
+
954
+ std::string pass (*ret_str + str_path.substr (current, offset - current));
955
+ return ResolveRealPathSync (env, ret_str, pass.c_str (), call_depth);
956
+ }
957
+
958
+
885
959
static void RealPath (const FunctionCallbackInfo<Value>& args) {
886
960
Environment* env = Environment::GetCurrent (args);
887
961
@@ -902,10 +976,14 @@ static void RealPath(const FunctionCallbackInfo<Value>& args) {
902
976
if (callback->IsObject ()) {
903
977
ASYNC_CALL (realpath , callback, encoding, *path);
904
978
} else {
905
- SYNC_CALL (realpath , *path, *path);
906
- const char * link_path = static_cast <const char *>(SYNC_REQ.ptr );
979
+ std::string rc_string;
980
+ // Resolve the symlink attempting simple amount of deep path resolution.
981
+ int err = ResolveRealPathSync (env, &rc_string, *path, 0 );
982
+ if (err) {
983
+ return env->ThrowUVException (err, " realpath" , nullptr , *path, *path);
984
+ }
907
985
Local<Value> rc = StringBytes::Encode (env->isolate (),
908
- link_path ,
986
+ rc_string. c_str () ,
909
987
encoding);
910
988
if (rc.IsEmpty ()) {
911
989
return env->ThrowUVException (UV_EINVAL,
0 commit comments